diff --git a/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs b/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs index 47290b3984..3249adcc54 100644 --- a/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs +++ b/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs @@ -24,7 +24,7 @@ public class AccountStateDeltaExtensionsTest public AccountStateDeltaExtensionsTest() { _agentAddress = default; - _avatarAddress = _agentAddress.Derive(string.Format(CultureInfo.InvariantCulture, CreateAvatar2.DeriveFormat, 0)); + _avatarAddress = _agentAddress.Derive(string.Format(CultureInfo.InvariantCulture, CreateAvatar.DeriveFormat, 0)); _agentState = new AgentState(_agentAddress); _agentState.avatarAddresses[0] = _avatarAddress; _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); diff --git a/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs b/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs index ba1bc35050..78635cf6d0 100644 --- a/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs +++ b/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs @@ -31,7 +31,7 @@ public class AccountStateViewExtensionsTest public AccountStateViewExtensionsTest() { _agentAddress = default; - _avatarAddress = _agentAddress.Derive(string.Format(CultureInfo.InvariantCulture, CreateAvatar2.DeriveFormat, 0)); + _avatarAddress = _agentAddress.Derive(string.Format(CultureInfo.InvariantCulture, CreateAvatar.DeriveFormat, 0)); _agentState = new AgentState(_agentAddress); _agentState.avatarAddresses[0] = _avatarAddress; _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); diff --git a/.Lib9c.Tests/Action/ActionEvaluationTest.cs b/.Lib9c.Tests/Action/ActionEvaluationTest.cs index 9a811172d9..744891a899 100644 --- a/.Lib9c.Tests/Action/ActionEvaluationTest.cs +++ b/.Lib9c.Tests/Action/ActionEvaluationTest.cs @@ -63,14 +63,11 @@ public ActionEvaluationTest() [InlineData(typeof(MigrationActivatedAccountsState))] [InlineData(typeof(MigrationAvatarState))] [InlineData(typeof(MigrationLegacyShop))] - [InlineData(typeof(MimisbrunnrBattle))] - [InlineData(typeof(MonsterCollect))] [InlineData(typeof(PatchTableSheet))] [InlineData(typeof(RankingBattle))] [InlineData(typeof(RapidCombination))] [InlineData(typeof(RedeemCode))] [InlineData(typeof(RewardGold))] - [InlineData(typeof(Sell))] [InlineData(typeof(SellCancellation))] [InlineData(typeof(UpdateSell))] [InlineData(typeof(CreatePendingActivations))] @@ -199,18 +196,6 @@ private ActionBase GetAction(Type type) avatarStates = new List(), }, MigrationLegacyShop _ => new MigrationLegacyShop(), - MimisbrunnrBattle _ => new MimisbrunnrBattle - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 0, - StageId = 0, - PlayCount = 0, - AvatarAddress = default, - }, - MonsterCollect _ => new MonsterCollect(), PatchTableSheet _ => new PatchTableSheet { TableCsv = "table", @@ -231,10 +216,6 @@ private ActionBase GetAction(Type type) AvatarAddress = new PrivateKey().ToAddress(), }, RewardGold _ => null, - Sell _ => new Sell - { - price = _currency * 100, - }, SellCancellation _ => new SellCancellation(), UpdateSell _ => new UpdateSell { diff --git a/.Lib9c.Tests/Action/BattleArena10Test.cs b/.Lib9c.Tests/Action/BattleArena10Test.cs deleted file mode 100644 index 59b9f88641..0000000000 --- a/.Lib9c.Tests/Action/BattleArena10Test.cs +++ /dev/null @@ -1,1340 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena10Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 1, false, 1, 5, 3)] - [InlineData(1, 1, false, 1, 5, 4)] - [InlineData(1, 1, false, 5, 5, 3)] - [InlineData(1, 1, true, 1, 5, 3)] - [InlineData(1, 1, false, 3, 5, 4)] - [InlineData(1, 2, false, 1, 5, 3)] - [InlineData(1, 2, true, 1, 5, 3)] - [InlineData(1, 3, false, 1, int.MaxValue, 3)] - [InlineData(1, 3, true, 1, int.MaxValue, 3)] - public void Execute_Success( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 2, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena10 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena10 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCount; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitDuringIntervalException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena10.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - long nextBlockIndex = 4; - int championshipId = 1; - int round = 1; - int ticket = 1; - int arenaInterval = 5; - int randomSeed = 3; - - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var ncgCurrency = previousStates.GetGoldCurrency(); - previousStates = previousStates.MintAsset(context, _agent1Address, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatar1Address, - SlotIndex = 1, - }; - - previousStates = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - _avatar1Address, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - _avatar2Address, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - _agent1Address, - _avatar1Address, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_ValidateDuplicateTicketPurchaseException() - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - if (roundData.ArenaType != ArenaType.OffSeason) - { - throw new InvalidSeasonException($"[{nameof(BattleArena)}] This test is only for OffSeason. ArenaType : {roundData.ArenaType}"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena10.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena10 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 8, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - } - - var action = new BattleArena10 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex < arenaInterval - ? roundData.StartBlockIndex - : roundData.StartBlockIndex + arenaInterval; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("battle_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena11Test.cs b/.Lib9c.Tests/Action/BattleArena11Test.cs deleted file mode 100644 index c494ff6a94..0000000000 --- a/.Lib9c.Tests/Action/BattleArena11Test.cs +++ /dev/null @@ -1,1340 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena11Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 1, false, 1, 5, 3)] - [InlineData(1, 1, false, 1, 5, 4)] - [InlineData(1, 1, false, 5, 5, 3)] - [InlineData(1, 1, true, 1, 5, 3)] - [InlineData(1, 1, false, 3, 5, 4)] - [InlineData(1, 2, false, 1, 5, 3)] - [InlineData(1, 2, true, 1, 5, 3)] - [InlineData(1, 3, false, 1, int.MaxValue, 3)] - [InlineData(1, 3, true, 1, int.MaxValue, 3)] - public void Execute_Success( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 2, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena11 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena11 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCount; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitDuringIntervalException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena11.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - long nextBlockIndex = 4; - int championshipId = 1; - int round = 1; - int ticket = 1; - int arenaInterval = 5; - int randomSeed = 3; - - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var ncgCurrency = previousStates.GetGoldCurrency(); - previousStates = previousStates.MintAsset(context, _agent1Address, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatar1Address, - SlotIndex = 1, - }; - - previousStates = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - _avatar1Address, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - _avatar2Address, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - _agent1Address, - _avatar1Address, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_ValidateDuplicateTicketPurchaseException() - { - var context = new ActionContext(); - const int championshipId = 1; - const int round = 1; - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - if (roundData.ArenaType != ArenaType.OffSeason) - { - throw new InvalidSeasonException($"[{nameof(BattleArena11)}] This test is only for OffSeason. ArenaType : {roundData.ArenaType}"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena11.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena11 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 8, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena11)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - } - - var action = new BattleArena11 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex < arenaInterval - ? roundData.StartBlockIndex - : roundData.StartBlockIndex + arenaInterval; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("battle_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena1Test.cs b/.Lib9c.Tests/Action/BattleArena1Test.cs deleted file mode 100644 index 251719db1c..0000000000 --- a/.Lib9c.Tests/Action/BattleArena1Test.cs +++ /dev/null @@ -1,603 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena1Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly AvatarState _avatar1; - private readonly AvatarState _avatar4; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _state; - - public BattleArena1Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(_sheets); - foreach (var (key, value) in _sheets) - { - _state = _state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1 = avatar1State; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4 = avatar4State; - _avatar4Address = avatar4State.address; - - _state = _state - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address.Derive(LegacyInventoryKey), _avatar1.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), _avatar1.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), _avatar1.questList.Serialize()) - .SetState(_avatar1Address, _avatar1.Serialize()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState(_avatar4Address.Derive(LegacyInventoryKey), _avatar4.inventory.Serialize()) - .SetState(_avatar4Address.Derive(LegacyWorldInformationKey), _avatar4.worldInformation.Serialize()) - .SetState(_avatar4Address.Derive(LegacyQuestListKey), _avatar4.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.Serialize()) - .SetState(Addresses.GameConfig, new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var items = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in items) - { - avatarState.inventory.AddItem(equipment); - } - - var equipments = items.Select(e => e.NonFungibleId).ToList(); - - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - public IAccountStateDelta JoinArena(IActionContext context, Address signer, Address avatarAddress, long blockIndex, int championshipId, int round, IRandom random) - { - var preCurrency = 1000 * _crystal; - _state = _state.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1() - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return _state; - } - - [Theory] - [InlineData(1, 1, 1, 1, 2, 3)] - [InlineData(1, 1, 1, 1, 2, 4)] - [InlineData(1, 1, 1, 5, 2, 0)] - [InlineData(1, 1, 1, 5, 2, 1)] - [InlineData(1, 1, 2, 5, 2, 0)] - [InlineData(1, 1, 2, 5, 2, 1)] - public void Execute(long nextBlockIndex, int championshipId, int round, int ticket, int arenaInterval, int randomSeed) - { - var context = new ActionContext(); - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena1)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var usedTicket = 2; - beforeInfo.UseTicket(usedTicket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var buyTicket = ticket - beforeInfo.Ticket; - if (buyTicket > 0) - { - var currency = buyTicket * _ncg * roundData.TicketPrice; - _state = _state.MintAsset(context, _agent1Address, currency); - } - - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress(_avatar1Address, championshipId, round); - var enemyScoreAdr = ArenaScore.DeriveAddress(_avatar2Address, championshipId, round); - if (!_state.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.Empty(_avatar1.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - _state = _state.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!_state.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = (afterInfo.Win * myWinScore) + (afterInfo.Lose * myDefeatScore); - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max(beforeMyScore.Score + addMyScore, ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max(beforeEnemyScore.Score + addEnemyScore, ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal(ArenaInformation.MaxTicketCount - usedTicket, beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - var balance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - Assert.Equal(0, balance.RawValue); - - var avatarState = _state.GetAvatarStateV2(_avatar1Address); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - Assert.Equal(afterInfo.Win, medal.count); - medalCount = medal.count; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - public GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _state.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena1() - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena1() - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 1; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena1)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = excludeMe - ? JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random) - : JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena1)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaScoreAdr = ArenaScore.DeriveAddress(isSigner ? _avatar1Address : _avatar2Address, roundData.ChampionshipId, roundData.Round); - _state.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - _state = _state.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena1() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena2Test.cs b/.Lib9c.Tests/Action/BattleArena2Test.cs deleted file mode 100644 index 8f4901d812..0000000000 --- a/.Lib9c.Tests/Action/BattleArena2Test.cs +++ /dev/null @@ -1,796 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena2Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly AvatarState _avatar1; - private readonly AvatarState _avatar2; - private readonly AvatarState _avatar3; - private readonly AvatarState _avatar4; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _state; - - public BattleArena2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(_sheets); - foreach (var (key, value) in _sheets) - { - _state = _state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1 = avatar1State; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2 = avatar2State; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3 = avatar3State; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4 = avatar4State; - _avatar4Address = avatar4State.address; - - _state = _state - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address.Derive(LegacyInventoryKey), _avatar1.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), _avatar1.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), _avatar1.questList.Serialize()) - .SetState(_avatar1Address, _avatar1.Serialize()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState(_avatar4Address.Derive(LegacyInventoryKey), _avatar4.inventory.Serialize()) - .SetState(_avatar4Address.Derive(LegacyWorldInformationKey), _avatar4.worldInformation.Serialize()) - .SetState(_avatar4Address.Derive(LegacyQuestListKey), _avatar4.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.Serialize()) - .SetState(Addresses.GameConfig, new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var items = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in items) - { - avatarState.inventory.AddItem(equipment); - } - - var equipments = items.Select(e => e.NonFungibleId).ToList(); - - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - public IAccountStateDelta JoinArena(IActionContext context, Address signer, Address avatarAddress, long blockIndex, int championshipId, int round, IRandom random) - { - var preCurrency = 1000 * _crystal; - _state = _state.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1() - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return _state; - } - - [Theory] - [InlineData(1, 1, 1, false, 1, 2, 3)] - [InlineData(1, 1, 1, false, 1, 2, 4)] - [InlineData(1, 1, 1, false, 5, 2, 3)] - [InlineData(1, 1, 1, true, 1, 2, 3)] - [InlineData(1, 1, 1, true, 3, 2, 3)] - [InlineData(1, 1, 2, false, 1, 2, 3)] - [InlineData(1, 1, 2, true, 1, 2, 3)] - public void Execute( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - var context = new ActionContext(); - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - var beforeBalance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress(_avatar1Address, championshipId, round); - var enemyScoreAdr = ArenaScore.DeriveAddress(_avatar2Address, championshipId, round); - if (!_state.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.Empty(_avatar1.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - _state = _state.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!_state.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = (afterInfo.Win * myWinScore) + (afterInfo.Lose * myDefeatScore); - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max(beforeMyScore.Score + addMyScore, ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max(beforeEnemyScore.Score + addEnemyScore, ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal(isPurchased ? 0 : ArenaInformation.MaxTicketCount, beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = _state.GetAvatarStateV2(_avatar1Address); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - public GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _state.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena2() - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena2() - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 1; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = excludeMe - ? JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random) - : JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaScoreAdr = ArenaScore.DeriveAddress(isSigner ? _avatar1Address : _avatar2Address, roundData.ChampionshipId, roundData.Round); - _state.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - _state = _state.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = ArenaHelper.GetMaxPurchasedTicketCount(roundData); - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - - var action = new BattleArena2() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena3Test.cs b/.Lib9c.Tests/Action/BattleArena3Test.cs deleted file mode 100644 index c0cef8bf64..0000000000 --- a/.Lib9c.Tests/Action/BattleArena3Test.cs +++ /dev/null @@ -1,796 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena3Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly AvatarState _avatar1; - private readonly AvatarState _avatar2; - private readonly AvatarState _avatar3; - private readonly AvatarState _avatar4; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _state; - - public BattleArena3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(_sheets); - foreach (var (key, value) in _sheets) - { - _state = _state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1 = avatar1State; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2 = avatar2State; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3 = avatar3State; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4 = avatar4State; - _avatar4Address = avatar4State.address; - - _state = _state - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address.Derive(LegacyInventoryKey), _avatar1.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), _avatar1.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), _avatar1.questList.Serialize()) - .SetState(_avatar1Address, _avatar1.Serialize()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState(_avatar4Address.Derive(LegacyInventoryKey), _avatar4.inventory.Serialize()) - .SetState(_avatar4Address.Derive(LegacyWorldInformationKey), _avatar4.worldInformation.Serialize()) - .SetState(_avatar4Address.Derive(LegacyQuestListKey), _avatar4.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.Serialize()) - .SetState(Addresses.GameConfig, new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var items = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in items) - { - avatarState.inventory.AddItem(equipment); - } - - var equipments = items.Select(e => e.NonFungibleId).ToList(); - - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - public IAccountStateDelta JoinArena(IActionContext context, Address signer, Address avatarAddress, long blockIndex, int championshipId, int round, IRandom random) - { - var preCurrency = 1000 * _crystal; - _state = _state.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1() - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return _state; - } - - [Theory] - [InlineData(1, 1, 1, false, 1, 2, 3)] - [InlineData(1, 1, 1, false, 1, 2, 4)] - [InlineData(1, 1, 1, false, 5, 2, 3)] - [InlineData(1, 1, 1, true, 1, 2, 3)] - [InlineData(1, 1, 1, true, 3, 2, 3)] - [InlineData(1, 1, 2, false, 1, 2, 3)] - [InlineData(1, 1, 2, true, 1, 2, 3)] - public void Execute( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - var context = new ActionContext(); - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - var beforeBalance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress(_avatar1Address, championshipId, round); - var enemyScoreAdr = ArenaScore.DeriveAddress(_avatar2Address, championshipId, round); - if (!_state.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.Empty(_avatar1.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - _state = _state.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!_state.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = (afterInfo.Win * myWinScore) + (afterInfo.Lose * myDefeatScore); - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max(beforeMyScore.Score + addMyScore, ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max(beforeEnemyScore.Score + addEnemyScore, ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal(isPurchased ? 0 : ArenaInformation.MaxTicketCount, beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = _state.GetAvatarStateV2(_avatar1Address); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - public GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _state.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena3() - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena3() - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 1; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = excludeMe - ? JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random) - : JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaScoreAdr = ArenaScore.DeriveAddress(isSigner ? _avatar1Address : _avatar2Address, roundData.ChampionshipId, roundData.Round); - _state.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - _state = _state.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = ArenaHelper.GetMaxPurchasedTicketCount(roundData); - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - - var action = new BattleArena3() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena4Test.cs b/.Lib9c.Tests/Action/BattleArena4Test.cs deleted file mode 100644 index 547107f6be..0000000000 --- a/.Lib9c.Tests/Action/BattleArena4Test.cs +++ /dev/null @@ -1,988 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena4Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly AvatarState _avatar1; - private readonly AvatarState _avatar2; - private readonly AvatarState _avatar3; - private readonly AvatarState _avatar4; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _state; - - public BattleArena4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(_sheets); - foreach (var (key, value) in _sheets) - { - _state = _state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1 = avatar1State; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2 = avatar2State; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3 = avatar3State; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4 = avatar4State; - _avatar4Address = avatar4State.address; - - _state = _state - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address.Derive(LegacyInventoryKey), _avatar1.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), _avatar1.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), _avatar1.questList.Serialize()) - .SetState(_avatar1Address, _avatar1.Serialize()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState(_avatar4Address.Derive(LegacyInventoryKey), _avatar4.inventory.Serialize()) - .SetState(_avatar4Address.Derive(LegacyWorldInformationKey), _avatar4.worldInformation.Serialize()) - .SetState(_avatar4Address.Derive(LegacyQuestListKey), _avatar4.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.Serialize()) - .SetState(Addresses.GameConfig, new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var items = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in items) - { - avatarState.inventory.AddItem(equipment); - } - - var equipments = items.Select(e => e.NonFungibleId).ToList(); - - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - public IAccountStateDelta JoinArena(IActionContext context, Address signer, Address avatarAddress, long blockIndex, int championshipId, int round, IRandom random) - { - var preCurrency = 1000 * _crystal; - _state = _state.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1() - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return _state; - } - - [Theory] - [InlineData(1, 2, 3, false, 1, 2, 3)] - [InlineData(1, 2, 3, false, 1, 2, 4)] - [InlineData(1, 2, 3, false, 5, 2, 3)] - [InlineData(1, 2, 3, true, 1, 2, 3)] - [InlineData(1, 2, 3, true, 3, 2, 3)] - [InlineData(1, 2, 4, false, 1, 2, 3)] - [InlineData(1, 2, 4, true, 1, 2, 3)] - public void Execute( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - var context = new ActionContext(); - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - var beforeBalance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress(_avatar1Address, championshipId, round); - var enemyScoreAdr = ArenaScore.DeriveAddress(_avatar2Address, championshipId, round); - if (!_state.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.Empty(_avatar1.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - _state = _state.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - - Assert.True(blockIndex > ActionObsoleteConfig.V100301ExecutedBlockIndex); - - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!_state.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = (afterInfo.Win * myWinScore) + (afterInfo.Lose * myDefeatScore); - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max(beforeMyScore.Score + addMyScore, ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max(beforeEnemyScore.Score + addEnemyScore, ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal(isPurchased ? 0 : ArenaInformation.MaxTicketCount, beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = _state.GetAvatarStateV2(_avatar1Address); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - public GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _state.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena4() - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena4() - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 1; - - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = excludeMe - ? JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random) - : JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaScoreAdr = ArenaScore.DeriveAddress(isSigner ? _avatar1Address : _avatar2Address, roundData.ChampionshipId, roundData.Round); - _state.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - _state = _state.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = ArenaHelper.GetMaxPurchasedTicketCount(roundData); - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - var context = new ActionContext(); - var championshipId = 1; - var round = 2; - - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = ArenaHelper.GetMaxPurchasedTicketCount(roundData); - _state = _state.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice(roundData, beforeInfo, _state.GetGoldCurrency()); - _state = _state.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var newState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _state, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex + 1, - PreviousState = newState, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_v100291() - { - var context = new ActionContext(); - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (keys.Contains(key)) - { - _state = _state.SetState(Addresses.TableSheet.Derive(key), null!); - } - } - - int championshipId = 1; - int round = 1; - Assert.True(_state.GetSheet().TryGetValue( - championshipId, - out var row)); - - Assert.True(row.TryGetRound(1, out var roundData)); - - var random = new TestRandom(1); - _state = JoinArena(context, _agent1Address, _avatar1Address, roundData.StartBlockIndex, championshipId, round, random); - _state = JoinArena(context, _agent2Address, _avatar2Address, roundData.StartBlockIndex, championshipId, round, random); - - var arenaInfoAdr = ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - foreach (var key in keys) - { - Assert.Null(_state.GetState(Addresses.GetSheetAddress(key))); - } - - Assert.NotNull(_state.GetState(Addresses.GetSheetAddress())); - - var action = new BattleArena4() - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress(_avatar1Address, championshipId, round); - var enemyScoreAdr = ArenaScore.DeriveAddress(_avatar2Address, championshipId, round); - if (!_state.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.Empty(_avatar1.inventory.Materials); - - var gameConfigState = SetArenaInterval(2); - _state = _state.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + 1; - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!_state.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!_state.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!_state.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = (afterInfo.Win * myWinScore) + (afterInfo.Lose * myDefeatScore); - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max(beforeMyScore.Score + addMyScore, ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max(beforeEnemyScore.Score + addEnemyScore, ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var balance = _state.GetBalance(_agent1Address, _state.GetGoldCurrency()); - Assert.Equal(0, balance.RawValue); - - var avatarState = _state.GetAvatarStateV2(_avatar1Address); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = ArenaHelper.GetRewardCount(beforeMyScore.Score) * 1 + medalCount; - Assert.InRange(materialCount, 0, high); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena5Test.cs b/.Lib9c.Tests/Action/BattleArena5Test.cs deleted file mode 100644 index e7b64be69a..0000000000 --- a/.Lib9c.Tests/Action/BattleArena5Test.cs +++ /dev/null @@ -1,1031 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena5Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 1, 1, false, 1, 2, 3)] - [InlineData(1, 1, 1, false, 1, 2, 4)] - [InlineData(1, 1, 1, false, 5, 2, 3)] - [InlineData(1, 1, 1, true, 1, 2, 3)] - [InlineData(1, 1, 1, true, 3, 2, 3)] - [InlineData(1, 1, 2, false, 1, 2, 3)] - [InlineData(1, 1, 2, true, 1, 2, 3)] - public void Execute_Success( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - nextBlockIndex, - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 1, - 1, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena5 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena5 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = ArenaHelper.GetMaxPurchasedTicketCount(roundData); - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = ArenaHelper.GetMaxPurchasedTicketCount(roundData); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - - var action = new BattleArena5 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - var action = new BattleArena5 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena6Test.cs b/.Lib9c.Tests/Action/BattleArena6Test.cs deleted file mode 100644 index 144bcd5349..0000000000 --- a/.Lib9c.Tests/Action/BattleArena6Test.cs +++ /dev/null @@ -1,1117 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena6Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - _sheets.Remove(nameof(RuneOptionSheet)); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(4, 1, 1, false, 1, 5, 3)] - [InlineData(4, 1, 1, false, 1, 5, 4)] - [InlineData(4, 1, 1, false, 5, 5, 3)] - [InlineData(4, 1, 1, true, 1, 5, 3)] - [InlineData(4, 1, 1, true, 3, 5, 3)] - [InlineData(1, 1, 2, false, 1, 5, 3)] - [InlineData(1, 1, 2, true, 1, 5, 3)] - public void Execute_Success( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - nextBlockIndex, - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 1, - 2, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena6 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena6 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 3, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCount; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitDuringIntervalException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena6.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var action = new BattleArena6 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - } - - var action = new BattleArena6 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena7Test.cs b/.Lib9c.Tests/Action/BattleArena7Test.cs deleted file mode 100644 index 6cbcf3b742..0000000000 --- a/.Lib9c.Tests/Action/BattleArena7Test.cs +++ /dev/null @@ -1,1130 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena7Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena7Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(4, 1, 1, false, 1, 5, 3)] - [InlineData(4, 1, 1, false, 1, 5, 4)] - [InlineData(4, 1, 1, false, 5, 5, 3)] - [InlineData(4, 1, 1, true, 1, 5, 3)] - [InlineData(4, 1, 1, true, 3, 5, 3)] - [InlineData(1, 1, 2, false, 1, 5, 3)] - [InlineData(1, 1, 2, true, 1, 5, 3)] - public void Execute_Success( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - nextBlockIndex, - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 1, - 2, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena7 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena7 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCount; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitDuringIntervalException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena7.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var action = new BattleArena7 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - long nextBlockIndex, - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - } - - var action = new BattleArena7 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("daily_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena8Test.cs b/.Lib9c.Tests/Action/BattleArena8Test.cs deleted file mode 100644 index ef0afc550a..0000000000 --- a/.Lib9c.Tests/Action/BattleArena8Test.cs +++ /dev/null @@ -1,1340 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena8Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 1, false, 1, 5, 3)] - [InlineData(1, 1, false, 1, 5, 4)] - [InlineData(1, 1, false, 5, 5, 3)] - [InlineData(1, 1, true, 1, 5, 3)] - [InlineData(1, 1, false, 3, 5, 4)] - [InlineData(1, 2, false, 1, 5, 3)] - [InlineData(1, 2, true, 1, 5, 3)] - [InlineData(1, 3, false, 1, int.MaxValue, 3)] - [InlineData(1, 3, true, 1, int.MaxValue, 3)] - public void Execute_Success( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 2, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena8 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena8 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCount; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitDuringIntervalException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena8.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - long nextBlockIndex = 4; - int championshipId = 1; - int round = 1; - int ticket = 1; - int arenaInterval = 5; - int randomSeed = 3; - - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var ncgCurrency = previousStates.GetGoldCurrency(); - previousStates = previousStates.MintAsset(context, _agent1Address, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatar1Address, - SlotIndex = 1, - }; - - previousStates = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - _avatar1Address, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - _avatar2Address, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - _agent1Address, - _avatar1Address, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_ValidateDuplicateTicketPurchaseException() - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - if (roundData.ArenaType != ArenaType.OffSeason) - { - throw new InvalidSeasonException($"[{nameof(BattleArena8)}] This test is only for OffSeason. ArenaType : {roundData.ArenaType}"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena8.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena8 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 8, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - } - - var action = new BattleArena8 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex < arenaInterval - ? roundData.StartBlockIndex - : roundData.StartBlockIndex + arenaInterval; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("battle_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena9Test.cs b/.Lib9c.Tests/Action/BattleArena9Test.cs deleted file mode 100644 index b5e6e7bf58..0000000000 --- a/.Lib9c.Tests/Action/BattleArena9Test.cs +++ /dev/null @@ -1,1340 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.EnumType; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleArena9Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _agent2Address; - private readonly Address _agent3Address; - private readonly Address _agent4Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _avatar3Address; - private readonly Address _avatar4Address; - private readonly Currency _crystal; - private readonly Currency _ncg; - private IAccountStateDelta _initialStates; - - public BattleArena9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new MockStateDelta(); - - _sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in _sheets) - { - _initialStates = _initialStates.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(_sheets); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _crystal = Currency.Legacy("CRYSTAL", 18, null); - _ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_ncg); - - var rankingMapAddress = new PrivateKey().ToAddress(); - var clearStageId = Math.Max( - _tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard); - - // account 1 - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agent1Address, agent1State.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - avatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - avatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - avatar1State.questList.Serialize()) - .SetState(_avatar1Address, avatar1State.SerializeV2()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(_agent4Address, agent4State.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyInventoryKey), - avatar4State.inventory.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyWorldInformationKey), - avatar4State.worldInformation.Serialize()) - .SetState( - _avatar4Address.Derive(LegacyQuestListKey), - avatar4State.questList.Serialize()) - .SetState(_avatar4Address, avatar4State.SerializeV2()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 1, false, 1, 5, 3)] - [InlineData(1, 1, false, 1, 5, 4)] - [InlineData(1, 1, false, 5, 5, 3)] - [InlineData(1, 1, true, 1, 5, 3)] - [InlineData(1, 1, false, 3, 5, 4)] - [InlineData(1, 2, false, 1, 5, 3)] - [InlineData(1, 2, true, 1, 5, 3)] - [InlineData(1, 3, false, 1, int.MaxValue, 3)] - [InlineData(1, 3, true, 1, int.MaxValue, 3)] - public void Execute_Success( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed) - { - Execute( - championshipId, - round, - isPurchased, - ticket, - arenaInterval, - randomSeed, - _agent1Address, - _avatar1Address, - _agent2Address, - _avatar2Address); - } - - [Fact] - public void Execute_Backward_Compatibility_Success() - { - Execute( - 1, - 2, - default, - 1, - 2, - default, - _agent2Address, - _avatar2Address, - _agent1Address, - _avatar1Address); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena9 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena9 - { - myAvatarAddress = _avatar4Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent4Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 9999999, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 1, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = excludeMe - ? JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random) - : JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_ValidateScoreDifferenceException(bool isSigner) - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaScoreAdr = ArenaScore.DeriveAddress( - isSigner - ? _avatar1Address - : _avatar2Address, roundData.ChampionshipId, - roundData.Round); - previousStates.TryGetArenaScore(arenaScoreAdr, out var arenaScore); - arenaScore.AddScore(900); - previousStates = previousStates.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InsufficientBalanceException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedPlayCountException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 2, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCount; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_ExceedTicketPurchaseLimitDuringIntervalException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - for (var i = 0; i < max; i++) - { - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena9.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_CoolDownBlockException() - { - const int championshipId = 1; - const int round = 2; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - var max = roundData.MaxPurchaseCountWithInterval; - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < max; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - - var nextStates = action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - long nextBlockIndex = 4; - int championshipId = 1; - int round = 1; - int ticket = 1; - int arenaInterval = 5; - int randomSeed = 3; - - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var ncgCurrency = previousStates.GetGoldCurrency(); - previousStates = previousStates.MintAsset(context, _agent1Address, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatar1Address, - SlotIndex = 1, - }; - - previousStates = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - }); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - _avatar1Address, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - _avatar2Address, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - _agent1Address, - _avatar1Address, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex + nextBlockIndex; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agent1Address, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_ValidateDuplicateTicketPurchaseException() - { - const int championshipId = 1; - const int round = 1; - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - if (roundData.ArenaType != ArenaType.OffSeason) - { - throw new InvalidSeasonException($"[{nameof(BattleArena9)}] This test is only for OffSeason. ArenaType : {roundData.ArenaType}"); - } - - var random = new TestRandom(); - previousStates = JoinArena( - context, - previousStates, - _agent1Address, - _avatar1Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - _agent2Address, - _avatar2Address, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(_avatar1Address, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - beforeInfo.UseTicket(ArenaInformation.MaxTicketCount); - - var purchasedCountDuringInterval = arenaInfoAdr.Derive(BattleArena9.PurchasedCountKey); - previousStates = previousStates - .SetState(arenaInfoAdr, beforeInfo.Serialize()) - .SetState( - purchasedCountDuringInterval, - new Integer(beforeInfo.PurchasedTicketCount)); - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, _agent1Address, price); - - var action = new BattleArena9 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = championshipId, - round = round, - ticket = 8, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var blockIndex = roundData.StartBlockIndex + 1; - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agent1Address, - Random = new TestRandom(), - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - clearStageId), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - private void Execute( - int championshipId, - int round, - bool isPurchased, - int ticket, - int arenaInterval, - int randomSeed, - Address myAgentAddress, - Address myAvatarAddress, - Address enemyAgentAddress, - Address enemyAvatarAddress) - { - var context = new ActionContext(); - var previousStates = _initialStates; - Assert.True(_initialStates.GetSheet().TryGetValue( - championshipId, - out var row)); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - var random = new TestRandom(randomSeed); - previousStates = JoinArena( - context, - previousStates, - myAgentAddress, - myAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - previousStates = JoinArena( - context, - previousStates, - enemyAgentAddress, - enemyAvatarAddress, - roundData.StartBlockIndex, - championshipId, - round, - random); - - var arenaInfoAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, championshipId, round); - if (!previousStates.TryGetArenaInformation(arenaInfoAdr, out var beforeInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - if (isPurchased) - { - beforeInfo.UseTicket(beforeInfo.Ticket); - previousStates = previousStates.SetState(arenaInfoAdr, beforeInfo.Serialize()); - for (var i = 0; i < ticket; i++) - { - var price = ArenaHelper.GetTicketPrice( - roundData, - beforeInfo, - previousStates.GetGoldCurrency()); - previousStates = previousStates.MintAsset(context, myAgentAddress, price); - beforeInfo.BuyTicket(roundData.MaxPurchaseCount); - } - } - - var action = new BattleArena9 - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = championshipId, - round = round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - var myScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - championshipId, - round); - var enemyScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - championshipId, - round); - if (!previousStates.TryGetArenaScore(myScoreAdr, out var beforeMyScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!previousStates.TryGetArenaScore(enemyScoreAdr, out var beforeEnemyScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - Assert.True(previousStates.TryGetAvatarStateV2( - myAgentAddress, - myAvatarAddress, - out var previousMyAvatarState, - out _)); - Assert.Empty(previousMyAvatarState.inventory.Materials); - - var gameConfigState = SetArenaInterval(arenaInterval); - previousStates = previousStates.SetState(GameConfigState.Address, gameConfigState.Serialize()); - - var blockIndex = roundData.StartBlockIndex < arenaInterval - ? roundData.StartBlockIndex - : roundData.StartBlockIndex + arenaInterval; - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAgentAddress, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - - if (!nextStates.TryGetArenaScore(myScoreAdr, out var myAfterScore)) - { - throw new ArenaScoreNotFoundException($"myScoreAdr : {myScoreAdr}"); - } - - if (!nextStates.TryGetArenaScore(enemyScoreAdr, out var enemyAfterScore)) - { - throw new ArenaScoreNotFoundException($"enemyScoreAdr : {enemyScoreAdr}"); - } - - if (!nextStates.TryGetArenaInformation(arenaInfoAdr, out var afterInfo)) - { - throw new ArenaInformationNotFoundException($"arenaInfoAdr : {arenaInfoAdr}"); - } - - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(beforeMyScore.Score, beforeEnemyScore.Score); - - var addMyScore = afterInfo.Win * myWinScore + afterInfo.Lose * myDefeatScore; - var addEnemyScore = afterInfo.Win * enemyWinScore; - var expectedMyScore = Math.Max( - beforeMyScore.Score + addMyScore, - ArenaScore.ArenaScoreDefault); - var expectedEnemyScore = Math.Max( - beforeEnemyScore.Score + addEnemyScore, - ArenaScore.ArenaScoreDefault); - - Assert.Equal(expectedMyScore, myAfterScore.Score); - Assert.Equal(expectedEnemyScore, enemyAfterScore.Score); - Assert.Equal( - isPurchased - ? 0 - : ArenaInformation.MaxTicketCount, - beforeInfo.Ticket); - Assert.Equal(0, beforeInfo.Win); - Assert.Equal(0, beforeInfo.Lose); - - var useTicket = Math.Min(ticket, beforeInfo.Ticket); - Assert.Equal(beforeInfo.Ticket - useTicket, afterInfo.Ticket); - Assert.Equal(ticket, afterInfo.Win + afterInfo.Lose); - - var balance = nextStates.GetBalance( - myAgentAddress, - nextStates.GetGoldCurrency()); - if (isPurchased) - { - Assert.Equal(ticket, afterInfo.PurchasedTicketCount); - } - - Assert.Equal(0, balance.RawValue); - - var avatarState = nextStates.GetAvatarStateV2(myAvatarAddress); - var medalCount = 0; - if (roundData.ArenaType != ArenaType.OffSeason) - { - var medalId = ArenaHelper.GetMedalItemId(championshipId, round); - avatarState.inventory.TryGetItem(medalId, out var medal); - if (afterInfo.Win > 0) - { - Assert.Equal(afterInfo.Win, medal.count); - } - else - { - Assert.Null(medal); - } - - medalCount = medal?.count ?? 0; - } - - var materialCount = avatarState.inventory.Materials.Count(); - var high = (ArenaHelper.GetRewardCount(beforeMyScore.Score) * ticket) + medalCount; - Assert.InRange(materialCount, 0, high); - } - - private IAccountStateDelta JoinArena( - IActionContext context, - IAccountStateDelta states, - Address signer, - Address avatarAddress, - long blockIndex, - int championshipId, - int round, - IRandom random) - { - var preCurrency = 1000 * _crystal; - states = states.MintAsset(context, signer, preCurrency); - - var action = new JoinArena1 - { - championshipId = championshipId, - round = round, - costumes = new List(), - equipments = new List(), - avatarAddress = avatarAddress, - }; - - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return states; - } - - private GameConfigState SetArenaInterval(int interval) - { - var gameConfigState = _initialStates.GetGameConfigState(); - var sheet = _tableSheets.GameConfigSheet; - foreach (var value in sheet.Values) - { - if (value.Key.Equals("battle_arena_interval")) - { - IReadOnlyList field = new[] - { - value.Key, - interval.ToString(), - }; - value.Set(field); - } - } - - gameConfigState.Set(sheet); - return gameConfigState; - } - } -} diff --git a/.Lib9c.Tests/Action/Buy10Test.cs b/.Lib9c.Tests/Action/Buy10Test.cs deleted file mode 100644 index 11410af01d..0000000000 --- a/.Lib9c.Tests/Action/Buy10Test.cs +++ /dev/null @@ -1,1141 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Bencodex.Types; - using Lib9c.DevExtensions; - using Lib9c.DevExtensions.Model; - using Lib9c.Model.Order; - using Lib9c.Tests.TestHelper; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static SerializeKeys; - - public class Buy10Test - { - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly Guid _orderId; - private IAccountStateDelta _initialState; - - public Buy10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - _orderId = new Guid("6d460c1a-755d-48e4-ad67-65d5f519dbc8"); - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 2, - }, - }; - } - - public static IEnumerable GetReconfigureFungibleItemMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 50, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex + 1, - Price = 10, - ItemCount = 60, - }, - }; - } - - [Theory] - [MemberData(nameof(GetExecuteMemberData))] - public void Execute(params OrderData[] orderDataList) - { - AvatarState buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - List purchaseInfos = new List(); - ShopState legacyShopState = _initialState.GetShopState(); - foreach (var orderData in orderDataList) - { - (AvatarState sellerAvatarState, AgentState sellerAgentState) = CreateAvatarState( - orderData.SellerAgentAddress, - orderData.SellerAvatarAddress - ); - ITradableItem tradableItem; - Guid orderId = orderData.OrderId; - Guid itemId = orderData.TradableId; - ItemSubType itemSubType; - if (orderData.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - 0); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else if (orderData.ItemType == ItemType.Costume) - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - tradableItem = costume; - itemSubType = costume.ItemSubType; - } - else - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - tradableItem = material; - itemSubType = ItemSubType.Hourglass; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update(mail); - buyerAvatarState.Update(mail); - } - - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = _initialState.GetState(shardedShopAddress) is null - ? new ShardedShopStateV2(shardedShopAddress) - : new ShardedShopStateV2((Dictionary)_initialState.GetState(shardedShopAddress)); - var order = OrderFactory.Create( - sellerAgentState.address, - sellerAvatarState.address, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, orderData.Price, 0), - tradableItem.TradableId, - 0, - itemSubType, - orderData.ItemCount - ); - sellerAvatarState.inventory.AddItem((ItemBase)tradableItem, orderData.ItemCount); - - var sellItem = order.Sell(sellerAvatarState); - var orderDigest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - Assert.True(sellerAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - - var orderDigestListState = new OrderDigestListState(OrderDigestListState.DeriveAddress(orderData.SellerAvatarAddress)); - orderDigestListState.Add(orderDigest); - shopState.Add(orderDigest, 0); - - Assert.Equal(order.ExpiredBlockIndex, sellItem.RequiredBlockIndex); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - sellerAvatarState.mailBox.Add(expirationMail); - - var purchaseInfo = new PurchaseInfo( - orderId, - tradableItem.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - itemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(orderDigestListState.Address, orderDigestListState.Serialize()); - } - - var buyAction = new Buy10 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - FungibleAssetValue totalTax = 0 * _goldCurrencyState.Currency; - FungibleAssetValue totalPrice = 0 * _goldCurrencyState.Currency; - Currency goldCurrencyState = nextState.GetGoldCurrency(); - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.Empty(buyAction.errors); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - Order order = - OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - FungibleAssetValue tax = order.GetTax(); - FungibleAssetValue taxedPrice = order.Price - tax; - totalTax += tax; - totalPrice += order.Price; - - int itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(100, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? orderDataList.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - Assert.Single(nextSellerAvatarState.mailBox.OfType()); - var sellerMail = nextSellerAvatarState.mailBox.OfType().First(); - Assert.Equal(order.OrderId, sellerMail.OrderId); - - var buyerMail = nextBuyerAvatarState.mailBox - .OfType() - .Single(i => i.OrderId.Equals(order.OrderId)); - Assert.Equal(order.OrderId, buyerMail.OrderId); - - FungibleAssetValue sellerGold = - nextState.GetBalance(purchaseInfo.SellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice, sellerGold); - - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(_buyerAgentAddress, orderReceipt.BuyerAgentAddress); - Assert.Equal(_buyerAvatarAddress, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - var nextOrderDigestListState = new OrderDigestListState( - (Dictionary)nextState.GetState(OrderDigestListState.DeriveAddress(purchaseInfo.SellerAvatarAddress)) - ); - Assert.Empty(nextOrderDigestListState.OrderDigestList); - } - - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var goldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(totalTax, goldCurrencyGold); - var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - var prevBuyerGold = _initialState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - } - - [Theory] - [InlineData(false, false, typeof(FailedLoadStateException))] - [InlineData(true, false, typeof(NotEnoughClearedStageLevelException))] - public void Execute_Throw_Exception(bool equalAvatarAddress, bool clearStage, Type exc) - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - default, - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food, - _goldCurrencyState.Currency * 0 - ); - - if (!clearStage) - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - } - - var avatarAddress = equalAvatarAddress ? _buyerAvatarAddress : default; - var action = new Buy10 - { - buyerAvatarAddress = avatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - Assert.Throws(exc, () => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Theory] - [MemberData(nameof(ErrorCodeMemberData))] - public void Execute_ErrorCode(ErrorCodeMember errorCodeMember) - { - var context = new ActionContext(); - var agentAddress = errorCodeMember.BuyerExist ? _buyerAgentAddress : default; - var orderPrice = new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0); - var sellerAvatarAddress = errorCodeMember.EqualSellerAvatar ? _sellerAvatarAddress : default; - Address sellerAgentAddress = default; - if (errorCodeMember.EqualSigner) - { - sellerAgentAddress = _buyerAgentAddress; - } - else if (errorCodeMember.EqualSellerAgent) - { - sellerAgentAddress = _sellerAgentAddress; - } - - var item = ItemFactory.CreateItem( - _tableSheets.ConsumableItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Food), new TestRandom()); - var orderTradableId = ((ITradableItem)item).TradableId; - var tradableId = errorCodeMember.EqualTradableId ? orderTradableId : Guid.NewGuid(); - var price = errorCodeMember.EqualPrice ? orderPrice : default; - - var blockIndex = errorCodeMember.Expire ? Order.ExpirationInterval + 1 : 10; - - if (errorCodeMember.ShopStateExist) - { - var shopAddress = ShardedShopStateV2.DeriveAddress(ItemSubType.Food, _orderId); - var shopState = new ShardedShopStateV2(shopAddress); - if (errorCodeMember.OrderExist) - { - var sellerAvatarState = _initialState.GetAvatarState(_sellerAvatarAddress); - if (!errorCodeMember.NotContains) - { - var orderLock = new OrderLock(_orderId); - sellerAvatarState.inventory.AddItem(item, iLock: orderLock); - } - - var order = OrderFactory.Create( - sellerAgentAddress, - sellerAvatarAddress, - _orderId, - orderPrice, - orderTradableId, - 0, - ItemSubType.Food, - 1 - ); - if (errorCodeMember.Duplicate) - { - _initialState = _initialState.SetState( - OrderReceipt.DeriveAddress(_orderId), - new OrderReceipt(_orderId, _buyerAgentAddress, _buyerAvatarAddress, 0) - .Serialize() - ); - } - - if (errorCodeMember.DigestExist) - { - var orderDigest = new OrderDigest( - sellerAvatarAddress, - order.StartedBlockIndex, - order.ExpiredBlockIndex, - order.OrderId, - order.TradableId, - orderPrice, - 0, - 0, - item.Id, - 1 - ); - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(sellerAvatarAddress)); - orderDigestList.Add(orderDigest); - _initialState = _initialState.SetState(orderDigestList.Address, orderDigestList.Serialize()); - - var digest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - shopState.Add(digest, 0); - _initialState = _initialState.SetState(sellerAvatarAddress, sellerAvatarState.Serialize()); - } - - _initialState = _initialState.SetState(Order.DeriveAddress(_orderId), order.Serialize()); - } - - _initialState = _initialState.SetState(shopAddress, shopState.Serialize()); - } - - if (errorCodeMember.NotEnoughBalance) - { - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - } - - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - tradableId, - sellerAgentAddress, - sellerAvatarAddress, - ItemSubType.Food, - price - ); - - var action = new Buy10 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - errorCodeMember.ErrorCode, - action.errors.Select(r => r.errorCode) - ); - - foreach (var address in new[] { agentAddress, sellerAgentAddress, GoldCurrencyState.Address }) - { - Assert.Equal( - _initialState.GetBalance(address, _goldCurrencyState.Currency), - nextState.GetBalance(address, _goldCurrencyState.Currency) - ); - } - } - - [Theory] - [MemberData(nameof(GetReconfigureFungibleItemMemberData))] - public void Execute_ReconfigureFungibleItem(params OrderData[] orderDataList) - { - var buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - var purchaseInfos = new List(); - var firstData = orderDataList.First(); - var (sellerAvatarState, sellerAgentState) = CreateAvatarState(firstData.SellerAgentAddress, firstData.SellerAvatarAddress); - - var dummyItem = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - sellerAvatarState.inventory.AddItem2((ItemBase)dummyItem, orderDataList.Sum(x => x.ItemCount)); - - foreach (var orderData in orderDataList) - { - var orderId = orderData.OrderId; - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - ITradableItem tradableItem = material; - var itemSubType = ItemSubType.Hourglass; - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update(mail); - buyerAvatarState.Update(mail); - } - - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = _initialState.GetState(shardedShopAddress) is null - ? new ShardedShopStateV2(shardedShopAddress) - : new ShardedShopStateV2((Dictionary)_initialState.GetState(shardedShopAddress)); - var order = OrderFactory.Create( - sellerAgentState.address, - sellerAvatarState.address, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, orderData.Price, 0), - tradableItem.TradableId, - 0, - itemSubType, - orderData.ItemCount - ); - var inventoryAddress = orderData.SellerAvatarAddress.Derive(LegacyInventoryKey); - _initialState.SetState(inventoryAddress, sellerAvatarState.inventory.Serialize()); - - var sellItem = order.Sell3(sellerAvatarState); - var orderDigest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - - Address digestListAddress = OrderDigestListState.DeriveAddress(firstData.SellerAvatarAddress); - var digestListState = new OrderDigestListState(OrderDigestListState.DeriveAddress(firstData.SellerAvatarAddress)); - if (_initialState.TryGetState(digestListAddress, out Dictionary rawDigestList)) - { - digestListState = new OrderDigestListState(rawDigestList); - } - - var orderDigestListState = digestListState; - orderDigestListState.Add(orderDigest); - shopState.Add(orderDigest, 0); - - Assert.Equal(order.ExpiredBlockIndex, sellItem.RequiredBlockIndex); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - sellerAvatarState.mailBox.Add(expirationMail); - - var purchaseInfo = new PurchaseInfo( - orderId, - tradableItem.TradableId, - firstData.SellerAgentAddress, - firstData.SellerAvatarAddress, - itemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(orderDigestListState.Address, orderDigestListState.Serialize()); - } - - var sumCount = orderDataList.Sum(x => x.ItemCount); - Assert.Equal(1, sellerAvatarState.inventory.Items.Count); - Assert.Equal(sumCount, sellerAvatarState.inventory.Items.First().count); - - var buyAction = new Buy10 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.Empty(buyAction.errors); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - Order order = - OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - FungibleAssetValue tax = order.GetTax(); - - int itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(100, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? orderDataList.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - Assert.Equal(2, nextSellerAvatarState.mailBox.OfType().Count()); - - var buyerMail = nextBuyerAvatarState.mailBox - .OfType() - .Single(i => i.OrderId.Equals(order.OrderId)); - Assert.Equal(order.OrderId, buyerMail.OrderId); - - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(_buyerAgentAddress, orderReceipt.BuyerAgentAddress); - Assert.Equal(_buyerAvatarAddress, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - var nextOrderDigestListState = new OrderDigestListState( - (Dictionary)nextState.GetState(OrderDigestListState.DeriveAddress(purchaseInfo.SellerAvatarAddress)) - ); - Assert.Empty(nextOrderDigestListState.OrderDigestList); - } - } - - [Fact] - public void Rehearsal() - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - default, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon, - new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0) - ); - - var action = new Buy10 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - var updatedAddresses = new List
() - { - _sellerAgentAddress, - _sellerAvatarAddress, - _sellerAvatarAddress.Derive(LegacyInventoryKey), - _sellerAvatarAddress.Derive(LegacyWorldInformationKey), - _sellerAvatarAddress.Derive(LegacyQuestListKey), - OrderDigestListState.DeriveAddress(_sellerAvatarAddress), - _buyerAgentAddress, - _buyerAvatarAddress, - _buyerAvatarAddress.Derive(LegacyInventoryKey), - _buyerAvatarAddress.Derive(LegacyWorldInformationKey), - _buyerAvatarAddress.Derive(LegacyQuestListKey), - Addresses.GoldCurrency, - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, _orderId), - OrderReceipt.DeriveAddress(_orderId), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _buyerAgentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void Execute_With_Testbed() - { - var result = BlockChainHelper.MakeInitialState(); - var testbed = result.GetTestbed(); - var nextState = result.GetState(); - var data = TestbedHelper.LoadData("TestbedSell"); - - Assert.Equal(testbed.Orders.Count(), testbed.result.ItemInfos.Count); - for (var i = 0; i < testbed.Orders.Count; i++) - { - Assert.Equal(data.Items[i].ItemSubType, testbed.Orders[i].ItemSubType); - } - - var purchaseInfos = new List(); - foreach (var order in testbed.Orders) - { - var purchaseInfo = new PurchaseInfo( - order.OrderId, - order.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - order.ItemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - } - - var prevBuyerGold = nextState.GetBalance(result.GetAgentState().address, nextState.GetGoldCurrency()); - - var buyAction = new Buy10 - { - buyerAvatarAddress = result.GetAvatarState().address, - purchaseInfos = purchaseInfos, - }; - - nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = nextState, - Random = new TestRandom(), - Rehearsal = false, - Signer = result.GetAgentState().address, - }); - - var totalTax = 0 * _goldCurrencyState.Currency; - var totalPrice = 0 * _goldCurrencyState.Currency; - var goldCurrencyState = nextState.GetGoldCurrency(); - var nextBuyerAvatarState = nextState.GetAvatarState(result.GetAvatarState().address); - - Assert.Empty(buyAction.errors); - - var agentRevenue = new Dictionary(); - foreach (var purchaseInfo in purchaseInfos) - { - var shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - - var order = OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - var tradableId = purchaseInfo.TradableId; - var itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - var nextSellerAvatarState = - nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - - Assert.True(nextBuyerAvatarState.inventory.TryGetTradableItem( - tradableId, 100, itemCount, out var _)); - Assert.False(nextSellerAvatarState.inventory.TryGetTradableItem( - tradableId, 100, itemCount, out var _)); - - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(result.GetAgentState().address, orderReceipt.BuyerAgentAddress); - Assert.Equal(result.GetAvatarState().address, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - totalTax += order.GetTax(); - totalPrice += order.Price; - - var revenue = order.Price - order.GetTax(); - if (agentRevenue.ContainsKey(order.SellerAgentAddress)) - { - agentRevenue[order.SellerAgentAddress] += revenue; - } - else - { - agentRevenue.Add(order.SellerAgentAddress, revenue); - } - - var mailCount = purchaseInfos.Count(x => - x.SellerAvatarAddress.Equals(purchaseInfo.SellerAvatarAddress)); - Assert.Equal(mailCount, nextSellerAvatarState.mailBox.OfType().Count()); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - } - - var buyerMails = nextBuyerAvatarState.mailBox.OfType().ToList(); - Assert.Equal(testbed.Orders.Count(), buyerMails.Count()); - foreach (var mail in buyerMails) - { - Assert.True(purchaseInfos.Exists(x => x.OrderId.Equals(mail.OrderId))); - } - - var buyerGold = nextState.GetBalance(result.GetAgentState().address, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - var goldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(result.GetCurrencyGold() + totalTax, goldCurrencyGold); - - foreach (var (agentAddress, expectedGold) in agentRevenue) - { - var gold = nextState.GetBalance(agentAddress, goldCurrencyState); - Assert.Equal(expectedGold, gold); - } - } - - private (AvatarState avatarState, AgentState agentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().ToAddress(); - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = avatarAddress; - - _initialState = _initialState - .SetState(agentAddress, agentState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - return (avatarState, agentState); - } - - public class OrderData - { - public ItemType ItemType { get; set; } - - public Guid TradableId { get; set; } - - public Guid OrderId { get; set; } - - public Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public int ItemCount { get; set; } - } - - public class ErrorCodeMember - { - public bool EqualSigner { get; set; } - - public bool BuyerExist { get; set; } - - public bool ShopStateExist { get; set; } - - public bool OrderExist { get; set; } - - public bool DigestExist { get; set; } - - public int ErrorCode { get; set; } - - public bool EqualSellerAgent { get; set; } - - public bool EqualSellerAvatar { get; set; } - - public bool EqualTradableId { get; set; } - - public bool EqualPrice { get; set; } - - public bool Expire { get; set; } - - public bool NotContains { get; set; } - - public bool NotEnoughBalance { get; set; } - - public bool Duplicate { get; set; } - } - -#pragma warning disable SA1201 - public static IEnumerable ErrorCodeMemberData() => new List - { - new object[] - { - new ErrorCodeMember() - { - EqualSigner = true, - ErrorCode = Buy.ErrorCodeInvalidAddress, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - ErrorCode = Buy.ErrorCodeInvalidTradableId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - ErrorCode = Buy.ErrorCodeInvalidPrice, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Expire = true, - ErrorCode = Buy.ErrorCodeShopItemExpired, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - NotEnoughBalance = true, - ErrorCode = Buy.ErrorCodeInsufficientBalance, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Duplicate = true, - ErrorCode = Buy.ErrorCodeDuplicateSell, - }, - }, - }; -#pragma warning restore SA1201 - } -} diff --git a/.Lib9c.Tests/Action/Buy11Test.cs b/.Lib9c.Tests/Action/Buy11Test.cs deleted file mode 100644 index 8e29e06c5d..0000000000 --- a/.Lib9c.Tests/Action/Buy11Test.cs +++ /dev/null @@ -1,1199 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Bencodex.Types; - using Lib9c.DevExtensions; - using Lib9c.DevExtensions.Model; - using Lib9c.Model.Order; - using Lib9c.Tests.TestHelper; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Buy11Test - { - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly Guid _orderId; - private IAccountStateDelta _initialState; - private IValue _arenaSheetState; - - public Buy11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - _orderId = new Guid("6d460c1a-755d-48e4-ad67-65d5f519dbc8"); - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _arenaSheetState = _initialState.GetState(arenaSheetAddress); - _initialState = _initialState.SetState(arenaSheetAddress, null); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 2, - }, - }; - } - - public static IEnumerable GetReconfigureFungibleItemMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 50, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex + 1, - Price = 10, - ItemCount = 60, - }, - }; - } - - [Theory] - [MemberData(nameof(GetExecuteMemberData))] - public void Execute(params OrderData[] orderDataList) - { - AvatarState buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - List purchaseInfos = new List(); - ShopState legacyShopState = _initialState.GetShopState(); - foreach (var orderData in orderDataList) - { - (AvatarState sellerAvatarState, AgentState sellerAgentState) = CreateAvatarState( - orderData.SellerAgentAddress, - orderData.SellerAvatarAddress - ); - ITradableItem tradableItem; - Guid orderId = orderData.OrderId; - Guid itemId = orderData.TradableId; - ItemSubType itemSubType; - if (orderData.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - 0); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else if (orderData.ItemType == ItemType.Costume) - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - tradableItem = costume; - itemSubType = costume.ItemSubType; - } - else - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - tradableItem = material; - itemSubType = ItemSubType.Hourglass; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update(mail); - buyerAvatarState.Update(mail); - } - - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = _initialState.GetState(shardedShopAddress) is null - ? new ShardedShopStateV2(shardedShopAddress) - : new ShardedShopStateV2((Dictionary)_initialState.GetState(shardedShopAddress)); - var order = OrderFactory.Create( - sellerAgentState.address, - sellerAvatarState.address, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, orderData.Price, 0), - tradableItem.TradableId, - 0, - itemSubType, - orderData.ItemCount - ); - sellerAvatarState.inventory.AddItem((ItemBase)tradableItem, orderData.ItemCount); - - var sellItem = order.Sell(sellerAvatarState); - var orderDigest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - Assert.True(sellerAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - - var orderDigestListState = new OrderDigestListState(OrderDigestListState.DeriveAddress(orderData.SellerAvatarAddress)); - orderDigestListState.Add(orderDigest); - shopState.Add(orderDigest, 0); - - Assert.Equal(order.ExpiredBlockIndex, sellItem.RequiredBlockIndex); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - sellerAvatarState.mailBox.Add(expirationMail); - - var purchaseInfo = new PurchaseInfo( - orderId, - tradableItem.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - itemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(orderDigestListState.Address, orderDigestListState.Serialize()); - } - - var buyAction = new Buy11 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - FungibleAssetValue totalTax = 0 * _goldCurrencyState.Currency; - FungibleAssetValue totalPrice = 0 * _goldCurrencyState.Currency; - Currency goldCurrencyState = nextState.GetGoldCurrency(); - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.Empty(buyAction.errors); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - Order order = - OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - FungibleAssetValue tax = order.GetTax(); - FungibleAssetValue taxedPrice = order.Price - tax; - totalTax += tax; - totalPrice += order.Price; - - int itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(100, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? orderDataList.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - Assert.Single(nextSellerAvatarState.mailBox.OfType()); - var sellerMail = nextSellerAvatarState.mailBox.OfType().First(); - Assert.Equal(order.OrderId, sellerMail.OrderId); - - var buyerMail = nextBuyerAvatarState.mailBox - .OfType() - .Single(i => i.OrderId.Equals(order.OrderId)); - Assert.Equal(order.OrderId, buyerMail.OrderId); - - FungibleAssetValue sellerGold = - nextState.GetBalance(purchaseInfo.SellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice, sellerGold); - - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(_buyerAgentAddress, orderReceipt.BuyerAgentAddress); - Assert.Equal(_buyerAvatarAddress, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - var nextOrderDigestListState = new OrderDigestListState( - (Dictionary)nextState.GetState(OrderDigestListState.DeriveAddress(purchaseInfo.SellerAvatarAddress)) - ); - Assert.Empty(nextOrderDigestListState.OrderDigestList); - } - - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var goldCurrencyGold = nextState.GetBalance(Buy11.GetFeeStoreAddress(), goldCurrencyState); - Assert.Equal(totalTax, goldCurrencyGold); - var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - var prevBuyerGold = _initialState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - } - - [Theory] - [InlineData(false, false, typeof(FailedLoadStateException))] - [InlineData(true, false, typeof(NotEnoughClearedStageLevelException))] - public void Execute_Throw_Exception(bool equalAvatarAddress, bool clearStage, Type exc) - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - default, - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food, - _goldCurrencyState.Currency * 0 - ); - - if (!clearStage) - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - } - - var avatarAddress = equalAvatarAddress ? _buyerAvatarAddress : default; - var action = new Buy11 - { - buyerAvatarAddress = avatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - Assert.Throws(exc, () => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Theory] - [MemberData(nameof(ErrorCodeMemberData))] - public void Execute_ErrorCode(ErrorCodeMember errorCodeMember) - { - var context = new ActionContext(); - var agentAddress = errorCodeMember.BuyerExist ? _buyerAgentAddress : default; - var orderPrice = new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0); - var sellerAvatarAddress = errorCodeMember.EqualSellerAvatar ? _sellerAvatarAddress : default; - Address sellerAgentAddress = default; - if (errorCodeMember.EqualSigner) - { - sellerAgentAddress = _buyerAgentAddress; - } - else if (errorCodeMember.EqualSellerAgent) - { - sellerAgentAddress = _sellerAgentAddress; - } - - var item = ItemFactory.CreateItem( - _tableSheets.ConsumableItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Food), new TestRandom()); - var orderTradableId = ((ITradableItem)item).TradableId; - var tradableId = errorCodeMember.EqualTradableId ? orderTradableId : Guid.NewGuid(); - var price = errorCodeMember.EqualPrice ? orderPrice : default; - - var blockIndex = errorCodeMember.Expire ? Order.ExpirationInterval + 1 : 10; - - if (errorCodeMember.ShopStateExist) - { - var shopAddress = ShardedShopStateV2.DeriveAddress(ItemSubType.Food, _orderId); - var shopState = new ShardedShopStateV2(shopAddress); - if (errorCodeMember.OrderExist) - { - var sellerAvatarState = _initialState.GetAvatarState(_sellerAvatarAddress); - if (!errorCodeMember.NotContains) - { - var orderLock = new OrderLock(_orderId); - sellerAvatarState.inventory.AddItem(item, iLock: orderLock); - } - - var order = OrderFactory.Create( - sellerAgentAddress, - sellerAvatarAddress, - _orderId, - orderPrice, - orderTradableId, - 0, - ItemSubType.Food, - 1 - ); - if (errorCodeMember.Duplicate) - { - _initialState = _initialState.SetState( - OrderReceipt.DeriveAddress(_orderId), - new OrderReceipt(_orderId, _buyerAgentAddress, _buyerAvatarAddress, 0) - .Serialize() - ); - } - - if (errorCodeMember.DigestExist) - { - var orderDigest = new OrderDigest( - sellerAvatarAddress, - order.StartedBlockIndex, - order.ExpiredBlockIndex, - order.OrderId, - order.TradableId, - orderPrice, - 0, - 0, - item.Id, - 1 - ); - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(sellerAvatarAddress)); - orderDigestList.Add(orderDigest); - _initialState = _initialState.SetState(orderDigestList.Address, orderDigestList.Serialize()); - - var digest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - shopState.Add(digest, 0); - _initialState = _initialState.SetState(sellerAvatarAddress, sellerAvatarState.Serialize()); - } - - _initialState = _initialState.SetState(Order.DeriveAddress(_orderId), order.Serialize()); - } - - _initialState = _initialState.SetState(shopAddress, shopState.Serialize()); - } - - if (errorCodeMember.NotEnoughBalance) - { - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - } - - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - tradableId, - sellerAgentAddress, - sellerAvatarAddress, - ItemSubType.Food, - price - ); - - var action = new Buy11 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - errorCodeMember.ErrorCode, - action.errors.Select(r => r.errorCode) - ); - - foreach (var address in new[] { agentAddress, sellerAgentAddress, GoldCurrencyState.Address }) - { - Assert.Equal( - _initialState.GetBalance(address, _goldCurrencyState.Currency), - nextState.GetBalance(address, _goldCurrencyState.Currency) - ); - } - } - - [Theory] - [MemberData(nameof(GetReconfigureFungibleItemMemberData))] - public void Execute_ReconfigureFungibleItem(params OrderData[] orderDataList) - { - var buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - var purchaseInfos = new List(); - var firstData = orderDataList.First(); - var (sellerAvatarState, sellerAgentState) = CreateAvatarState(firstData.SellerAgentAddress, firstData.SellerAvatarAddress); - - var dummyItem = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - sellerAvatarState.inventory.AddItem2((ItemBase)dummyItem, orderDataList.Sum(x => x.ItemCount)); - - foreach (var orderData in orderDataList) - { - var orderId = orderData.OrderId; - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - ITradableItem tradableItem = material; - var itemSubType = ItemSubType.Hourglass; - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update(mail); - buyerAvatarState.Update(mail); - } - - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = _initialState.GetState(shardedShopAddress) is null - ? new ShardedShopStateV2(shardedShopAddress) - : new ShardedShopStateV2((Dictionary)_initialState.GetState(shardedShopAddress)); - var order = OrderFactory.Create( - sellerAgentState.address, - sellerAvatarState.address, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, orderData.Price, 0), - tradableItem.TradableId, - 0, - itemSubType, - orderData.ItemCount - ); - var inventoryAddress = orderData.SellerAvatarAddress.Derive(LegacyInventoryKey); - _initialState.SetState(inventoryAddress, sellerAvatarState.inventory.Serialize()); - - var sellItem = order.Sell3(sellerAvatarState); - var orderDigest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - - Address digestListAddress = OrderDigestListState.DeriveAddress(firstData.SellerAvatarAddress); - var digestListState = new OrderDigestListState(OrderDigestListState.DeriveAddress(firstData.SellerAvatarAddress)); - if (_initialState.TryGetState(digestListAddress, out Dictionary rawDigestList)) - { - digestListState = new OrderDigestListState(rawDigestList); - } - - var orderDigestListState = digestListState; - orderDigestListState.Add(orderDigest); - shopState.Add(orderDigest, 0); - - Assert.Equal(order.ExpiredBlockIndex, sellItem.RequiredBlockIndex); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - sellerAvatarState.mailBox.Add(expirationMail); - - var purchaseInfo = new PurchaseInfo( - orderId, - tradableItem.TradableId, - firstData.SellerAgentAddress, - firstData.SellerAvatarAddress, - itemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(orderDigestListState.Address, orderDigestListState.Serialize()); - } - - var sumCount = orderDataList.Sum(x => x.ItemCount); - Assert.Equal(1, sellerAvatarState.inventory.Items.Count); - Assert.Equal(sumCount, sellerAvatarState.inventory.Items.First().count); - - var buyAction = new Buy11 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.Empty(buyAction.errors); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - Order order = - OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - FungibleAssetValue tax = order.GetTax(); - - int itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(100, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? orderDataList.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - Assert.Equal(2, nextSellerAvatarState.mailBox.OfType().Count()); - - var buyerMail = nextBuyerAvatarState.mailBox - .OfType() - .Single(i => i.OrderId.Equals(order.OrderId)); - Assert.Equal(order.OrderId, buyerMail.OrderId); - - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(_buyerAgentAddress, orderReceipt.BuyerAgentAddress); - Assert.Equal(_buyerAvatarAddress, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - var nextOrderDigestListState = new OrderDigestListState( - (Dictionary)nextState.GetState(OrderDigestListState.DeriveAddress(purchaseInfo.SellerAvatarAddress)) - ); - Assert.Empty(nextOrderDigestListState.OrderDigestList); - } - } - - [Fact] - public void Rehearsal() - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - default, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon, - new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0) - ); - - var action = new Buy11 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - var updatedAddresses = new List
() - { - _sellerAgentAddress, - _sellerAvatarAddress, - _sellerAvatarAddress.Derive(LegacyInventoryKey), - _sellerAvatarAddress.Derive(LegacyWorldInformationKey), - _sellerAvatarAddress.Derive(LegacyQuestListKey), - OrderDigestListState.DeriveAddress(_sellerAvatarAddress), - _buyerAgentAddress, - _buyerAvatarAddress, - _buyerAvatarAddress.Derive(LegacyInventoryKey), - _buyerAvatarAddress.Derive(LegacyWorldInformationKey), - _buyerAvatarAddress.Derive(LegacyQuestListKey), - Buy11.GetFeeStoreAddress(), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, _orderId), - OrderReceipt.DeriveAddress(_orderId), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _buyerAgentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void Execute_With_Testbed() - { - var result = BlockChainHelper.MakeInitialState(); - var testbed = result.GetTestbed(); - var nextState = result.GetState(); - var data = TestbedHelper.LoadData("TestbedSell"); - - Assert.Equal(testbed.Orders.Count(), testbed.result.ItemInfos.Count); - for (var i = 0; i < testbed.Orders.Count; i++) - { - Assert.Equal(data.Items[i].ItemSubType, testbed.Orders[i].ItemSubType); - } - - var purchaseInfos = new List(); - foreach (var order in testbed.Orders) - { - var purchaseInfo = new PurchaseInfo( - order.OrderId, - order.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - order.ItemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - } - - var prevBuyerGold = nextState.GetBalance(result.GetAgentState().address, nextState.GetGoldCurrency()); - - var buyAction = new Buy11 - { - buyerAvatarAddress = result.GetAvatarState().address, - purchaseInfos = purchaseInfos, - }; - - var arenaSheetAddress = Addresses.GetSheetAddress(); - nextState = nextState.SetState(arenaSheetAddress, null); - - nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = nextState, - Random = new TestRandom(), - Rehearsal = false, - Signer = result.GetAgentState().address, - }); - - var totalTax = 0 * _goldCurrencyState.Currency; - var totalPrice = 0 * _goldCurrencyState.Currency; - var goldCurrencyState = nextState.GetGoldCurrency(); - var nextBuyerAvatarState = nextState.GetAvatarState(result.GetAvatarState().address); - - Assert.Empty(buyAction.errors); - - var agentRevenue = new Dictionary(); - foreach (var purchaseInfo in purchaseInfos) - { - var shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - - var order = OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - var tradableId = purchaseInfo.TradableId; - var itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - var nextSellerAvatarState = - nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - - Assert.True(nextBuyerAvatarState.inventory.TryGetTradableItem( - tradableId, 100, itemCount, out var _)); - Assert.False(nextSellerAvatarState.inventory.TryGetTradableItem( - tradableId, 100, itemCount, out var _)); - - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(result.GetAgentState().address, orderReceipt.BuyerAgentAddress); - Assert.Equal(result.GetAvatarState().address, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - totalTax += order.GetTax(); - totalPrice += order.Price; - - var revenue = order.Price - order.GetTax(); - if (agentRevenue.ContainsKey(order.SellerAgentAddress)) - { - agentRevenue[order.SellerAgentAddress] += revenue; - } - else - { - agentRevenue.Add(order.SellerAgentAddress, revenue); - } - - var mailCount = purchaseInfos.Count(x => - x.SellerAvatarAddress.Equals(purchaseInfo.SellerAvatarAddress)); - Assert.Equal(mailCount, nextSellerAvatarState.mailBox.OfType().Count()); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - } - - var buyerMails = nextBuyerAvatarState.mailBox.OfType().ToList(); - Assert.Equal(testbed.Orders.Count(), buyerMails.Count()); - foreach (var mail in buyerMails) - { - Assert.True(purchaseInfos.Exists(x => x.OrderId.Equals(mail.OrderId))); - } - - var buyerGold = nextState.GetBalance(result.GetAgentState().address, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - var goldCurrencyGold = nextState.GetBalance(Buy11.GetFeeStoreAddress(), goldCurrencyState); - Assert.Equal(totalTax, goldCurrencyGold); - - foreach (var (agentAddress, expectedGold) in agentRevenue) - { - var gold = nextState.GetBalance(agentAddress, goldCurrencyState); - Assert.Equal(expectedGold, gold); - } - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var result = BlockChainHelper.MakeInitialState(); - var testbed = result.GetTestbed(); - var nextState = result.GetState(); - var data = TestbedHelper.LoadData("TestbedSell"); - - Assert.Equal(testbed.Orders.Count(), testbed.result.ItemInfos.Count); - for (var i = 0; i < testbed.Orders.Count; i++) - { - Assert.Equal(data.Items[i].ItemSubType, testbed.Orders[i].ItemSubType); - } - - var purchaseInfos = new List(); - foreach (var order in testbed.Orders) - { - var purchaseInfo = new PurchaseInfo( - order.OrderId, - order.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - order.ItemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - } - - var buyAction = new Buy11 - { - buyerAvatarAddress = result.GetAvatarState().address, - purchaseInfos = purchaseInfos, - }; - - var arenaSheetAddress = Addresses.GetSheetAddress(); - nextState = nextState.SetState(arenaSheetAddress, _arenaSheetState); - Assert.Throws(() => - { - buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = nextState, - Random = new TestRandom(), - Rehearsal = false, - Signer = result.GetAgentState().address, - }); - }); - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().ToAddress(); - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = avatarAddress; - - _initialState = _initialState - .SetState(agentAddress, agentState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - return (avatarState, agentState); - } - - public class OrderData - { - public ItemType ItemType { get; set; } - - public Guid TradableId { get; set; } - - public Guid OrderId { get; set; } - - public Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public int ItemCount { get; set; } - } - - public class ErrorCodeMember - { - public bool EqualSigner { get; set; } - - public bool BuyerExist { get; set; } - - public bool ShopStateExist { get; set; } - - public bool OrderExist { get; set; } - - public bool DigestExist { get; set; } - - public int ErrorCode { get; set; } - - public bool EqualSellerAgent { get; set; } - - public bool EqualSellerAvatar { get; set; } - - public bool EqualTradableId { get; set; } - - public bool EqualPrice { get; set; } - - public bool Expire { get; set; } - - public bool NotContains { get; set; } - - public bool NotEnoughBalance { get; set; } - - public bool Duplicate { get; set; } - } - -#pragma warning disable SA1201 - public static IEnumerable ErrorCodeMemberData() => new List - { - new object[] - { - new ErrorCodeMember() - { - EqualSigner = true, - ErrorCode = Buy.ErrorCodeInvalidAddress, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - ErrorCode = Buy.ErrorCodeInvalidTradableId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - ErrorCode = Buy.ErrorCodeInvalidPrice, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Expire = true, - ErrorCode = Buy.ErrorCodeShopItemExpired, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - NotEnoughBalance = true, - ErrorCode = Buy.ErrorCodeInsufficientBalance, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Duplicate = true, - ErrorCode = Buy.ErrorCodeDuplicateSell, - }, - }, - }; -#pragma warning restore SA1201 - } -} diff --git a/.Lib9c.Tests/Action/Buy2Test.cs b/.Lib9c.Tests/Action/Buy2Test.cs deleted file mode 100644 index 5aeb37272a..0000000000 --- a/.Lib9c.Tests/Action/Buy2Test.cs +++ /dev/null @@ -1,178 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Buy2Test - { - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public Buy2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - equipment)); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _buyerAvatarState.Update2(mail); - sellerAvatarState.Update2(mail); - } - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - Assert.NotEmpty(shopState.Products); - - var (productId, shopItem) = shopState.Products.FirstOrDefault(); - Assert.NotNull(shopItem); - - var tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - var taxedPrice = shopItem.Price - tax; - - var buyAction = new Buy2 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = productId, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - var nextShopState = nextState.GetShopState(); - Assert.Empty(nextShopState.Products); - - var nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - Assert.True( - nextBuyerAvatarState.inventory.TryGetNonFungibleItem(shopItem.ItemUsable.ItemId, out ItemUsable _)); - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var nextSellerAvatarState = nextState.GetAvatarState(_sellerAvatarAddress); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - - var goldCurrencyState = nextState.GetGoldCurrency(); - var goldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(tax, goldCurrencyGold); - var sellerGold = nextState.GetBalance(_sellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice, sellerGold); - var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(new FungibleAssetValue(goldCurrencyState, 0, 0), buyerGold); - } - } -} diff --git a/.Lib9c.Tests/Action/Buy3Test.cs b/.Lib9c.Tests/Action/Buy3Test.cs deleted file mode 100644 index bc131c3800..0000000000 --- a/.Lib9c.Tests/Action/Buy3Test.cs +++ /dev/null @@ -1,321 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Buy3Test - { - private const long ProductPrice = 100; - - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public Buy3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - - var consumable = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - equipment)); - - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - consumable)); - - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - costume)); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .MintAsset(context, _buyerAgentAddress, shopState.Products - .Select(pair => pair.Value.Price) - .Aggregate((totalPrice, next) => totalPrice + next)); - } - - [Fact] - public void Execute() - { - var previousStates = _initialState; - var goldCurrencyState = previousStates.GetGoldCurrency(); - var shopState = previousStates.GetShopState(); - Assert.Equal(3, shopState.Products.Count); - Assert.NotNull(shopState.Products); - - var buyerGold = previousStates.GetBalance(_buyerAgentAddress, goldCurrencyState); - var loopCount = 0; - foreach (var (productId, shopItem) in shopState.Products) - { - loopCount++; - var tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - var taxedPrice = shopItem.Price - tax; - - var buyAction = new Buy3 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = productId, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - var nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - if (shopItem.ItemUsable != null) - { - Assert.True(nextBuyerAvatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, out _)); - } - - if (shopItem.Costume != null) - { - Assert.True(nextBuyerAvatarState.inventory.TryGetNonFungibleItem( - shopItem.Costume.ItemId, out _)); - } - - var nextGoldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(tax * loopCount, nextGoldCurrencyGold); - var nextSellerGold = nextState.GetBalance(_sellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice * loopCount, nextSellerGold); - var nextBuyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(buyerGold - shopItem.Price * loopCount, nextBuyerGold); - - previousStates = nextState; - } - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new Buy3 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _buyerAgentAddress, - sellerAvatarAddress = _buyerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new Buy3 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - - var action = new Buy3 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new Buy3 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var shopState = _initialState.GetShopState(); - Assert.NotEmpty(shopState.Products); - - var (productId, shopItem) = shopState.Products.FirstOrDefault(); - Assert.NotNull(shopItem); - - var context = new ActionContext(); - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - - var action = new Buy3 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = productId, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/Buy4Test.cs b/.Lib9c.Tests/Action/Buy4Test.cs deleted file mode 100644 index 4210222b1e..0000000000 --- a/.Lib9c.Tests/Action/Buy4Test.cs +++ /dev/null @@ -1,345 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Buy4Test - { - private const long ProductPrice = 100; - - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public Buy4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - - var consumable = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - equipment)); - - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - consumable)); - - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - costume)); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _buyerAvatarState.Update2(mail); - sellerAvatarState.Update2(mail); - } - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .MintAsset(context, _buyerAgentAddress, shopState.Products - .Select(pair => pair.Value.Price) - .Aggregate((totalPrice, next) => totalPrice + next)); - } - - [Fact] - public void Execute() - { - var previousStates = _initialState; - var goldCurrencyState = previousStates.GetGoldCurrency(); - var shopState = previousStates.GetShopState(); - Assert.Equal(3, shopState.Products.Count); - Assert.NotNull(shopState.Products); - - var buyerGold = previousStates.GetBalance(_buyerAgentAddress, goldCurrencyState); - var loopCount = 0; - foreach (var (productId, shopItem) in shopState.Products) - { - loopCount++; - var tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - var taxedPrice = shopItem.Price - tax; - - var buyAction = new Buy4 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = productId, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - var nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - if (shopItem.ItemUsable != null) - { - Assert.True(nextBuyerAvatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, out _)); - } - - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var nextSellerAvatarState = nextState.GetAvatarState(_sellerAvatarAddress); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - - if (shopItem.Costume != null) - { - Assert.True(nextBuyerAvatarState.inventory.TryGetNonFungibleItem( - shopItem.Costume.ItemId, out _)); - } - - var nextGoldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(tax * loopCount, nextGoldCurrencyGold); - var nextSellerGold = nextState.GetBalance(_sellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice * loopCount, nextSellerGold); - var nextBuyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(buyerGold - shopItem.Price * loopCount, nextBuyerGold); - - previousStates = nextState; - } - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new Buy4 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _buyerAgentAddress, - sellerAvatarAddress = _buyerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new Buy4 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - - var action = new Buy4 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new Buy4 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = default, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var context = new ActionContext(); - var shopState = _initialState.GetShopState(); - Assert.NotEmpty(shopState.Products); - - var (productId, shopItem) = shopState.Products.FirstOrDefault(); - Assert.NotNull(shopItem); - - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - - var action = new Buy4 - { - buyerAvatarAddress = _buyerAvatarAddress, - productId = productId, - sellerAgentAddress = _sellerAgentAddress, - sellerAvatarAddress = _sellerAvatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/Buy6Test.cs b/.Lib9c.Tests/Action/Buy6Test.cs deleted file mode 100644 index c436887215..0000000000 --- a/.Lib9c.Tests/Action/Buy6Test.cs +++ /dev/null @@ -1,800 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Buy6Test - { - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly Guid _productId; - private IAccountStateDelta _initialState; - - public Buy6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - _productId = new Guid("6d460c1a-755d-48e4-ad67-65d5f519dbc8"); - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ContainsInInventory = true, - ItemCount = 1, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 20, - ContainsInInventory = false, - ItemCount = 1, - }, - }; - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ContainsInInventory = false, - ItemCount = 1, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ContainsInInventory = true, - ItemCount = 1, - }, - }; - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Material, - ItemId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ContainsInInventory = true, - ItemCount = 1, - }, - new ShopItemData() - { - ItemType = ItemType.Material, - ItemId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ContainsInInventory = true, - ItemCount = 2, - }, - }; - } - - [Theory] - [MemberData(nameof(GetExecuteMemberData))] - public void Execute(params ShopItemData[] shopItemMembers) - { - AvatarState buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - List purchaseInfos = new List(); - Dictionary shardedShopStates = new Dictionary(); - ShopState legacyShopState = _initialState.GetShopState(); - foreach (var shopItemData in shopItemMembers) - { - (AvatarState sellerAvatarState, AgentState sellerAgentState) = CreateAvatarState( - shopItemData.SellerAgentAddress, - shopItemData.SellerAvatarAddress - ); - ITradableItem tradableItem; - Guid productId = shopItemData.ProductId; - Guid itemId = shopItemData.ItemId; - long requiredBlockIndex = shopItemData.RequiredBlockIndex; - ItemSubType itemSubType; - if (shopItemData.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else if (shopItemData.ItemType == ItemType.Costume) - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - tradableItem = costume; - itemSubType = costume.ItemSubType; - } - else - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - material.RequiredBlockIndex = requiredBlockIndex; - tradableItem = material; - itemSubType = ItemSubType.Hourglass; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update2(mail); - buyerAvatarState.Update2(mail); - } - - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - ShardedShopState shopState = shardedShopStates.ContainsKey(shardedShopAddress) - ? shardedShopStates[shardedShopAddress] - : new ShardedShopState(shardedShopAddress); - var shopItem = new ShopItem( - sellerAgentState.address, - sellerAvatarState.address, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, shopItemData.Price, 0), - requiredBlockIndex, - tradableItem, - shopItemData.ItemCount - ); - - // Case for backward compatibility. - if (shopItemData.ContainsInInventory) - { - shopState.Register(shopItem); - shardedShopStates[shardedShopAddress] = shopState; - sellerAvatarState.inventory.AddItem2((ItemBase)tradableItem, shopItemData.ItemCount); - _initialState = _initialState.SetState(shardedShopAddress, shopState.Serialize()); - } - else - { - legacyShopState.Register(shopItem); - } - - Assert.Equal(requiredBlockIndex, tradableItem.RequiredBlockIndex); - Assert.Equal( - shopItemData.ContainsInInventory, - sellerAvatarState.inventory.TryGetTradableItems( - shopItemData.ItemId, - shopItemData.RequiredBlockIndex, - shopItemData.ItemCount, - out _ - ) - ); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var purchaseInfo = new PurchaseInfo0( - shopItem.ProductId, - shopItem.SellerAgentAddress, - shopItem.SellerAvatarAddress, - itemSubType - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(Addresses.Shop, legacyShopState.Serialize()); - } - - if (shopItemMembers.Any(i => i.ItemType == ItemType.Material)) - { - Assert.Empty(legacyShopState.Products); - Assert.Equal(2, shardedShopStates.Sum(r => r.Value.Products.Count)); - } - else - { - Assert.Single(legacyShopState.Products); - Assert.True(shardedShopStates.All(r => r.Value.Products.Count == 1)); - } - - var buyAction = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 1, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - FungibleAssetValue totalTax = 0 * _goldCurrencyState.Currency; - FungibleAssetValue totalPrice = 0 * _goldCurrencyState.Currency; - Currency goldCurrencyState = nextState.GetGoldCurrency(); - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.True(buyAction.buyerMultipleResult.purchaseResults.All(r => r.errorCode == 0)); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId); - var nextShopState = new ShardedShopState((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.Empty(nextShopState.Products); - Guid itemId = shopItemMembers - .Where(i => i.ProductId == purchaseInfo.productId) - .Select(i => i.ItemId).First(); - Buy7.PurchaseResult pr = - buyAction.buyerMultipleResult.purchaseResults.First(r => r.productId == purchaseInfo.productId); - ShopItem shopItem = pr.shopItem; - FungibleAssetValue tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - FungibleAssetValue taxedPrice = shopItem.Price - tax; - totalTax += tax; - totalPrice += shopItem.Price; - - int itemCount = shopItem.TradableFungibleItemCount == 0 ? 1 : shopItem.TradableFungibleItemCount; - Assert.Equal(shopItem.TradableFungibleItemCount == 0, pr.tradableFungibleItem is null); - Assert.Equal(shopItem.TradableFungibleItemCount, pr.tradableFungibleItemCount); - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - itemId, - 1, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(1, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? shopItemMembers.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarState(purchaseInfo.sellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - itemId, - 1, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - - FungibleAssetValue sellerGold = - nextState.GetBalance(purchaseInfo.sellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice, sellerGold); - } - - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var goldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(totalTax, goldCurrencyGold); - var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - var prevBuyerGold = _initialState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - ShopState nextLegacyShopState = nextState.GetShopState(); - Assert.Empty(nextLegacyShopState.Products); - } - - [Fact] - public void Execute_ErrorCode_InvalidAddress() - { - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food - ); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeInvalidAddress, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food - ); - - var action = new Buy6 - { - buyerAvatarAddress = default, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevel() - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food - ); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void Execute_ErrorCode_ItemDoesNotExist() - { - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - default, - default, - _sellerAvatarAddress, - ItemSubType.Weapon - ); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeItemDoesNotExist, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Theory] - [InlineData(ItemSubType.Hourglass)] - [InlineData(ItemSubType.ApStone)] - public void Execute_ErrorCode_ItemDoesNotExist_Material(ItemSubType itemSubType) - { - TradableMaterial material = - ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == itemSubType)); - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - default, - default, - _sellerAvatarAddress, - itemSubType - ); - - var shopItem = new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - _productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - material); - - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, _productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - shopState.Register(shopItem); - - _initialState = _initialState.SetState(shardedShopAddress, shopState.Serialize()); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeItemDoesNotExist, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Fact] - public void Execute_ErrorCode_InsufficientBalance() - { - var context = new ActionContext(); - Address shardedShopAddress = ShardedShopState.DeriveAddress(ItemSubType.Weapon, _productId); - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - - var shopItem = new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - _productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - itemUsable); - - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - shopState.Register(shopItem); - - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance) - .SetState(shardedShopAddress, shopState.Serialize()); - - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - _productId, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon - ); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeInsufficientBalance, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Theory] - [InlineData(ItemType.Equipment)] - [InlineData(ItemType.Consumable)] - [InlineData(ItemType.Costume)] - [InlineData(ItemType.Material)] - public void Execute_ErrorCode_ItemDoesNotExist_By_SellerAvatar(ItemType itemType) - { - ITradableItem tradableItem; - ItemSheet.Row row; - switch (itemType) - { - case ItemType.Consumable: - row = _tableSheets.ConsumableItemSheet.First; - break; - case ItemType.Costume: - row = _tableSheets.CostumeItemSheet.First; - break; - case ItemType.Equipment: - row = _tableSheets.EquipmentItemSheet.First; - break; - case ItemType.Material: - row = _tableSheets.MaterialItemSheet.OrderedList - .First(r => r.ItemSubType == ItemSubType.Hourglass); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - if (itemType == ItemType.Material) - { - tradableItem = ItemFactory.CreateTradableMaterial((MaterialItemSheet.Row)row); - } - else - { - tradableItem = (ITradableItem)ItemFactory.CreateItem(row, new TestRandom()); - } - - tradableItem.RequiredBlockIndex = Sell6.ExpiredBlockIndex; - - Address shardedShopAddress = ShardedShopState.DeriveAddress(tradableItem.ItemSubType, _productId); - - var shopItem = new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - _productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - tradableItem); - - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - shopState.Register(shopItem); - _initialState = _initialState.SetState(shardedShopAddress, shopState.Serialize()); - - Assert.True(shopItem.ExpiredBlockIndex > 0); - - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - _productId, - _sellerAgentAddress, - _sellerAvatarAddress, - tradableItem.ItemSubType - ); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeItemDoesNotExist, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Fact] - public void Execute_ErrorCode_ShopItemExpired() - { - IAccountStateDelta previousStates = _initialState; - Address shardedShopStateAddress = ShardedShopState.DeriveAddress(ItemSubType.Weapon, _productId); - ShardedShopState shopState = new ShardedShopState(shardedShopStateAddress); - Weapon itemUsable = (Weapon)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 10); - var shopItem = new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - _productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - 10, - itemUsable); - - shopState.Register(shopItem); - previousStates = previousStates.SetState(shardedShopStateAddress, shopState.Serialize()); - - Assert.True(shopState.Products.ContainsKey(_productId)); - - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - _productId, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon - ); - - var action = new Buy6 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 11, - PreviousState = previousStates, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeShopItemExpired, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().ToAddress(); - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = avatarAddress; - - _initialState = _initialState - .SetState(agentAddress, agentState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - return (avatarState, agentState); - } - - public class ShopItemData - { - public ItemType ItemType { get; set; } - - public Guid ItemId { get; set; } - - public Guid ProductId { get; set; } - - public Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public bool ContainsInInventory { get; set; } - - public int ItemCount { get; set; } - } - } -} diff --git a/.Lib9c.Tests/Action/Buy8Test.cs b/.Lib9c.Tests/Action/Buy8Test.cs deleted file mode 100644 index c11c8da02f..0000000000 --- a/.Lib9c.Tests/Action/Buy8Test.cs +++ /dev/null @@ -1,818 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Buy8Test - { - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly Guid _orderId; - private IAccountStateDelta _initialState; - - public Buy8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - _orderId = new Guid("6d460c1a-755d-48e4-ad67-65d5f519dbc8"); - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 2, - }, - }; - } - - [Theory] - [MemberData(nameof(GetExecuteMemberData))] - public void Execute(params OrderData[] orderDataList) - { - AvatarState buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - List purchaseInfos = new List(); - ShopState legacyShopState = _initialState.GetShopState(); - foreach (var orderData in orderDataList) - { - (AvatarState sellerAvatarState, AgentState sellerAgentState) = CreateAvatarState( - orderData.SellerAgentAddress, - orderData.SellerAvatarAddress - ); - ITradableItem tradableItem; - Guid orderId = orderData.OrderId; - Guid itemId = orderData.TradableId; - ItemSubType itemSubType; - if (orderData.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - 0); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else if (orderData.ItemType == ItemType.Costume) - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - tradableItem = costume; - itemSubType = costume.ItemSubType; - } - else - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - tradableItem = material; - itemSubType = ItemSubType.Hourglass; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update2(mail); - buyerAvatarState.Update2(mail); - } - - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = _initialState.GetState(shardedShopAddress) is null - ? new ShardedShopStateV2(shardedShopAddress) - : new ShardedShopStateV2((Dictionary)_initialState.GetState(shardedShopAddress)); - var order = OrderFactory.Create( - sellerAgentState.address, - sellerAvatarState.address, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, orderData.Price, 0), - tradableItem.TradableId, - 0, - itemSubType, - orderData.ItemCount - ); - sellerAvatarState.inventory.AddItem2((ItemBase)tradableItem, orderData.ItemCount); - var sellItem = order.Sell2(sellerAvatarState); - OrderDigest orderDigest = order.Digest2(sellerAvatarState, _tableSheets.CostumeStatSheet); - var orderDigestListState = - new OrderDigestListState(OrderDigestListState.DeriveAddress(orderData.SellerAvatarAddress)); - orderDigestListState.Add(orderDigest); - shopState.Add(orderDigest, 0); - - Assert.Equal(order.ExpiredBlockIndex, sellItem.RequiredBlockIndex); - Assert.True( - sellerAvatarState.inventory.TryGetTradableItems( - order.TradableId, - order.ExpiredBlockIndex, - orderData.ItemCount, - out _ - ) - ); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - sellerAvatarState.mailBox.Add(expirationMail); - - var purchaseInfo = new PurchaseInfo( - orderId, - tradableItem.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - itemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(orderDigestListState.Address, orderDigestListState.Serialize()); - } - - var buyAction = new Buy8 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - FungibleAssetValue totalTax = 0 * _goldCurrencyState.Currency; - FungibleAssetValue totalPrice = 0 * _goldCurrencyState.Currency; - Currency goldCurrencyState = nextState.GetGoldCurrency(); - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.Empty(buyAction.errors); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - Order order = - OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - FungibleAssetValue tax = order.GetTax(); - FungibleAssetValue taxedPrice = order.Price - tax; - totalTax += tax; - totalPrice += order.Price; - - int itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(100, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? orderDataList.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - Assert.Single(nextSellerAvatarState.mailBox.OfType()); - var sellerMail = nextSellerAvatarState.mailBox.OfType().First(); - Assert.Equal(order.OrderId, sellerMail.OrderId); - - var buyerMail = nextBuyerAvatarState.mailBox - .OfType() - .Single(i => i.OrderId.Equals(order.OrderId)); - Assert.Equal(order.OrderId, buyerMail.OrderId); - - FungibleAssetValue sellerGold = - nextState.GetBalance(purchaseInfo.SellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice, sellerGold); - - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(_buyerAgentAddress, orderReceipt.BuyerAgentAddress); - Assert.Equal(_buyerAvatarAddress, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - var nextOrderDigestListState = new OrderDigestListState( - (Dictionary)nextState.GetState(OrderDigestListState.DeriveAddress(purchaseInfo.SellerAvatarAddress)) - ); - Assert.Empty(nextOrderDigestListState.OrderDigestList); - } - - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var goldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(totalTax, goldCurrencyGold); - var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - var prevBuyerGold = _initialState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - } - - [Theory] - [InlineData(false, false, typeof(FailedLoadStateException))] - [InlineData(true, false, typeof(NotEnoughClearedStageLevelException))] - public void Execute_Throw_Exception(bool equalAvatarAddress, bool clearStage, Type exc) - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - default, - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food, - _goldCurrencyState.Currency * 0 - ); - - if (!clearStage) - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - } - - var avatarAddress = equalAvatarAddress ? _buyerAvatarAddress : default; - var action = new Buy8 - { - buyerAvatarAddress = avatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - Assert.Throws(exc, () => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Theory] - [MemberData(nameof(ErrorCodeMemberData))] - public void Execute_ErrorCode(ErrorCodeMember errorCodeMember) - { - var context = new ActionContext(); - var agentAddress = errorCodeMember.BuyerExist ? _buyerAgentAddress : default; - var orderPrice = new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0); - var sellerAvatarAddress = errorCodeMember.EqualSellerAvatar ? _sellerAvatarAddress : default; - Address sellerAgentAddress = default; - if (errorCodeMember.EqualSigner) - { - sellerAgentAddress = _buyerAgentAddress; - } - else if (errorCodeMember.EqualSellerAgent) - { - sellerAgentAddress = _sellerAgentAddress; - } - - var item = ItemFactory.CreateItem( - _tableSheets.ConsumableItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Food), new TestRandom()); - var orderTradableId = ((ITradableItem)item).TradableId; - var tradableId = errorCodeMember.EqualTradableId ? orderTradableId : Guid.NewGuid(); - var price = errorCodeMember.EqualPrice ? orderPrice : default; - - var blockIndex = errorCodeMember.Expire ? Order.ExpirationInterval + 1 : 10; - - if (errorCodeMember.ShopStateExist) - { - var shopAddress = ShardedShopStateV2.DeriveAddress(ItemSubType.Food, _orderId); - var shopState = new ShardedShopStateV2(shopAddress); - if (errorCodeMember.OrderExist) - { - var sellerAvatarState = _initialState.GetAvatarState(_sellerAvatarAddress); - if (!errorCodeMember.NotContains) - { - sellerAvatarState.inventory.AddItem2(item); - } - - var order = OrderFactory.Create( - sellerAgentAddress, - sellerAvatarAddress, - _orderId, - orderPrice, - orderTradableId, - 0, - ItemSubType.Food, - 1 - ); - if (errorCodeMember.Duplicate) - { - _initialState = _initialState.SetState( - OrderReceipt.DeriveAddress(_orderId), - new OrderReceipt(_orderId, _buyerAgentAddress, _buyerAvatarAddress, 0) - .Serialize() - ); - } - - _initialState = _initialState.SetState(Order.DeriveAddress(_orderId), order.Serialize()); - - if (errorCodeMember.DigestExist) - { - var digest = order.Digest2(sellerAvatarState, _tableSheets.CostumeStatSheet); - shopState.Add(digest, 0); - _initialState = _initialState.SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()); - } - } - - _initialState = _initialState.SetState(shopAddress, shopState.Serialize()); - } - - if (errorCodeMember.NotEnoughBalance) - { - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - } - - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - tradableId, - sellerAgentAddress, - sellerAvatarAddress, - ItemSubType.Food, - price - ); - - var action = new Buy8 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - errorCodeMember.ErrorCode, - action.errors.Select(r => r.errorCode) - ); - - foreach (var address in new[] { agentAddress, sellerAgentAddress, GoldCurrencyState.Address }) - { - Assert.Equal( - _initialState.GetBalance(address, _goldCurrencyState.Currency), - nextState.GetBalance(address, _goldCurrencyState.Currency) - ); - } - } - - [Fact] - public void Rehearsal() - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - default, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon, - new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0) - ); - - var action = new Buy8 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - var updatedAddresses = new List
() - { - _sellerAgentAddress, - _sellerAvatarAddress, - _sellerAvatarAddress.Derive(LegacyInventoryKey), - _sellerAvatarAddress.Derive(LegacyWorldInformationKey), - _sellerAvatarAddress.Derive(LegacyQuestListKey), - OrderDigestListState.DeriveAddress(_sellerAvatarAddress), - _buyerAgentAddress, - _buyerAvatarAddress, - _buyerAvatarAddress.Derive(LegacyInventoryKey), - _buyerAvatarAddress.Derive(LegacyWorldInformationKey), - _buyerAvatarAddress.Derive(LegacyQuestListKey), - Addresses.GoldCurrency, - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, _orderId), - OrderReceipt.DeriveAddress(_orderId), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _buyerAgentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().ToAddress(); - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = avatarAddress; - - _initialState = _initialState - .SetState(agentAddress, agentState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - return (avatarState, agentState); - } - - public class OrderData - { - public ItemType ItemType { get; set; } - - public Guid TradableId { get; set; } - - public Guid OrderId { get; set; } - - public Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public int ItemCount { get; set; } - } - - public class ErrorCodeMember - { - public bool EqualSigner { get; set; } - - public bool BuyerExist { get; set; } - - public bool ShopStateExist { get; set; } - - public bool OrderExist { get; set; } - - public bool DigestExist { get; set; } - - public int ErrorCode { get; set; } - - public bool EqualSellerAgent { get; set; } - - public bool EqualSellerAvatar { get; set; } - - public bool EqualTradableId { get; set; } - - public bool EqualPrice { get; set; } - - public bool Expire { get; set; } - - public bool NotContains { get; set; } - - public bool NotEnoughBalance { get; set; } - - public bool Duplicate { get; set; } - } - -#pragma warning disable SA1201 - public static IEnumerable ErrorCodeMemberData() => new List - { - new object[] - { - new ErrorCodeMember() - { - EqualSigner = true, - ErrorCode = Buy.ErrorCodeInvalidAddress, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - ErrorCode = Buy.ErrorCodeInvalidTradableId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - ErrorCode = Buy.ErrorCodeInvalidPrice, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Expire = true, - ErrorCode = Buy.ErrorCodeShopItemExpired, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - NotEnoughBalance = true, - ErrorCode = Buy.ErrorCodeInsufficientBalance, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Duplicate = true, - ErrorCode = Buy.ErrorCodeDuplicateSell, - }, - }, - }; -#pragma warning restore SA1201 - } -} diff --git a/.Lib9c.Tests/Action/Buy9Test.cs b/.Lib9c.Tests/Action/Buy9Test.cs deleted file mode 100644 index 27401158a0..0000000000 --- a/.Lib9c.Tests/Action/Buy9Test.cs +++ /dev/null @@ -1,919 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Numerics; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Buy9Test - { - private readonly Address _sellerAgentAddress; - private readonly Address _sellerAvatarAddress; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly Guid _orderId; - private IAccountStateDelta _initialState; - - public Buy9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentAddress = new PrivateKey().ToAddress(); - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var sellerAvatarState = new AvatarState( - _sellerAvatarAddress, - _sellerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - sellerAgentState.avatarAddresses[0] = _sellerAvatarAddress; - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - _orderId = new Guid("6d460c1a-755d-48e4-ad67-65d5f519dbc8"); - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_sellerAgentAddress, sellerAgentState.Serialize()) - .SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - FromPreviousAction = true, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - FromPreviousAction = true, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - FromPreviousAction = true, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - FromPreviousAction = true, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - FromPreviousAction = true, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 2, - FromPreviousAction = true, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - FromPreviousAction = false, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - FromPreviousAction = false, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - FromPreviousAction = false, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - FromPreviousAction = false, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ItemCount = 1, - FromPreviousAction = false, - }, - new OrderData() - { - ItemType = ItemType.Material, - TradableId = new Guid("15396359-04db-68d5-f24a-d89c18665900"), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 2, - FromPreviousAction = false, - }, - }; - } - - [Theory] - [MemberData(nameof(GetExecuteMemberData))] - public void Execute(params OrderData[] orderDataList) - { - AvatarState buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - List purchaseInfos = new List(); - ShopState legacyShopState = _initialState.GetShopState(); - foreach (var orderData in orderDataList) - { - (AvatarState sellerAvatarState, AgentState sellerAgentState) = CreateAvatarState( - orderData.SellerAgentAddress, - orderData.SellerAvatarAddress - ); - ITradableItem tradableItem; - Guid orderId = orderData.OrderId; - Guid itemId = orderData.TradableId; - ItemSubType itemSubType; - if (orderData.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - 0); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else if (orderData.ItemType == ItemType.Costume) - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - tradableItem = costume; - itemSubType = costume.ItemSubType; - } - else - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - tradableItem = material; - itemSubType = ItemSubType.Hourglass; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - sellerAvatarState.Update2(mail); - buyerAvatarState.Update2(mail); - } - - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = _initialState.GetState(shardedShopAddress) is null - ? new ShardedShopStateV2(shardedShopAddress) - : new ShardedShopStateV2((Dictionary)_initialState.GetState(shardedShopAddress)); - var order = OrderFactory.Create( - sellerAgentState.address, - sellerAvatarState.address, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, orderData.Price, 0), - tradableItem.TradableId, - 0, - itemSubType, - orderData.ItemCount - ); - sellerAvatarState.inventory.AddItem2((ItemBase)tradableItem, orderData.ItemCount); - - var sellItem = orderData.FromPreviousAction ? order.Sell2(sellerAvatarState) : order.Sell3(sellerAvatarState); - var orderDigest = orderData.FromPreviousAction - ? order.Digest2(sellerAvatarState, _tableSheets.CostumeStatSheet) - : order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - - if (orderData.FromPreviousAction) - { - Assert.True( - sellerAvatarState.inventory.TryGetTradableItems( - order.TradableId, - order.ExpiredBlockIndex, - orderData.ItemCount, - out _ - ) - ); - } - else - { - Assert.True(sellerAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - } - - var orderDigestListState = new OrderDigestListState(OrderDigestListState.DeriveAddress(orderData.SellerAvatarAddress)); - orderDigestListState.Add(orderDigest); - shopState.Add(orderDigest, 0); - - Assert.Equal(order.ExpiredBlockIndex, sellItem.RequiredBlockIndex); - Assert.DoesNotContain(((ItemBase)tradableItem).Id, buyerAvatarState.itemMap.Keys); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - sellerAvatarState.mailBox.Add(expirationMail); - - var purchaseInfo = new PurchaseInfo( - orderId, - tradableItem.TradableId, - order.SellerAgentAddress, - order.SellerAvatarAddress, - itemSubType, - order.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(orderDigestListState.Address, orderDigestListState.Serialize()); - } - - var buyAction = new Buy9 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - FungibleAssetValue totalTax = 0 * _goldCurrencyState.Currency; - FungibleAssetValue totalPrice = 0 * _goldCurrencyState.Currency; - Currency goldCurrencyState = nextState.GetGoldCurrency(); - AvatarState nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - - Assert.Empty(buyAction.errors); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.DoesNotContain(nextShopState.OrderDigestList, o => o.OrderId.Equals(purchaseInfo.OrderId)); - Order order = - OrderFactory.Deserialize( - (Dictionary)nextState.GetState(Order.DeriveAddress(purchaseInfo.OrderId))); - FungibleAssetValue tax = order.GetTax(); - FungibleAssetValue taxedPrice = order.Price - tax; - totalTax += tax; - totalPrice += order.Price; - - int itemCount = order is FungibleOrder fungibleOrder ? fungibleOrder.ItemCount : 1; - Assert.True( - nextBuyerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out List inventoryItems) - ); - Assert.Single(inventoryItems); - Inventory.Item inventoryItem = inventoryItems.First(); - ITradableItem tradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(100, tradableItem.RequiredBlockIndex); - int expectedCount = tradableItem is TradableMaterial - ? orderDataList.Sum(i => i.ItemCount) - : itemCount; - Assert.Equal(expectedCount, inventoryItem.count); - Assert.Equal(expectedCount, nextBuyerAvatarState.itemMap[((ItemBase)tradableItem).Id]); - - var nextSellerAvatarState = nextState.GetAvatarStateV2(purchaseInfo.SellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetTradableItems( - purchaseInfo.TradableId, - 100, - itemCount, - out _) - ); - Assert.Equal(30, nextSellerAvatarState.mailBox.Count); - Assert.Empty(nextSellerAvatarState.mailBox.OfType()); - Assert.Single(nextSellerAvatarState.mailBox.OfType()); - var sellerMail = nextSellerAvatarState.mailBox.OfType().First(); - Assert.Equal(order.OrderId, sellerMail.OrderId); - - var buyerMail = nextBuyerAvatarState.mailBox - .OfType() - .Single(i => i.OrderId.Equals(order.OrderId)); - Assert.Equal(order.OrderId, buyerMail.OrderId); - - FungibleAssetValue sellerGold = - nextState.GetBalance(purchaseInfo.SellerAgentAddress, goldCurrencyState); - Assert.Equal(taxedPrice, sellerGold); - - var orderReceipt = new OrderReceipt((Dictionary)nextState.GetState(OrderReceipt.DeriveAddress(order.OrderId))); - Assert.Equal(order.OrderId, orderReceipt.OrderId); - Assert.Equal(_buyerAgentAddress, orderReceipt.BuyerAgentAddress); - Assert.Equal(_buyerAvatarAddress, orderReceipt.BuyerAvatarAddress); - Assert.Equal(100, orderReceipt.TransferredBlockIndex); - - var nextOrderDigestListState = new OrderDigestListState( - (Dictionary)nextState.GetState(OrderDigestListState.DeriveAddress(purchaseInfo.SellerAvatarAddress)) - ); - Assert.Empty(nextOrderDigestListState.OrderDigestList); - } - - Assert.Equal(30, nextBuyerAvatarState.mailBox.Count); - - var goldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrencyState); - Assert.Equal(totalTax, goldCurrencyGold); - var buyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrencyState); - var prevBuyerGold = _initialState.GetBalance(_buyerAgentAddress, goldCurrencyState); - Assert.Equal(prevBuyerGold - totalPrice, buyerGold); - } - - [Theory] - [InlineData(false, false, typeof(FailedLoadStateException))] - [InlineData(true, false, typeof(NotEnoughClearedStageLevelException))] - public void Execute_Throw_Exception(bool equalAvatarAddress, bool clearStage, Type exc) - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - default, - default, - _buyerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Food, - _goldCurrencyState.Currency * 0 - ); - - if (!clearStage) - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - } - - var avatarAddress = equalAvatarAddress ? _buyerAvatarAddress : default; - var action = new Buy9 - { - buyerAvatarAddress = avatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - Assert.Throws(exc, () => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Theory] - [MemberData(nameof(ErrorCodeMemberData))] - public void Execute_ErrorCode(ErrorCodeMember errorCodeMember) - { - var context = new ActionContext(); - var agentAddress = errorCodeMember.BuyerExist ? _buyerAgentAddress : default; - var orderPrice = new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0); - var sellerAvatarAddress = errorCodeMember.EqualSellerAvatar ? _sellerAvatarAddress : default; - Address sellerAgentAddress = default; - if (errorCodeMember.EqualSigner) - { - sellerAgentAddress = _buyerAgentAddress; - } - else if (errorCodeMember.EqualSellerAgent) - { - sellerAgentAddress = _sellerAgentAddress; - } - - var item = ItemFactory.CreateItem( - _tableSheets.ConsumableItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Food), new TestRandom()); - var orderTradableId = ((ITradableItem)item).TradableId; - var tradableId = errorCodeMember.EqualTradableId ? orderTradableId : Guid.NewGuid(); - var price = errorCodeMember.EqualPrice ? orderPrice : default; - - var blockIndex = errorCodeMember.Expire ? Order.ExpirationInterval + 1 : 10; - - if (errorCodeMember.ShopStateExist) - { - var shopAddress = ShardedShopStateV2.DeriveAddress(ItemSubType.Food, _orderId); - var shopState = new ShardedShopStateV2(shopAddress); - if (errorCodeMember.OrderExist) - { - var sellerAvatarState = _initialState.GetAvatarState(_sellerAvatarAddress); - if (!errorCodeMember.NotContains) - { - var orderLock = new OrderLock(_orderId); - sellerAvatarState.inventory.AddItem2(item, iLock: orderLock); - } - - var order = OrderFactory.Create( - sellerAgentAddress, - sellerAvatarAddress, - _orderId, - orderPrice, - orderTradableId, - 0, - ItemSubType.Food, - 1 - ); - if (errorCodeMember.Duplicate) - { - _initialState = _initialState.SetState( - OrderReceipt.DeriveAddress(_orderId), - new OrderReceipt(_orderId, _buyerAgentAddress, _buyerAvatarAddress, 0) - .Serialize() - ); - } - - _initialState = _initialState.SetState(Order.DeriveAddress(_orderId), order.Serialize()); - - if (errorCodeMember.DigestExist) - { - var digest = order.Digest(sellerAvatarState, _tableSheets.CostumeStatSheet); - shopState.Add(digest, 0); - _initialState = _initialState.SetState(_sellerAvatarAddress, sellerAvatarState.Serialize()); - } - } - - _initialState = _initialState.SetState(shopAddress, shopState.Serialize()); - } - - if (errorCodeMember.NotEnoughBalance) - { - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - } - - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - tradableId, - sellerAgentAddress, - sellerAvatarAddress, - ItemSubType.Food, - price - ); - - var action = new Buy9 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - errorCodeMember.ErrorCode, - action.errors.Select(r => r.errorCode) - ); - - foreach (var address in new[] { agentAddress, sellerAgentAddress, GoldCurrencyState.Address }) - { - Assert.Equal( - _initialState.GetBalance(address, _goldCurrencyState.Currency), - nextState.GetBalance(address, _goldCurrencyState.Currency) - ); - } - } - - [Fact] - public void Rehearsal() - { - PurchaseInfo purchaseInfo = new PurchaseInfo( - _orderId, - default, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon, - new FungibleAssetValue(_goldCurrencyState.Currency, 10, 0) - ); - - var action = new Buy9 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo }, - }; - - var updatedAddresses = new List
() - { - _sellerAgentAddress, - _sellerAvatarAddress, - _sellerAvatarAddress.Derive(LegacyInventoryKey), - _sellerAvatarAddress.Derive(LegacyWorldInformationKey), - _sellerAvatarAddress.Derive(LegacyQuestListKey), - OrderDigestListState.DeriveAddress(_sellerAvatarAddress), - _buyerAgentAddress, - _buyerAvatarAddress, - _buyerAvatarAddress.Derive(LegacyInventoryKey), - _buyerAvatarAddress.Derive(LegacyWorldInformationKey), - _buyerAvatarAddress.Derive(LegacyQuestListKey), - Addresses.GoldCurrency, - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, _orderId), - OrderReceipt.DeriveAddress(_orderId), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _buyerAgentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().ToAddress(); - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = avatarAddress; - - _initialState = _initialState - .SetState(agentAddress, agentState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - return (avatarState, agentState); - } - - public class OrderData - { - public ItemType ItemType { get; set; } - - public Guid TradableId { get; set; } - - public Guid OrderId { get; set; } - - public Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public int ItemCount { get; set; } - - public bool FromPreviousAction { get; set; } - } - - public class ErrorCodeMember - { - public bool EqualSigner { get; set; } - - public bool BuyerExist { get; set; } - - public bool ShopStateExist { get; set; } - - public bool OrderExist { get; set; } - - public bool DigestExist { get; set; } - - public int ErrorCode { get; set; } - - public bool EqualSellerAgent { get; set; } - - public bool EqualSellerAvatar { get; set; } - - public bool EqualTradableId { get; set; } - - public bool EqualPrice { get; set; } - - public bool Expire { get; set; } - - public bool NotContains { get; set; } - - public bool NotEnoughBalance { get; set; } - - public bool Duplicate { get; set; } - } - -#pragma warning disable SA1201 - public static IEnumerable ErrorCodeMemberData() => new List - { - new object[] - { - new ErrorCodeMember() - { - EqualSigner = true, - ErrorCode = Buy.ErrorCodeInvalidAddress, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - ErrorCode = Buy.ErrorCodeInvalidOrderId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - ErrorCode = Buy.ErrorCodeFailedLoadingState, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - ErrorCode = Buy.ErrorCodeInvalidTradableId, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - ErrorCode = Buy.ErrorCodeInvalidPrice, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Expire = true, - ErrorCode = Buy.ErrorCodeShopItemExpired, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - NotEnoughBalance = true, - ErrorCode = Buy.ErrorCodeInsufficientBalance, - }, - }, - new object[] - { - new ErrorCodeMember() - { - BuyerExist = true, - ShopStateExist = true, - OrderExist = true, - DigestExist = true, - EqualSellerAgent = true, - EqualSellerAvatar = true, - EqualTradableId = true, - EqualPrice = true, - Duplicate = true, - ErrorCode = Buy.ErrorCodeDuplicateSell, - }, - }, - }; -#pragma warning restore SA1201 - } -} diff --git a/.Lib9c.Tests/Action/BuyMultipleTest.cs b/.Lib9c.Tests/Action/BuyMultipleTest.cs deleted file mode 100644 index 7be32d6d89..0000000000 --- a/.Lib9c.Tests/Action/BuyMultipleTest.cs +++ /dev/null @@ -1,671 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class BuyMultipleTest - { - private readonly Dictionary _sellerAgentStateMap; - private readonly Address _buyerAgentAddress; - private readonly Address _buyerAvatarAddress; - private readonly AvatarState _buyerAvatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public BuyMultipleTest(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _sellerAgentStateMap = new Dictionary(); - - _buyerAgentAddress = new PrivateKey().ToAddress(); - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _buyerAvatarState = new AvatarState( - _buyerAvatarAddress, - _buyerAgentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - buyerAgentState.avatarAddresses[0] = _buyerAvatarAddress; - - var shopState = new ShopState(); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_buyerAgentAddress, buyerAgentState.Serialize()) - .SetState(_buyerAvatarAddress, _buyerAvatarState.Serialize()) - .MintAsset(context, _buyerAgentAddress, _goldCurrencyState.Currency * 100); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = true, - Price = 10, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = false, - Price = 20, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Buy = true, - Price = 30, - ContainsInInventory = false, - }, - }; - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = false, - Price = 10, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = false, - Price = 50, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = false, - Price = 30, - ContainsInInventory = true, - }, - }; - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Buy = true, - Price = 20, - ContainsInInventory = false, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Buy = true, - Price = 50, - ContainsInInventory = false, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = 0, - Buy = true, - Price = 30, - ContainsInInventory = false, - }, - }; - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = true, - Price = 30, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = true, - Price = 30, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().ToAddress(), - SellerAvatarAddress = new PrivateKey().ToAddress(), - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Buy = true, - Price = 30, - ContainsInInventory = true, - }, - }; - } - - [Theory] - [MemberData(nameof(GetExecuteMemberData))] - public void Execute(params ShopItemData[] productDatas) - { - var buyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - - var goldCurrency = _initialState.GetGoldCurrency(); - var shopState = _initialState.GetShopState(); - var buyCount = 0; - var itemsToBuy = new List(); - - foreach (var product in productDatas) - { - var (sellerAvatarState, sellerAgentState) = - CreateAvatarState(product.SellerAgentAddress, product.SellerAvatarAddress); - - INonFungibleItem nonFungibleItem; - if (product.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - product.ItemId, - product.RequiredBlockIndex); - nonFungibleItem = itemUsable; - } - else - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, product.ItemId); - costume.Update(product.RequiredBlockIndex); - nonFungibleItem = costume; - } - - // Case for backward compatibility of `Buy` - if (product.ContainsInInventory) - { - sellerAvatarState.inventory.AddItem2((ItemBase)nonFungibleItem); - } - - var shopItemId = Guid.NewGuid(); - - var shopItem = new ShopItem( - sellerAgentState.address, - sellerAvatarState.address, - shopItemId, - new FungibleAssetValue(_goldCurrencyState.Currency, product.Price, 0), - product.RequiredBlockIndex, - nonFungibleItem); - shopState.Register(shopItem); - - if (product.Buy) - { - ++buyCount; - var purchaseInfo = new BuyMultiple.PurchaseInfo( - shopItem.ProductId, - shopItem.SellerAgentAddress, - shopItem.SellerAvatarAddress); - itemsToBuy.Add(purchaseInfo); - } - } - - Assert.NotNull(shopState.Products); - Assert.Equal(3, shopState.Products.Count); - - _initialState = _initialState - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()); - - var priceData = new PriceData(goldCurrency); - - foreach (var avatarState in _sellerAgentStateMap.Keys) - { - var agentState = _sellerAgentStateMap[avatarState]; - priceData.TaxedPriceSum[agentState.address] = new FungibleAssetValue(goldCurrency, 0, 0); - - _initialState = _initialState - .SetState(avatarState.address, avatarState.Serialize()); - } - - IAccountStateDelta previousStates = _initialState; - - var buyerGold = previousStates.GetBalance(_buyerAgentAddress, goldCurrency); - var priceSumData = productDatas - .Where(i => i.Buy) - .Aggregate(priceData, (priceSum, next) => - { - var price = new FungibleAssetValue(goldCurrency, next.Price, 0); - var tax = price.DivRem(100, out _) * Buy.TaxRate; - var taxedPrice = price - tax; - priceData.TaxSum += tax; - - var prevSum = priceData.TaxedPriceSum[next.SellerAgentAddress]; - priceData.TaxedPriceSum[next.SellerAgentAddress] = prevSum + taxedPrice; - priceData.PriceSum += price; - return priceData; - }); - - var buyMultipleAction = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = itemsToBuy, - }; - var nextState = buyMultipleAction.Execute(new ActionContext() - { - BlockIndex = 1, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _buyerAgentAddress, - }); - - var nextShopState = nextState.GetShopState(); - Assert.Equal(productDatas.Length - buyCount, nextShopState.Products.Count); - - var nextBuyerAvatarState = nextState.GetAvatarState(_buyerAvatarAddress); - foreach (var product in productDatas.Where(i => i.Buy)) - { - Assert.True( - nextBuyerAvatarState.inventory.TryGetNonFungibleItem( - product.ItemId, - out INonFungibleItem outNonFungibleItem) - ); - Assert.Equal(product.RequiredBlockIndex, outNonFungibleItem.RequiredBlockIndex); - } - - Assert.Equal(buyCount, nextBuyerAvatarState.mailBox.Count); - - goldCurrency = nextState.GetGoldCurrency(); - var nextGoldCurrencyGold = nextState.GetBalance(Addresses.GoldCurrency, goldCurrency); - Assert.Equal(priceSumData.TaxSum, nextGoldCurrencyGold); - - foreach (var product in productDatas) - { - var nextSellerGold = nextState.GetBalance(product.SellerAgentAddress, goldCurrency); - Assert.Equal(priceSumData.TaxedPriceSum[product.SellerAgentAddress], nextSellerGold); - } - - var nextBuyerGold = nextState.GetBalance(_buyerAgentAddress, goldCurrency); - Assert.Equal(buyerGold - priceSumData.PriceSum, nextBuyerGold); - - previousStates = nextState; - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var shopState = _initialState.GetShopState(); - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - shopState.Register(new ShopItem( - _buyerAgentAddress, - _buyerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - 100, - costume)); - - _initialState = _initialState - .SetState(Addresses.Shop, shopState.Serialize()); - - shopState = _initialState.GetShopState(); - var products = shopState.Products.Values - .Select(p => new BuyMultiple.PurchaseInfo( - p.ProductId, - p.SellerAgentAddress, - p.SellerAvatarAddress)) - .ToList(); - Assert.NotEmpty(products); - - var action = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = products, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_buyerAvatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - _initialState = _initialState.SetState(_buyerAvatarAddress, avatarState.Serialize()); - - var action = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistErrorByEmptyCollection() - { - var action = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new List(), - }; - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - var nextBuyerAvatarState = _initialState.GetAvatarState(_buyerAvatarAddress); - foreach (var result in action.buyerResult.purchaseResults) - { - Assert.Equal(BuyMultiple.ERROR_CODE_ITEM_DOES_NOT_EXIST, result.errorCode); - } - } - - [Fact] - public void ExecuteInsufficientBalanceError() - { - var shopState = _initialState.GetShopState(); - - var sellerAvatarAddress = new PrivateKey().ToAddress(); - var sellerAgentAddress = new PrivateKey().ToAddress(); - var (avatarState, agentState) = CreateAvatarState(sellerAgentAddress, sellerAvatarAddress); - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 1); - shopState.Register(new ShopItem( - sellerAgentAddress, - sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, 1, 0), - 100, - equipment)); - - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - shopState.Register(new ShopItem( - sellerAgentAddress, - sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - 100, - costume)); - - var context = new ActionContext(); - _initialState = _initialState - .SetState(Addresses.Shop, shopState.Serialize()); - shopState = _initialState.GetShopState(); - Assert.NotEmpty(shopState.Products); - - var products = shopState.Products.Values - .Select(p => new BuyMultiple.PurchaseInfo( - p.ProductId, - p.SellerAgentAddress, - p.SellerAvatarAddress)) - .ToList(); - Assert.NotEmpty(products); - - var balance = _initialState.GetBalance(_buyerAgentAddress, _goldCurrencyState.Currency); - _initialState = _initialState.BurnAsset(context, _buyerAgentAddress, balance); - - var action = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = products, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - var results = action.buyerResult.purchaseResults; - var isAllFailed = results.Any(r => r.errorCode == BuyMultiple.ERROR_CODE_INSUFFICIENT_BALANCE); - Assert.True(isAllFailed); - } - - [Fact] - public void ExecuteThrowShopItemExpiredError() - { - var sellerAvatarAddress = new PrivateKey().ToAddress(); - var sellerAgentAddress = new PrivateKey().ToAddress(); - var (avatarState, agentState) = CreateAvatarState(sellerAgentAddress, sellerAvatarAddress); - - IAccountStateDelta previousStates = _initialState; - var shopState = previousStates.GetShopState(); - - var productId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 10); - shopState.Register(new ShopItem( - sellerAgentAddress, - sellerAvatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - 10, - equipment)); - - previousStates = previousStates - .SetState(Addresses.Shop, shopState.Serialize()); - shopState = previousStates.GetShopState(); - - Assert.True(shopState.Products.ContainsKey(productId)); - var products = shopState.Products.Values - .Select(p => new BuyMultiple.PurchaseInfo( - p.ProductId, - p.SellerAgentAddress, - p.SellerAvatarAddress)) - .ToList(); - - var action = new BuyMultiple - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = products, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 11, - PreviousState = previousStates, - Random = new TestRandom(), - Signer = _buyerAgentAddress, - }); - - var results = action.buyerResult.purchaseResults; - var isAllFailed = results.Any(r => r.errorCode == BuyMultiple.ERROR_CODE_SHOPITEM_EXPIRED); - Assert.True(isAllFailed); - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().ToAddress(); - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = avatarAddress; - _sellerAgentStateMap[avatarState] = agentState; - - _initialState = _initialState - .SetState(agentAddress, agentState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - return (avatarState, agentState); - } - - private struct PriceData - { - public FungibleAssetValue TaxSum; - public Dictionary TaxedPriceSum; - public FungibleAssetValue PriceSum; - - public PriceData(Currency currency) - { - TaxSum = new FungibleAssetValue(currency, 0, 0); - TaxedPriceSum = new Dictionary(); - PriceSum = new FungibleAssetValue(currency, 0, 0); - } - } - - public class ShopItemData - { - public ItemType ItemType { get; set; } - - public Guid ItemId { get; set; } - - public Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public bool Buy { get; set; } - - public bool ContainsInInventory { get; set; } - } - } -} diff --git a/.Lib9c.Tests/Action/CancelMonsterCollectTest.cs b/.Lib9c.Tests/Action/CancelMonsterCollectTest.cs deleted file mode 100644 index 9d43a62122..0000000000 --- a/.Lib9c.Tests/Action/CancelMonsterCollectTest.cs +++ /dev/null @@ -1,213 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class CancelMonsterCollectTest - { - private readonly Address _signer; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _state; - - public CancelMonsterCollectTest() - { - _signer = default; - _state = new MockStateDelta(); - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - var agentState = new AgentState(_signer); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _state = _state - .SetState(_signer, agentState.Serialize()) - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - foreach ((string key, string value) in sheets) - { - _state = _state - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(7, 1, 1)] - [InlineData(6, 2, MonsterCollectionState0.RewardInterval)] - [InlineData(5, 3, MonsterCollectionState0.RewardInterval * 3)] - [InlineData(4, 3, MonsterCollectionState0.RewardInterval * 4)] - public void Execute(int prevLevel, int collectionLevel, long blockIndex) - { - var context = new ActionContext(); - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - List rewardInfos = _tableSheets.MonsterCollectionRewardSheet[prevLevel].Rewards; - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, prevLevel, 0, _tableSheets.MonsterCollectionRewardSheet); - Currency currency = _state.GetGoldCurrency(); - FungibleAssetValue balance = 0 * currency; - foreach (var row in _tableSheets.MonsterCollectionSheet) - { - if (collectionLevel < row.Level && row.Level <= prevLevel) - { - balance += row.RequiredGold * currency; - } - } - - Assert.All(monsterCollectionState.RewardLevelMap, kv => Assert.Equal(rewardInfos, kv.Value)); - - _state = _state - .SetState(collectionAddress, monsterCollectionState.Serialize()) - .MintAsset(context, collectionAddress, balance); - - CancelMonsterCollect action = new CancelMonsterCollect - { - collectRound = 0, - level = collectionLevel, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = blockIndex, - }); - - MonsterCollectionState0 nextMonsterCollectionState = new MonsterCollectionState0((Dictionary)nextState.GetState(collectionAddress)); - Assert.Equal(collectionLevel, nextMonsterCollectionState.Level); - Assert.Equal(0 * currency, nextState.GetBalance(collectionAddress, currency)); - Assert.Equal(balance, nextState.GetBalance(_signer, currency)); - - long rewardLevel = nextMonsterCollectionState.GetRewardLevel(blockIndex); - List nextRewardInfos = _tableSheets.MonsterCollectionRewardSheet[collectionLevel].Rewards; - for (long i = rewardLevel; i < 4; i++) - { - Assert.Equal(nextRewardInfos, nextMonsterCollectionState.RewardLevelMap[i + 1]); - } - } - - [Fact] - public void Execute_Throw_FailedLoadStateException_AgentState() - { - CancelMonsterCollect action = new CancelMonsterCollect - { - level = 0, - collectRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = new PrivateKey().ToAddress(), - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException_MonsterCollectionState() - { - CancelMonsterCollect action = new CancelMonsterCollect - { - level = 0, - collectRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(1, 1)] - [InlineData(2, 6)] - [InlineData(3, 0)] - public void Execute_Throw_InvalidLevelException(int prevLevel, int level) - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, prevLevel, 0, _tableSheets.MonsterCollectionRewardSheet); - - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - CancelMonsterCollect action = new CancelMonsterCollect - { - level = level, - collectRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_MonsterCollectionExpiredException() - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, 2, 0, _tableSheets.MonsterCollectionRewardSheet); - for (int i = 0; i < MonsterCollectionState0.RewardCapacity; i++) - { - MonsterCollectionResult monsterCollectionResult = new MonsterCollectionResult(Guid.NewGuid(), default, new List()); - monsterCollectionState.UpdateRewardMap(i + 1, monsterCollectionResult, 0); - } - - Assert.True(monsterCollectionState.End); - - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - CancelMonsterCollect action = new CancelMonsterCollect - { - level = 1, - collectRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, 2, 0, _tableSheets.MonsterCollectionRewardSheet); - - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - CancelMonsterCollect action = new CancelMonsterCollect - { - level = 1, - collectRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/ChargeActionPoint0Test.cs b/.Lib9c.Tests/Action/ChargeActionPoint0Test.cs deleted file mode 100644 index 7130c6a1d2..0000000000 --- a/.Lib9c.Tests/Action/ChargeActionPoint0Test.cs +++ /dev/null @@ -1,84 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class ChargeActionPoint0Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - public ChargeActionPoint0Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - } - - [Fact] - public void Execute() - { - var privateKey = new PrivateKey(); - var agentAddress = privateKey.PublicKey.ToAddress(); - var agent = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ) - { - actionPoint = 0, - }; - agent.avatarAddresses.Add(0, avatarAddress); - - var apStone = - ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone), - new TestRandom()); - avatarState.inventory.AddItem2(apStone); - - Assert.Equal(0, avatarState.actionPoint); - - var state = new MockStateDelta() - .SetState(Addresses.GameConfig, gameConfigState.Serialize()) - .SetState(agentAddress, agent.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var action = new ChargeActionPoint0() - { - avatarAddress = avatarAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(avatarAddress); - - Assert.Equal(gameConfigState.ActionPointMax, nextAvatarState.actionPoint); - } - } -} diff --git a/.Lib9c.Tests/Action/ChargeActionPoint2Test.cs b/.Lib9c.Tests/Action/ChargeActionPoint2Test.cs deleted file mode 100644 index 79b0ee3ee1..0000000000 --- a/.Lib9c.Tests/Action/ChargeActionPoint2Test.cs +++ /dev/null @@ -1,154 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class ChargeActionPoint2Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly IAccountStateDelta _initialState; - - public ChargeActionPoint2Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agent = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ) - { - actionPoint = 0, - }; - agent.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(Addresses.GameConfig, gameConfigState.Serialize()) - .SetState(_agentAddress, agent.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool useTradable) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (useTradable) - { - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem2(apStone); - } - else - { - var apStone = ItemFactory.CreateItem(row, new TestRandom()); - avatarState.inventory.AddItem2(apStone); - } - - Assert.Equal(0, avatarState.actionPoint); - - var state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var action = new ChargeActionPoint2() - { - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var gameConfigState = nextState.GetGameConfigState(); - Assert.Equal(gameConfigState.ActionPointMax, nextAvatarState.actionPoint); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new ChargeActionPoint2 - { - avatarAddress = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Signer = default, - }) - ); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_NotEnoughMaterialException(bool useTradable) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - if (useTradable) - { - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - apStone.RequiredBlockIndex = 10; - avatarState.inventory.AddItem2(apStone); - } - - Assert.Equal(0, avatarState.actionPoint); - - var state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new ChargeActionPoint2() - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimMonsterCollectionReward0Test.cs b/.Lib9c.Tests/Action/ClaimMonsterCollectionReward0Test.cs deleted file mode 100644 index d66875bad2..0000000000 --- a/.Lib9c.Tests/Action/ClaimMonsterCollectionReward0Test.cs +++ /dev/null @@ -1,299 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class ClaimMonsterCollectionReward0Test - { - private readonly Address _signer; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _state; - - public ClaimMonsterCollectionReward0Test() - { - _signer = default; - _avatarAddress = _signer.Derive("avatar"); - _state = new MockStateDelta(); - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - var rankingMapAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_signer); - var avatarState = new AvatarState( - _avatarAddress, - _signer, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress); - agentState.avatarAddresses[0] = _avatarAddress; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _state = _state - .SetState(_signer, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - foreach ((string key, string value) in sheets) - { - _state = _state - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(1, 0, 1)] - [InlineData(2, 0, 2)] - [InlineData(2, 1, 3)] - [InlineData(3, 0, 4)] - [InlineData(3, 1, 5)] - [InlineData(3, 2, 6)] - [InlineData(4, 0, 7)] - [InlineData(4, 1, 4)] - [InlineData(4, 2, 5)] - [InlineData(4, 3, 6)] - public void Execute(int rewardLevel, int prevRewardLevel, int collectionLevel) - { - var context = new ActionContext(); - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - List rewards = _tableSheets.MonsterCollectionRewardSheet[1].Rewards; - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, 1, 0, _tableSheets.MonsterCollectionRewardSheet); - for (int i = 0; i < prevRewardLevel; i++) - { - int level = i + 1; - MonsterCollectionResult result = new MonsterCollectionResult(Guid.NewGuid(), _avatarAddress, rewards); - monsterCollectionState.UpdateRewardMap(level, result, i * MonsterCollectionState0.RewardInterval); - } - - List collectionRewards = _tableSheets.MonsterCollectionRewardSheet[collectionLevel].Rewards; - monsterCollectionState.Update(collectionLevel, rewardLevel, _tableSheets.MonsterCollectionRewardSheet); - for (long i = rewardLevel; i < 4; i++) - { - Assert.Equal(collectionRewards, monsterCollectionState.RewardLevelMap[i + 1]); - } - - Dictionary rewardExpectedMap = new Dictionary(); - foreach (var (key, value) in monsterCollectionState.RewardLevelMap) - { - if (monsterCollectionState.RewardMap.ContainsKey(key) || key > rewardLevel) - { - continue; - } - - foreach (var info in value) - { - if (rewardExpectedMap.ContainsKey(info.ItemId)) - { - rewardExpectedMap[info.ItemId] += info.Quantity; - } - else - { - rewardExpectedMap[info.ItemId] = info.Quantity; - } - } - } - - AvatarState prevAvatarState = _state.GetAvatarState(_avatarAddress); - Assert.Empty(prevAvatarState.mailBox); - - Currency currency = _state.GetGoldCurrency(); - int collectionRound = _state.GetAgentState(_signer).MonsterCollectionRound; - - _state = _state - .SetState(collectionAddress, monsterCollectionState.Serialize()); - - FungibleAssetValue balance = 0 * currency; - if (rewardLevel == 4) - { - foreach (var row in _tableSheets.MonsterCollectionSheet) - { - if (row.Level <= collectionLevel) - { - balance += row.RequiredGold * currency; - } - } - - collectionRound += 1; - _state = _state - .MintAsset(context, collectionAddress, balance); - } - - Assert.Equal(prevRewardLevel, monsterCollectionState.RewardLevel); - Assert.Equal(0, _state.GetAgentState(_signer).MonsterCollectionRound); - - ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0 - { - avatarAddress = _avatarAddress, - collectionRound = 0, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = rewardLevel * MonsterCollectionState0.RewardInterval, - Random = new TestRandom(), - }); - - MonsterCollectionState0 nextMonsterCollectionState = new MonsterCollectionState0((Dictionary)nextState.GetState(collectionAddress)); - Assert.Equal(rewardLevel, nextMonsterCollectionState.RewardLevel); - - AvatarState nextAvatarState = nextState.GetAvatarState(_avatarAddress); - foreach (var (itemId, qty) in rewardExpectedMap) - { - Assert.True(nextAvatarState.inventory.HasItem(itemId, qty)); - } - - Assert.Equal(rewardLevel - prevRewardLevel, nextAvatarState.mailBox.Count); - Assert.All(nextAvatarState.mailBox, mail => - { - Assert.IsType(mail); - MonsterCollectionMail monsterCollectionMail = (MonsterCollectionMail)mail; - Assert.IsType(monsterCollectionMail.attachment); - MonsterCollectionResult result = (MonsterCollectionResult)monsterCollectionMail.attachment; - Assert.Equal(result.id, mail.id); - }); - - for (int i = 0; i < nextMonsterCollectionState.RewardLevel; i++) - { - int level = i + 1; - List rewardInfos = _tableSheets.MonsterCollectionRewardSheet[collectionLevel].Rewards; - Assert.Contains(level, nextMonsterCollectionState.RewardMap.Keys); - Assert.Equal(_avatarAddress, nextMonsterCollectionState.RewardMap[level].avatarAddress); - } - - Assert.Equal(0 * currency, nextState.GetBalance(collectionAddress, currency)); - Assert.Equal(balance, nextState.GetBalance(_signer, currency)); - Assert.Equal(collectionRound, nextState.GetAgentState(_signer).MonsterCollectionRound); - Assert.Equal(nextMonsterCollectionState.End, rewardLevel == 4); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException_AgentState() - { - ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0 - { - avatarAddress = _avatarAddress, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = new PrivateKey().ToAddress(), - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException_MonsterCollectionState() - { - ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0 - { - avatarAddress = _avatarAddress, - collectionRound = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_MonsterCollectionExpiredException() - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, 1, 0, _tableSheets.MonsterCollectionRewardSheet); - List rewards = _tableSheets.MonsterCollectionRewardSheet[4].Rewards; - MonsterCollectionResult result = new MonsterCollectionResult(Guid.NewGuid(), _avatarAddress, rewards); - monsterCollectionState.UpdateRewardMap(4, result, 0); - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0 - { - avatarAddress = _avatarAddress, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0, -1)] - [InlineData(0, MonsterCollectionState0.RewardInterval - 1)] - public void Execute_Throw_RequiredBlockIndexException(long startedBlockIndex, long blockIndex) - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, 1, startedBlockIndex, _tableSheets.MonsterCollectionRewardSheet); - List rewards = _tableSheets.MonsterCollectionRewardSheet[1].Rewards; - MonsterCollectionResult result = new MonsterCollectionResult(Guid.NewGuid(), _avatarAddress, rewards); - monsterCollectionState.UpdateRewardMap(1, result, 0); - - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0 - { - avatarAddress = _avatarAddress, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = blockIndex, - }) - ); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 monsterCollectionState = new MonsterCollectionState0(collectionAddress, 1, 0, _tableSheets.MonsterCollectionRewardSheet); - - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - ClaimMonsterCollectionReward0 action = new ClaimMonsterCollectionReward0 - { - avatarAddress = _avatarAddress, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = MonsterCollectionState0.ExpirationIndex, - Random = new TestRandom(), - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimMonsterCollectionReward2Test.cs b/.Lib9c.Tests/Action/ClaimMonsterCollectionReward2Test.cs deleted file mode 100644 index 8271b4072c..0000000000 --- a/.Lib9c.Tests/Action/ClaimMonsterCollectionReward2Test.cs +++ /dev/null @@ -1,381 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - using static Nekoyume.Model.Item.Inventory; - - public class ClaimMonsterCollectionReward2Test - { - private readonly Address _signer; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _state; - - public ClaimMonsterCollectionReward2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _signer = default; - _avatarAddress = _signer.Derive("avatar"); - _state = new MockStateDelta(); - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - var rankingMapAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_signer); - var avatarState = new AvatarState( - _avatarAddress, - _signer, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress); - agentState.avatarAddresses[0] = _avatarAddress; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _state = _state - .SetState(_signer, agentState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - foreach ((string key, string value) in sheets) - { - _state = _state - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [ClassData(typeof(ExecuteFixture))] - public void Execute(int collectionLevel, long claimBlockIndex, long? receivedBlockIndex, (int, int)[] expectedRewards, Type exc) - { - Address collectionAddress = MonsterCollectionState.DeriveAddress(_signer, 0); - var monsterCollectionState = new MonsterCollectionState(collectionAddress, collectionLevel, 0); - if (receivedBlockIndex is { } receivedBlockIndexNotNull) - { - monsterCollectionState.Claim(receivedBlockIndexNotNull); - } - - AvatarState prevAvatarState = _state.GetAvatarStateV2(_avatarAddress); - Assert.Empty(prevAvatarState.mailBox); - - Currency currency = _state.GetGoldCurrency(); - - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - Assert.Equal(0, _state.GetAgentState(_signer).MonsterCollectionRound); - Assert.Equal(0 * currency, _state.GetBalance(_signer, currency)); - Assert.Equal(0 * currency, _state.GetBalance(collectionAddress, currency)); - - ClaimMonsterCollectionReward2 action = new ClaimMonsterCollectionReward2 - { - avatarAddress = _avatarAddress, - }; - - if (exc is { }) - { - Assert.Throws(exc, () => - { - action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = claimBlockIndex, - Random = new TestRandom(), - }); - }); - } - else - { - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = claimBlockIndex, - Random = new TestRandom(), - }); - - var nextMonsterCollectionState = new MonsterCollectionState( - (Dictionary)nextState.GetState(collectionAddress) - ); - Assert.Equal(0, nextMonsterCollectionState.RewardLevel); - - AvatarState nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.mailBox); - Mail mail = nextAvatarState.mailBox.First(); - MonsterCollectionMail monsterCollectionMail = Assert.IsType(mail); - MonsterCollectionResult result = - Assert.IsType(monsterCollectionMail.attachment); - Assert.Equal(result.id, mail.id); - Assert.Equal(0, nextMonsterCollectionState.StartedBlockIndex); - Assert.Equal(claimBlockIndex, nextMonsterCollectionState.ReceivedBlockIndex); - Assert.Equal(0 * currency, nextState.GetBalance(_signer, currency)); - Assert.Equal(0, nextState.GetAgentState(_signer).MonsterCollectionRound); - - foreach ((int id, int quantity) in expectedRewards) - { - Assert.True(nextAvatarState.inventory.TryGetItem(id, out Item item)); - Assert.Equal(quantity, item.count); - } - } - } - - [Fact] - public void Execute_Throw_FailedLoadStateException_AgentState() - { - ClaimMonsterCollectionReward2 action = new ClaimMonsterCollectionReward2 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = new PrivateKey().ToAddress(), - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException_MonsterCollectionState() - { - ClaimMonsterCollectionReward2 action = new ClaimMonsterCollectionReward2 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Execute_Throw_RequiredBlockIndexException() - { - Address collectionAddress = MonsterCollectionState.DeriveAddress(_signer, 0); - var monsterCollectionState = new MonsterCollectionState(collectionAddress, 1, 0); - _state = _state.SetState(collectionAddress, monsterCollectionState.Serialize()); - - ClaimMonsterCollectionReward2 action = new ClaimMonsterCollectionReward2 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Rehearsal() - { - ClaimMonsterCollectionReward2 action = new ClaimMonsterCollectionReward2 - { - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - BlockIndex = 0, - Rehearsal = true, - } - ); - - List
updatedAddresses = new List
- { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - MonsterCollectionState.DeriveAddress(_signer, 0), - MonsterCollectionState.DeriveAddress(_signer, 1), - MonsterCollectionState.DeriveAddress(_signer, 2), - MonsterCollectionState.DeriveAddress(_signer, 3), - }; - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private class ExecuteFixture : IEnumerable - { - private readonly List _data = new List - { - new object[] - { - 1, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 80), - (500000, 1), - }, - null, - }, - new object[] - { - 2, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 265), - (500000, 2), - }, - null, - }, - new object[] - { - 3, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 1265), - (500000, 5), - }, - null, - }, - new object[] - { - 4, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 8465), - (500000, 31), - }, - null, - }, - new object[] - { - 5, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 45965), - (500000, 161), - }, - null, - }, - new object[] - { - 6, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 120965), - (500000, 361), - }, - null, - }, - new object[] - { - 7, - MonsterCollectionState.RewardInterval, - null, - new (int, int)[] - { - (400000, 350965), - (500000, 1121), - }, - null, - }, - new object[] - { - 1, - MonsterCollectionState.RewardInterval * 2, - null, - new (int, int)[] - { - (400000, 80 * 2), - (500000, 1 * 2), - }, - null, - }, - new object[] - { - 2, - MonsterCollectionState.RewardInterval * 2, - null, - new (int, int)[] - { - (400000, 265 * 2), - (500000, 2 * 2), - }, - null, - }, - new object[] - { - 1, - MonsterCollectionState.RewardInterval * 2, - MonsterCollectionState.RewardInterval * 2 - 1, - new (int, int)[] - { - (400000, 80), - (500000, 1), - }, - null, - }, - new object[] - { - 1, - 1, - null, - new (int, int)[] { }, - typeof(RequiredBlockIndexException), - }, - new object[] - { - 1, - MonsterCollectionState.RewardInterval + 1, - MonsterCollectionState.RewardInterval, - new (int, int)[] { }, - typeof(RequiredBlockIndexException), - }, - }; - - public IEnumerator GetEnumerator() => _data.GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => _data.GetEnumerator(); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimStakeReward1Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward1Test.cs deleted file mode 100644 index 642c9cdd39..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward1Test.cs +++ /dev/null @@ -1,115 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class ClaimStakeReward1Test - { - private readonly IAccountStateDelta _initialState; - private readonly Currency _currency; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - private readonly Address _signerAddress; - private readonly Address _avatarAddress; - - public ClaimStakeReward1Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(_currency); - - _signerAddress = new PrivateKey().ToAddress(); - var stakeStateAddress = StakeState.DeriveAddress(_signerAddress); - var agentState = new AgentState(_signerAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - agentState.avatarAddresses.Add(0, _avatarAddress); - var avatarState = new AvatarState( - _avatarAddress, - _signerAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress - ) - { - level = 100, - }; - _initialState = _initialState - .SetState(_signerAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()) - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(stakeStateAddress, new StakeState(stakeStateAddress, 0).Serialize()) - .MintAsset(context, stakeStateAddress, _currency * 100); - } - - [Fact] - public void Execute() - { - var action = new ClaimStakeReward1(_avatarAddress); - var states = action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signerAddress, - BlockIndex = StakeState.LockupInterval, - }); - - AvatarState avatarState = states.GetAvatarStateV2(_avatarAddress); - // regular (100 / 10) * 4 - Assert.Equal(40, avatarState.inventory.Items.First(x => x.item.Id == 400000).count); - // regular ((100 / 800) + 1) * 4 - // It must be never added into the inventory if the amount is 0. - Assert.Equal(4, avatarState.inventory.Items.First(x => x.item.Id == 500000).count); - - Assert.True(states.TryGetStakeState(_signerAddress, out StakeState stakeState)); - Assert.Equal(StakeState.LockupInterval, stakeState.ReceivedBlockIndex); - } - - [Fact] - public void Serialization() - { - var action = new ClaimStakeReward1(_avatarAddress); - var deserialized = new ClaimStakeReward1(); - deserialized.LoadPlainValue(action.PlainValue); - Assert.Equal(action.AvatarAddress, deserialized.AvatarAddress); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimStakeReward3Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward3Test.cs deleted file mode 100644 index 08be998518..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward3Test.cs +++ /dev/null @@ -1,237 +0,0 @@ -#nullable enable - -namespace Lib9c.Tests.Action -{ - using System.Linq; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class ClaimStakeReward3Test - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccountStateDelta _initialStatesWithAvatarStateV1; - private readonly IAccountStateDelta _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public ClaimStakeReward3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - ( - _, - _, - _avatarAddr, - _initialStatesWithAvatarStateV1, - _initialStatesWithAvatarStateV2) = InitializeUtil.InitializeStates( - agentAddr: _agentAddr); - _ncg = _initialStatesWithAvatarStateV1.GetGoldCurrency(); - } - - [Fact] - public void Serialization() - { - var action = new ClaimStakeReward3(_avatarAddr); - var deserialized = new ClaimStakeReward3(); - deserialized.LoadPlainValue(action.PlainValue); - Assert.Equal(action.AvatarAddress, deserialized.AvatarAddress); - } - - [Theory] - [InlineData(ClaimStakeReward2.ObsoletedIndex)] - [InlineData(ClaimStakeReward2.ObsoletedIndex - 1)] - public void Execute_Throw_ActionUnAvailableException(long blockIndex) - { - var action = new ClaimStakeReward3(_avatarAddr); - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStatesWithAvatarStateV2, - Signer = _agentAddr, - BlockIndex = blockIndex, - })); - } - - [Theory] - [InlineData( - ClaimStakeReward2.ObsoletedIndex, - 100L, - null, - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - 40, - 4, - 0 - )] - [InlineData( - ClaimStakeReward2.ObsoletedIndex, - 6000L, - null, - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - 4800, - 36, - 4 - )] - // Calculate rune start from hard fork index - [InlineData( - 0L, - 6000L, - 0L, - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - 136800, - 1026, - 4 - )] - // Stake reward v2 - // Stake before v2, prev. receive v1, receive v1 & v2 - [InlineData( - StakeState.StakeRewardSheetV2Index - StakeState.RewardInterval * 2, - 50L, - StakeState.StakeRewardSheetV2Index - StakeState.RewardInterval, - StakeState.StakeRewardSheetV2Index + 1, - 5, - 1, - 0 - )] - // Stake before v2, prev. receive v2, receive v2 - [InlineData( - StakeState.StakeRewardSheetV2Index - StakeState.RewardInterval, - 50L, - StakeState.StakeRewardSheetV2Index, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval, - 5, - 1, - 0 - )] - // Stake after v2, no prev. receive, receive v2 - [InlineData( - StakeState.StakeRewardSheetV2Index, - 6000L, - null, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval, - 1200, - 9, - 1 - )] - // stake after v2, prev. receive v2, receive v2 - [InlineData( - StakeState.StakeRewardSheetV2Index, - 50L, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval * 2, - 5, - 1, - 0 - )] - // stake before currency as reward, non prev. - [InlineData( - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval * 2, - 10_000_000L, - null, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval, - 3_000_000, - 37_506, - 4_998 - )] - // stake before currency as reward, prev. - [InlineData( - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval * 2, - 10_000_000L, - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval, - 2_000_000, - 25_004, - 3_332 - )] - public void Execute_Success( - long startedBlockIndex, - long stakeAmount, - long? previousRewardReceiveIndex, - long blockIndex, - int expectedHourglass, - int expectedApStone, - int expectedRune) - { - Execute( - _initialStatesWithAvatarStateV1, - _agentAddr, - _avatarAddr, - startedBlockIndex, - stakeAmount, - previousRewardReceiveIndex, - blockIndex, - expectedHourglass, - expectedApStone, - expectedRune); - - Execute( - _initialStatesWithAvatarStateV2, - _agentAddr, - _avatarAddr, - startedBlockIndex, - stakeAmount, - previousRewardReceiveIndex, - blockIndex, - expectedHourglass, - expectedApStone, - expectedRune); - } - - private void Execute( - IAccountStateDelta prevState, - Address agentAddr, - Address avatarAddr, - long startedBlockIndex, - long stakeAmount, - long? previousRewardReceiveIndex, - long blockIndex, - int expectedHourglass, - int expectedApStone, - int expectedRune) - { - var context = new ActionContext(); - var stakeStateAddr = StakeState.DeriveAddress(agentAddr); - var initialStakeState = new StakeState(stakeStateAddr, startedBlockIndex); - if (!(previousRewardReceiveIndex is null)) - { - initialStakeState.Claim((long)previousRewardReceiveIndex); - } - - prevState = prevState - .SetState(stakeStateAddr, initialStakeState.Serialize()) - .MintAsset(context, stakeStateAddr, _ncg * stakeAmount); - - var action = new ClaimStakeReward3(avatarAddr); - var states = action.Execute(new ActionContext - { - PreviousState = prevState, - Signer = agentAddr, - BlockIndex = blockIndex, - }); - - AvatarState avatarState = states.GetAvatarStateV2(avatarAddr); - Assert.Equal( - expectedHourglass, - avatarState.inventory.Items.First(x => x.item.Id == 400000).count); - // It must be never added into the inventory if the amount is 0. - Assert.Equal( - expectedApStone, - avatarState.inventory.Items.First(x => x.item.Id == 500000).count); - Assert.Equal( - expectedRune * RuneHelper.StakeRune, - states.GetBalance(avatarAddr, RuneHelper.StakeRune)); - - Assert.True(states.TryGetStakeState(agentAddr, out StakeState stakeState)); - Assert.Equal(blockIndex, stakeState.ReceivedBlockIndex); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimStakeReward4Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward4Test.cs deleted file mode 100644 index a2c11cb94b..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward4Test.cs +++ /dev/null @@ -1,306 +0,0 @@ -#nullable enable - -namespace Lib9c.Tests.Action -{ - using System.Linq; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class ClaimStakeReward4Test - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccountStateDelta _initialStatesWithAvatarStateV1; - private readonly IAccountStateDelta _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public ClaimStakeReward4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - ( - _, - _, - _avatarAddr, - _initialStatesWithAvatarStateV1, - _initialStatesWithAvatarStateV2) = InitializeUtil.InitializeStates( - agentAddr: _agentAddr); - _ncg = _initialStatesWithAvatarStateV1.GetGoldCurrency(); - } - - [Fact] - public void Serialization() - { - var action = new ClaimStakeReward4(_avatarAddr); - var deserialized = new ClaimStakeReward4(); - deserialized.LoadPlainValue(action.PlainValue); - Assert.Equal(action.AvatarAddress, deserialized.AvatarAddress); - } - - [Theory] - [InlineData( - ClaimStakeReward2.ObsoletedIndex, - 100L, - null, - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - 40, - 4, - 0, - null, - null, - 0L - )] - [InlineData( - ClaimStakeReward2.ObsoletedIndex, - 6000L, - null, - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - 4800, - 36, - 4, - null, - null, - 0L - )] - // Calculate rune start from hard fork index - [InlineData( - 0L, - 6000L, - 0L, - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - 136800, - 1026, - 4, - null, - null, - 0L - )] - // Stake reward v2 - // Stake before v2, prev. receive v1, receive v1 & v2 - [InlineData( - StakeState.StakeRewardSheetV2Index - StakeState.RewardInterval * 2, - 50L, - StakeState.StakeRewardSheetV2Index - StakeState.RewardInterval, - StakeState.StakeRewardSheetV2Index + 1, - 5, - 1, - 0, - null, - null, - 0L - )] - // Stake before v2, prev. receive v2, receive v2 - [InlineData( - StakeState.StakeRewardSheetV2Index - StakeState.RewardInterval, - 50L, - StakeState.StakeRewardSheetV2Index, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval, - 5, - 1, - 0, - null, - null, - 0L - )] - // Stake after v2, no prev. receive, receive v2 - [InlineData( - StakeState.StakeRewardSheetV2Index, - 6000L, - null, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval, - 1200, - 9, - 1, - null, - null, - 0L - )] - // stake after v2, prev. receive v2, receive v2 - [InlineData( - StakeState.StakeRewardSheetV2Index, - 50L, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval * 2, - 5, - 1, - 0, - null, - null, - 0L - )] - // stake before currency as reward, non prev. - [InlineData( - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval * 2, - 10_000_000L, - null, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval, - 3_000_000, - 37_506, - 4_998, - AgentAddressHex, - "GARAGE", - 100_000L - )] - // stake before currency as reward, prev. - [InlineData( - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval * 2, - 10_000_000L, - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval, - 2_000_000, - 25_004, - 3_332, - AgentAddressHex, - "GARAGE", - 100_000L - )] - // test tx(c46cf83c46bc106372015a5020d6b9f15dc733819a6dc3fde37d9b5625fc3d93) - [InlineData( - 7_009_561L, - 10_000_000L, - 7_110_390L, - 7_160_778L, - 0, - 0, - 0, - null, - null, - 0)] - public void Execute_Success( - long startedBlockIndex, - long stakeAmount, - long? previousRewardReceiveIndex, - long blockIndex, - int expectedHourglass, - int expectedApStone, - int expectedRune, - string expectedCurrencyAddrHex, - string expectedCurrencyTicker, - long expectedCurrencyAmount) - { - Execute( - _initialStatesWithAvatarStateV1, - _agentAddr, - _avatarAddr, - startedBlockIndex, - stakeAmount, - previousRewardReceiveIndex, - blockIndex, - expectedHourglass, - expectedApStone, - expectedRune, - expectedCurrencyAddrHex, - expectedCurrencyTicker, - expectedCurrencyAmount); - - Execute( - _initialStatesWithAvatarStateV2, - _agentAddr, - _avatarAddr, - startedBlockIndex, - stakeAmount, - previousRewardReceiveIndex, - blockIndex, - expectedHourglass, - expectedApStone, - expectedRune, - expectedCurrencyAddrHex, - expectedCurrencyTicker, - expectedCurrencyAmount); - } - - private void Execute( - IAccountStateDelta prevState, - Address agentAddr, - Address avatarAddr, - long startedBlockIndex, - long stakeAmount, - long? previousRewardReceiveIndex, - long blockIndex, - int expectedHourglass, - int expectedApStone, - int expectedRune, - string expectedCurrencyAddrHex, - string expectedCurrencyTicker, - long expectedCurrencyAmount) - { - var stakeStateAddr = StakeState.DeriveAddress(agentAddr); - var initialStakeState = new StakeState(stakeStateAddr, startedBlockIndex); - if (!(previousRewardReceiveIndex is null)) - { - initialStakeState.Claim((long)previousRewardReceiveIndex); - } - - prevState = prevState - .SetState(stakeStateAddr, initialStakeState.Serialize()) - .MintAsset(new ActionContext(), stakeStateAddr, _ncg * stakeAmount); - - var action = new ClaimStakeReward4(avatarAddr); - var states = action.Execute(new ActionContext - { - PreviousState = prevState, - Signer = agentAddr, - BlockIndex = blockIndex, - }); - - var avatarState = states.GetAvatarStateV2(avatarAddr); - if (expectedHourglass > 0) - { - Assert.Equal( - expectedHourglass, - avatarState.inventory.Items.First(x => x.item.Id == 400000).count); - } - else - { - Assert.DoesNotContain(avatarState.inventory.Items, x => x.item.Id == 400000); - } - - if (expectedApStone > 0) - { - Assert.Equal( - expectedApStone, - avatarState.inventory.Items.First(x => x.item.Id == 500000).count); - } - else - { - Assert.DoesNotContain(avatarState.inventory.Items, x => x.item.Id == 500000); - } - - if (expectedRune > 0) - { - Assert.Equal( - expectedRune * RuneHelper.StakeRune, - states.GetBalance(avatarAddr, RuneHelper.StakeRune)); - } - else - { - Assert.Equal( - 0 * RuneHelper.StakeRune, - states.GetBalance(avatarAddr, RuneHelper.StakeRune)); - } - - if (!string.IsNullOrEmpty(expectedCurrencyAddrHex)) - { - var addr = new Address(expectedCurrencyAddrHex); - var currency = Currencies.GetMinterlessCurrency(expectedCurrencyTicker); - Assert.Equal( - expectedCurrencyAmount * currency, - states.GetBalance(addr, currency)); - } - - Assert.True(states.TryGetStakeState(agentAddr, out StakeState stakeState)); - Assert.Equal(blockIndex, stakeState.ReceivedBlockIndex); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationConsumable0Test.cs b/.Lib9c.Tests/Action/CombinationConsumable0Test.cs deleted file mode 100644 index cbad109f85..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable0Test.cs +++ /dev/null @@ -1,292 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class CombinationConsumable0Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationConsumable0Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage(1, i, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - var action = new CombinationConsumable0() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new CombinationConsumable0() - { - AvatarAddress = _avatarAddress, - recipeId = 1, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - _initialState = _initialState - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()); - - var action = new CombinationConsumable0() - { - AvatarAddress = _avatarAddress, - recipeId = 1, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }) - ); - } - - [Fact] - public void ExecuteThrowCombinationSlotUnlockException() - { - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage(1, i, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage + 10).Serialize()); - - var action = new CombinationConsumable0() - { - AvatarAddress = _avatarAddress, - recipeId = 1, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }) - ); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundException() - { - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage(1, i, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - var action = new CombinationConsumable0() - { - AvatarAddress = _avatarAddress, - recipeId = -1, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughMaterialException() - { - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage(1, i, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - var action = new CombinationConsumable0() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }) - ); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary() - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary() - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationConsumable2Test.cs b/.Lib9c.Tests/Action/CombinationConsumable2Test.cs deleted file mode 100644 index b66de46b6b..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable2Test.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class CombinationConsumable2Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationConsumable2Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipment = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.First, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - var action = new CombinationConsumable2() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationConsumable3Test.cs b/.Lib9c.Tests/Action/CombinationConsumable3Test.cs deleted file mode 100644 index 29ebf29b82..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable3Test.cs +++ /dev/null @@ -1,138 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class CombinationConsumable3Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationConsumable3Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipment = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.First, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - var action = new CombinationConsumable3() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationConsumable4Test.cs b/.Lib9c.Tests/Action/CombinationConsumable4Test.cs deleted file mode 100644 index 57beb321ed..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable4Test.cs +++ /dev/null @@ -1,139 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class CombinationConsumable4Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationConsumable4Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipment = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.First, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - var action = new CombinationConsumable4() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationConsumable6Test.cs b/.Lib9c.Tests/Action/CombinationConsumable6Test.cs deleted file mode 100644 index ab50812d79..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable6Test.cs +++ /dev/null @@ -1,153 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - using static Lib9c.SerializeKeys; - - public class CombinationConsumable6Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationConsumable6Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipment = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.First, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState.SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - - if (backward) - { - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - _initialState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), _avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), _avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), _avatarState.questList.Serialize()) - .SetState(_avatarAddress, _avatarState.SerializeV2()); - } - - var action = new CombinationConsumable6() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationConsumable7Test.cs b/.Lib9c.Tests/Action/CombinationConsumable7Test.cs deleted file mode 100644 index cc17b1b4be..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable7Test.cs +++ /dev/null @@ -1,148 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - using static Lib9c.SerializeKeys; - - public class CombinationConsumable7Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly IAccountStateDelta _initialState; - - public CombinationConsumable7Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var gameConfigState = new GameConfigState(); - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var row = _tableSheets.ConsumableItemRecipeSheet.Values.First(); - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - avatarState.inventory.AddItem(material, count: materialInfo.Count); - } - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationConsumableAction; - for (var i = 1; i < requiredStage + 1; i++) - { - avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipment = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.First, default, 0); - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - avatarState.Update(mail); - } - - var previousState = _initialState.SetState(_slotAddress, new CombinationSlotState(_slotAddress, requiredStage).Serialize()); - if (backward) - { - previousState = previousState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var action = new CombinationConsumable7 - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment0Test.cs b/.Lib9c.Tests/Action/CombinationEquipment0Test.cs deleted file mode 100644 index aa526d8cff..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment0Test.cs +++ /dev/null @@ -1,401 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class CombinationEquipment0Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment0Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - _slotAddress, - new CombinationSlotState( - _slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - } - - [Fact] - public void ExecuteWithSubRecipe() - { - var rowList = _tableSheets.EquipmentItemRecipeSheet.Values.ToList(); - var row = rowList[1]; - var subRecipeId = row.SubRecipeIds.First(); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheet.Values.First(r => r.Id == subRecipeId); - foreach (var materialInfo in subRecipeRow.Materials) - { - materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - for (var i = 1; i < row.UnlockStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SubRecipeId = subRecipeId, - SlotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = 1, - SubRecipeId = 1, - SlotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void ExecuteThrowCombinationSlotUnlockException() - { - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(r => r.SubRecipeIds.Any()); - var subRecipeId = row.SubRecipeIds.First(); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheetV2.Values.First(r => r.Id == subRecipeId); - foreach (var materialInfo in subRecipeRow.Materials) - { - materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - for (var i = 1; i < row.UnlockStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - _slotAddress, - new CombinationSlotState(_slotAddress, row.UnlockStage + 10).Serialize() - ); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SubRecipeId = subRecipeId, - SlotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundException() - { - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = 999, - SlotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }) - ); - } - - [Fact] - public void ExecuteThrowSheetRowColumnException() - { - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(r => r.SubRecipeIds.Any()); - var subRecipeId = row.SubRecipeIds.First(); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - var subRecipeRow = _tableSheets.EquipmentItemSubRecipeSheetV2.Values.First(r => r.Id == subRecipeId); - foreach (var materialInfo in subRecipeRow.Materials) - { - materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: materialInfo.Count); - } - - for (var i = 1; i < row.UnlockStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SubRecipeId = 100, - SlotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction; - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(r => r.UnlockStage > requiredStage); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void ExecuteThrowNotEnoughMaterialException() - { - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(); - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment0() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment10Test.cs b/.Lib9c.Tests/Action/CombinationEquipment10Test.cs deleted file mode 100644 index 66f8f22c07..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment10Test.cs +++ /dev/null @@ -1,377 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static SerializeKeys; - - public class CombinationEquipment10Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - - public CombinationEquipment10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(false, 1, null)] - [InlineData(false, 145, 341)] - [InlineData(false, 145, 342)] - [InlineData(true, 1, null)] - [InlineData(true, 145, 341)] - [InlineData(true, 145, 342)] - public void Execute_Success(bool backward, int recipeId, int? subRecipeId) => - Execute(backward, recipeId, subRecipeId, 10000); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_InsufficientBalanceException(bool backward) - { - var subRecipeId = _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList - .First(e => e.RequiredGold > 0) - .Id; - var recipeId = _tableSheets.EquipmentItemRecipeSheet.OrderedList - .First(e => e.SubRecipeIds.Contains(subRecipeId)) - .Id; - - Assert.Throws(() => Execute( - backward, recipeId, subRecipeId, 0)); - } - - [Fact] - public void Rehearsal() - { - var action = new CombinationEquipment10 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = 1, - subRecipeId = 255, - }; - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.Blacksmith, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void AddAndUnlockOption() - { - var agentState = _initialState.GetAgentState(_agentAddress); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment12.AddAndUnlockOption( - agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - - [Theory] - [InlineData(1, false, 375, false)] - [InlineData(1, false, 374, false)] - [InlineData(2, true, 3, true)] - [InlineData(2, true, 2, false)] - [InlineData(3, false, 6, false)] - [InlineData(3, false, 5, false)] - [InlineData(134, true, 313, false)] - [InlineData(134, true, 314, false)] - [InlineData(134, true, 315, true)] - public void MadeWithMimisbrunnrRecipe( - int recipeId, - bool isElementalTypeFire, - int? subRecipeId, - bool isMadeWithMimisbrunnrRecipe) - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var requiredStage = row.UnlockStage; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - requiredStage); - - avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - } - - var context = new ActionContext(); - var previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - previousState = previousState.MintAsset(context, _agentAddress, 10_000 * currency); - - var action = new CombinationEquipment10 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - var isMadeWithMimisbrunnrRecipe_considerElementalType = - isElementalTypeFire && - ((Equipment)slotState.Result.itemUsable).MadeWithMimisbrunnrRecipe; - Assert.Equal( - isMadeWithMimisbrunnrRecipe, - isMadeWithMimisbrunnrRecipe_considerElementalType); - Assert.Equal( - isMadeWithMimisbrunnrRecipe, - ((Equipment)slotState.Result.itemUsable).IsMadeWithMimisbrunnrRecipe( - _tableSheets.EquipmentItemRecipeSheet, - _tableSheets.EquipmentItemSubRecipeSheetV2, - _tableSheets.EquipmentItemOptionSheet - )); - } - - private void Execute(bool backward, int recipeId, int? subRecipeId, int mintNCG) - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var requiredStage = row.UnlockStage; - var costActionPoint = row.RequiredActionPoint; - var costNCG = row.RequiredGold * currency; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var previousActionPoint = avatarState.actionPoint; - var previousResultEquipmentCount = - avatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId); - var previousMailCount = avatarState.mailBox.Count; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - requiredStage); - - avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - costActionPoint += subRow.RequiredActionPoint; - costNCG += subRow.RequiredGold * currency; - - foreach (var materialInfo in subRow.Materials) - { - material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - } - - var context = new ActionContext(); - IAccountStateDelta previousState; - if (backward) - { - previousState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - if (mintNCG > 0) - { - previousState = previousState.MintAsset(context, _agentAddress, mintNCG * currency); - } - - var goldCurrencyState = previousState.GetGoldCurrency(); - var previousNCG = previousState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(mintNCG * currency, previousNCG); - - var action = new CombinationEquipment10 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - if (subRecipeId.HasValue) - { - Assert.True(((Equipment)slotState.Result.itemUsable).optionCountFromCombination > 0); - } - else - { - Assert.Equal(0, ((Equipment)slotState.Result.itemUsable).optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(previousActionPoint - costActionPoint, nextAvatarState.actionPoint); - Assert.Equal(previousMailCount + 1, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - Assert.Equal( - previousResultEquipmentCount + 1, - nextAvatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId)); - - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(previousNCG - costNCG, agentGold); - - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); - Assert.Equal(costNCG, blackSmithGold); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment11Test.cs b/.Lib9c.Tests/Action/CombinationEquipment11Test.cs deleted file mode 100644 index 282575ee1d..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment11Test.cs +++ /dev/null @@ -1,410 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static SerializeKeys; - - public class CombinationEquipment11Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - private IValue _arenaSheetState; - - public CombinationEquipment11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _arenaSheetState = _initialState.GetState(arenaSheetAddress); - _initialState = _initialState.SetState(arenaSheetAddress, null); - } - - [Theory] - [InlineData(false, 1, null)] - [InlineData(false, 145, 341)] - [InlineData(false, 145, 342)] - [InlineData(true, 1, null)] - [InlineData(true, 145, 341)] - [InlineData(true, 145, 342)] - public void Execute_Success(bool backward, int recipeId, int? subRecipeId) => - Execute(backward, recipeId, subRecipeId, 10000); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_InsufficientBalanceException(bool backward) - { - var subRecipeId = _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList - .First(e => e.RequiredGold > 0) - .Id; - var recipeId = _tableSheets.EquipmentItemRecipeSheet.OrderedList - .First(e => e.SubRecipeIds.Contains(subRecipeId)) - .Id; - - Assert.Throws(() => Execute( - backward, recipeId, subRecipeId, 0)); - } - - [Fact] - public void Rehearsal() - { - var action = new CombinationEquipment11 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = 1, - subRecipeId = 255, - }; - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - ItemEnhancement10.GetFeeStoreAddress(), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void AddAndUnlockOption() - { - var agentState = _initialState.GetAgentState(_agentAddress); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment11.AddAndUnlockOption( - agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - - [Theory] - [InlineData(1, false, 375, false)] - [InlineData(1, false, 374, false)] - [InlineData(2, true, 3, true)] - [InlineData(2, true, 2, false)] - [InlineData(3, false, 6, false)] - [InlineData(3, false, 5, false)] - [InlineData(134, true, 313, false)] - [InlineData(134, true, 314, false)] - [InlineData(134, true, 315, true)] - public void MadeWithMimisbrunnrRecipe( - int recipeId, - bool isElementalTypeFire, - int? subRecipeId, - bool isMadeWithMimisbrunnrRecipe) - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var requiredStage = row.UnlockStage; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - requiredStage); - - avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - } - - var context = new ActionContext(); - var previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - previousState = previousState.MintAsset(context, _agentAddress, 10_000 * currency); - - var action = new CombinationEquipment11 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - var isMadeWithMimisbrunnrRecipe_considerElementalType = - isElementalTypeFire && - ((Equipment)slotState.Result.itemUsable).MadeWithMimisbrunnrRecipe; - Assert.Equal( - isMadeWithMimisbrunnrRecipe, - isMadeWithMimisbrunnrRecipe_considerElementalType); - Assert.Equal( - isMadeWithMimisbrunnrRecipe, - ((Equipment)slotState.Result.itemUsable).IsMadeWithMimisbrunnrRecipe( - _tableSheets.EquipmentItemRecipeSheet, - _tableSheets.EquipmentItemSubRecipeSheetV2, - _tableSheets.EquipmentItemOptionSheet - )); - } - - private void Execute(bool backward, int recipeId, int? subRecipeId, int mintNCG) - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var requiredStage = row.UnlockStage; - var costActionPoint = row.RequiredActionPoint; - var costNCG = row.RequiredGold * currency; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var previousActionPoint = avatarState.actionPoint; - var previousResultEquipmentCount = - avatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId); - var previousMailCount = avatarState.mailBox.Count; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - requiredStage); - - avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - costActionPoint += subRow.RequiredActionPoint; - costNCG += subRow.RequiredGold * currency; - - foreach (var materialInfo in subRow.Materials) - { - material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - } - - var context = new ActionContext(); - IAccountStateDelta previousState; - if (backward) - { - previousState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - if (mintNCG > 0) - { - previousState = previousState.MintAsset(context, _agentAddress, mintNCG * currency); - } - - var goldCurrencyState = previousState.GetGoldCurrency(); - var previousNCG = previousState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(mintNCG * currency, previousNCG); - - var action = new CombinationEquipment11 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - if (subRecipeId.HasValue) - { - Assert.True(((Equipment)slotState.Result.itemUsable).optionCountFromCombination > 0); - } - else - { - Assert.Equal(0, ((Equipment)slotState.Result.itemUsable).optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(previousActionPoint - costActionPoint, nextAvatarState.actionPoint); - Assert.Equal(previousMailCount + 1, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - Assert.Equal( - previousResultEquipmentCount + 1, - nextAvatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId)); - - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(previousNCG - costNCG, agentGold); - var fee = nextState.GetBalance(ItemEnhancement10.GetFeeStoreAddress(), goldCurrencyState); - Assert.Equal(costNCG, fee); - } - - [Fact] - private void Execute_ActionObsoletedException() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var previousState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - var arenaSheetAddress = Addresses.GetSheetAddress(); - previousState = previousState.SetState(arenaSheetAddress, _arenaSheetState); - var action = new CombinationEquipment11 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = 1, - subRecipeId = 1, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment12Test.cs b/.Lib9c.Tests/Action/CombinationEquipment12Test.cs deleted file mode 100644 index 2f4f0bc445..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment12Test.cs +++ /dev/null @@ -1,398 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Arena; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData.Crystal; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment12Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - private readonly AgentState _agentState; - private readonly AvatarState _avatarState; - - public CombinationEquipment12Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - _agentState = new AgentState(_agentAddress); - _agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var combinationSlotState = new CombinationSlotState( - _slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - - _initialState = new MockStateDelta() - .SetState(_slotAddress, combinationSlotState.Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - // Tutorial recipe. - [InlineData(null, false, false, true, true, false, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // Migration AvatarState. - [InlineData(null, false, false, true, true, true, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // SubRecipe - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 1, true, false, false, false, false)] - // Mimisbrunnr Equipment. - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, true, true, false, false)] - // Purchase CRYSTAL. - [InlineData(null, true, true, true, true, false, 3, 0, true, 1L, 1, null, false, false, false, true, false)] - // Purchase CRYSTAL with calculate previous cost. - [InlineData(null, true, true, true, true, false, 3, 0, true, 100_800L, 1, null, false, false, true, true, true)] - // Arena round not found - [InlineData(null, false, false, true, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // UnlockEquipmentRecipe not executed. - [InlineData(typeof(FailedLoadStateException), false, true, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // CRYSTAL not paid. - [InlineData(typeof(InvalidRecipeIdException), true, false, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // AgentState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, false, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // AvatarState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, false, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - [InlineData(typeof(FailedLoadStateException), true, true, true, false, true, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // Tutorial not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 1, 0, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, true, false, 3, 5, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState locked. - [InlineData(typeof(CombinationSlotUnlockException), true, true, true, true, false, 3, 0, false, 0L, 1, null, true, false, false, false, false)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 3, 0, true, 0L, 2, null, true, false, false, false, false)] - // Not enough material. - [InlineData(typeof(NotEnoughMaterialException), true, true, true, true, false, 3, 0, true, 0L, 1, null, false, false, false, false, false)] - // Purchase CRYSTAL failed by Mimisbrunnr material. - [InlineData(typeof(ArgumentException), true, true, true, true, false, 11, 0, true, 0L, 2, 3, false, false, true, true, false)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, false, true, false, false)] - public void Execute( - Type exc, - bool unlockIdsExist, - bool crystalUnlock, - bool agentExist, - bool avatarExist, - bool migrationRequired, - int stageId, - int slotIndex, - bool slotUnlock, - long blockIndex, - int recipeId, - int? subRecipeId, - bool enoughMaterial, - bool ncgBalanceExist, - bool mimisbrunnr, - bool payByCrystal, - bool previousCostStateExist - ) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - if (unlockIdsExist) - { - var unlockIds = List.Empty.Add(1.Serialize()); - if (crystalUnlock) - { - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - } - - if (agentExist) - { - state = state.SetState(_agentAddress, _agentState.Serialize()); - - if (avatarExist) - { - _avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - if (enoughMaterial) - { - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (ncgBalanceExist && subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - } - } - - if (migrationRequired) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - } - - if (!slotUnlock) - { - // Lock slot. - state = state.SetState( - _slotAddress, - new CombinationSlotState(_slotAddress, stageId + 1).Serialize() - ); - } - } - } - - int expectedCrystal = 0; - if (payByCrystal) - { - var crystalBalance = 0; - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var costSheet = _tableSheets.CrystalMaterialCostSheet; - crystalBalance += costSheet[row.MaterialId].CRYSTAL * row.MaterialCount; - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - if (costSheet.ContainsKey(materialInfo.Id)) - { - crystalBalance += costSheet[materialInfo.Id].CRYSTAL * row.MaterialCount; - } - } - } - - if (previousCostStateExist) - { - var previousCostAddress = Addresses.GetWeeklyCrystalCostAddress(6); - var previousCostState = new CrystalCostState(previousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL * 2); - var beforePreviousCostAddress = Addresses.GetWeeklyCrystalCostAddress(5); - var beforePreviousCostState = new CrystalCostState(beforePreviousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL); - - state = state - .SetState(previousCostAddress, previousCostState.Serialize()) - .SetState(beforePreviousCostAddress, beforePreviousCostState.Serialize()); - } - - expectedCrystal = crystalBalance; - state = state.MintAsset(context, _agentAddress, expectedCrystal * CrystalCalculator.CRYSTAL); - } - - var dailyCostAddress = - Addresses.GetDailyCrystalCostAddress((int)(blockIndex / CrystalCostState.DailyIntervalIndex)); - var weeklyInterval = _tableSheets.CrystalFluctuationSheet.Values.First(r => - r.Type == CrystalFluctuationSheet.ServiceType.Combination).BlockInterval; - var weeklyCostAddress = Addresses.GetWeeklyCrystalCostAddress((int)(blockIndex / weeklyInterval)); - - Assert.Null(state.GetState(dailyCostAddress)); - Assert.Null(state.GetState(weeklyCostAddress)); - - var action = new CombinationEquipment12 - { - avatarAddress = _avatarAddress, - slotIndex = slotIndex, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = payByCrystal, - }; - - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - }); - - var currency = nextState.GetGoldCurrency(); - Assert.Equal(0 * currency, nextState.GetBalance(_agentAddress, currency)); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var equipment = (Equipment)slotState.Result.itemUsable; - if (subRecipeId.HasValue) - { - Assert.True(equipment.optionCountFromCombination > 0); - - if (ncgBalanceExist) - { - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(blockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal(450 * currency, nextState.GetBalance(feeStoreAddress, currency)); - } - - Assert.Equal(mimisbrunnr, equipment.MadeWithMimisbrunnrRecipe); - Assert.Equal( - mimisbrunnr, - equipment.IsMadeWithMimisbrunnrRecipe( - _tableSheets.EquipmentItemRecipeSheet, - _tableSheets.EquipmentItemSubRecipeSheetV2, - _tableSheets.EquipmentItemOptionSheet - ) - ); - - if (mimisbrunnr) - { - Assert.Equal(ElementalType.Fire, equipment.ElementalType); - } - } - else - { - Assert.Equal(0, equipment.optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var mail = nextAvatarState.mailBox.OfType().First(); - - Assert.Equal(equipment, mail.attachment.itemUsable); - Assert.Equal(payByCrystal, !(nextState.GetState(dailyCostAddress) is null)); - Assert.Equal(payByCrystal, !(nextState.GetState(weeklyCostAddress) is null)); - - if (payByCrystal) - { - var dailyCostState = nextState.GetCrystalCostState(dailyCostAddress); - var weeklyCostState = nextState.GetCrystalCostState(weeklyCostAddress); - - Assert.Equal(0 * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - Assert.Equal(1, dailyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, dailyCostState.CRYSTAL); - Assert.Equal(1, weeklyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, weeklyCostState.CRYSTAL); - } - - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, nextState.GetBalance(Addresses.MaterialCost, CrystalCalculator.CRYSTAL)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - })); - } - } - - [Fact] - public void AddAndUnlockOption() - { - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment12.AddAndUnlockOption( - _agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment13Test.cs b/.Lib9c.Tests/Action/CombinationEquipment13Test.cs deleted file mode 100644 index f8204bd317..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment13Test.cs +++ /dev/null @@ -1,525 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData.Crystal; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment13Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - private readonly AgentState _agentState; - private readonly AvatarState _avatarState; - - public CombinationEquipment13Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - _agentState = new AgentState(_agentAddress); - _agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var combinationSlotState = new CombinationSlotState( - _slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - - _initialState = new MockStateDelta() - .SetState(_slotAddress, combinationSlotState.Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - // Tutorial recipe. - [InlineData(null, false, false, true, true, false, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // Migration AvatarState. - [InlineData(null, false, false, true, true, true, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // SubRecipe - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 1, true, false, false, false, false)] - // Mimisbrunnr Equipment. - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, true, true, false, false)] - // Purchase CRYSTAL. - [InlineData(null, true, true, true, true, false, 3, 0, true, 1L, 1, null, false, false, false, true, false)] - // Purchase CRYSTAL with calculate previous cost. - [InlineData(null, true, true, true, true, false, 3, 0, true, 100_800L, 1, null, false, false, true, true, true)] - // Arena round not found - [InlineData(null, false, false, true, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // UnlockEquipmentRecipe not executed. - [InlineData(typeof(FailedLoadStateException), false, true, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // CRYSTAL not paid. - [InlineData(typeof(InvalidRecipeIdException), true, false, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // AgentState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, false, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // AvatarState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, false, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - [InlineData(typeof(FailedLoadStateException), true, true, true, false, true, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // Tutorial not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 1, 0, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, true, false, 3, 5, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState locked. - [InlineData(typeof(CombinationSlotUnlockException), true, true, true, true, false, 3, 0, false, 0L, 1, null, true, false, false, false, false)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 3, 0, true, 0L, 2, null, true, false, false, false, false)] - // Not enough material. - [InlineData(typeof(NotEnoughMaterialException), true, true, true, true, false, 3, 0, true, 0L, 1, null, false, false, false, false, false)] - // Purchase CRYSTAL failed by Mimisbrunnr material. - [InlineData(typeof(ArgumentException), true, true, true, true, false, 11, 0, true, 0L, 2, 3, false, false, true, true, false)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, false, true, false, false)] - public void Execute( - Type exc, - bool unlockIdsExist, - bool crystalUnlock, - bool agentExist, - bool avatarExist, - bool migrationRequired, - int stageId, - int slotIndex, - bool slotUnlock, - long blockIndex, - int recipeId, - int? subRecipeId, - bool enoughMaterial, - bool ncgBalanceExist, - bool mimisbrunnr, - bool payByCrystal, - bool previousCostStateExist - ) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - if (unlockIdsExist) - { - var unlockIds = List.Empty.Add(1.Serialize()); - if (crystalUnlock) - { - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - } - - if (agentExist) - { - state = state.SetState(_agentAddress, _agentState.Serialize()); - - if (avatarExist) - { - _avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - if (enoughMaterial) - { - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (ncgBalanceExist && subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - } - } - - if (migrationRequired) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - } - - if (!slotUnlock) - { - // Lock slot. - state = state.SetState( - _slotAddress, - new CombinationSlotState(_slotAddress, stageId + 1).Serialize() - ); - } - } - } - - int expectedCrystal = 0; - if (payByCrystal) - { - var crystalBalance = 0; - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var costSheet = _tableSheets.CrystalMaterialCostSheet; - crystalBalance += costSheet[row.MaterialId].CRYSTAL * row.MaterialCount; - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - if (costSheet.ContainsKey(materialInfo.Id)) - { - crystalBalance += costSheet[materialInfo.Id].CRYSTAL * row.MaterialCount; - } - } - } - - if (previousCostStateExist) - { - var previousCostAddress = Addresses.GetWeeklyCrystalCostAddress(6); - var previousCostState = new CrystalCostState(previousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL * 2); - var beforePreviousCostAddress = Addresses.GetWeeklyCrystalCostAddress(5); - var beforePreviousCostState = new CrystalCostState(beforePreviousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL); - - state = state - .SetState(previousCostAddress, previousCostState.Serialize()) - .SetState(beforePreviousCostAddress, beforePreviousCostState.Serialize()); - } - - expectedCrystal = crystalBalance; - state = state.MintAsset(context, _agentAddress, expectedCrystal * CrystalCalculator.CRYSTAL); - } - - var dailyCostAddress = - Addresses.GetDailyCrystalCostAddress((int)(blockIndex / CrystalCostState.DailyIntervalIndex)); - var weeklyInterval = _tableSheets.CrystalFluctuationSheet.Values.First(r => - r.Type == CrystalFluctuationSheet.ServiceType.Combination).BlockInterval; - var weeklyCostAddress = Addresses.GetWeeklyCrystalCostAddress((int)(blockIndex / weeklyInterval)); - - Assert.Null(state.GetState(dailyCostAddress)); - Assert.Null(state.GetState(weeklyCostAddress)); - - var action = new CombinationEquipment13 - { - avatarAddress = _avatarAddress, - slotIndex = slotIndex, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = payByCrystal, - useHammerPoint = false, - }; - - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - }); - - var currency = nextState.GetGoldCurrency(); - Assert.Equal(0 * currency, nextState.GetBalance(_agentAddress, currency)); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var equipment = (Equipment)slotState.Result.itemUsable; - if (subRecipeId.HasValue) - { - Assert.True(equipment.optionCountFromCombination > 0); - - if (ncgBalanceExist) - { - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(blockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal(450 * currency, nextState.GetBalance(feeStoreAddress, currency)); - } - - Assert.Equal(mimisbrunnr, equipment.MadeWithMimisbrunnrRecipe); - Assert.Equal( - mimisbrunnr, - equipment.IsMadeWithMimisbrunnrRecipe( - _tableSheets.EquipmentItemRecipeSheet, - _tableSheets.EquipmentItemSubRecipeSheetV2, - _tableSheets.EquipmentItemOptionSheet - ) - ); - - if (mimisbrunnr) - { - Assert.Equal(ElementalType.Fire, equipment.ElementalType); - } - } - else - { - Assert.Equal(0, equipment.optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var mail = nextAvatarState.mailBox.OfType().First(); - - Assert.Equal(equipment, mail.attachment.itemUsable); - Assert.Equal(payByCrystal, !(nextState.GetState(dailyCostAddress) is null)); - Assert.Equal(payByCrystal, !(nextState.GetState(weeklyCostAddress) is null)); - - if (payByCrystal) - { - var dailyCostState = nextState.GetCrystalCostState(dailyCostAddress); - var weeklyCostState = nextState.GetCrystalCostState(weeklyCostAddress); - - Assert.Equal(0 * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - Assert.Equal(1, dailyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, dailyCostState.CRYSTAL); - Assert.Equal(1, weeklyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, weeklyCostState.CRYSTAL); - } - - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, nextState.GetBalance(Addresses.MaterialCost, CrystalCalculator.CRYSTAL)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - })); - } - } - - [Theory] - [InlineData(null, false, true, 1)] - [InlineData(null, false, false, 1)] - [InlineData(typeof(NotEnoughFungibleAssetValueException), true, true, 1)] - [InlineData(null, true, true, 1)] - [InlineData(typeof(ArgumentException), true, false, 1)] - public void ExecuteBySuperCraft( - Type exc, - bool doSuperCraft, - bool useBasicRecipe, - int recipeId) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - var unlockIds = List.Empty.Add(1.Serialize()); - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - state = state.SetState(_agentAddress, _agentState.Serialize()); - _avatarState.worldInformation = new WorldInformation(0, _tableSheets.WorldSheet, 200); - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - int? subRecipeId = useBasicRecipe ? row.SubRecipeIds.First() : row.SubRecipeIds.Skip(1).First(); - if (exc?.FullName?.Contains(nameof(ArgumentException)) ?? false) - { - subRecipeId = row.SubRecipeIds.Last(); - } - - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - var hammerPointAddress = - Addresses.GetHammerPointStateAddress(_avatarAddress, recipeId); - if (doSuperCraft) - { - var hammerPointState = new HammerPointState(hammerPointAddress, recipeId); - var hammerPointSheet = _tableSheets.CrystalHammerPointSheet; - hammerPointState.AddHammerPoint( - hammerPointSheet[recipeId].MaxPoint, - hammerPointSheet); - state = state.SetState(hammerPointAddress, hammerPointState.Serialize()); - if (exc is null) - { - var costCrystal = CrystalCalculator.CRYSTAL * - hammerPointSheet[recipeId].CRYSTAL; - state = state.MintAsset( - context, - _agentAddress, - costCrystal); - } - } - - var action = new CombinationEquipment13 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = false, - useHammerPoint = doSuperCraft, - }; - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - Assert.True(nextState.TryGetState(hammerPointAddress, out List serialized)); - var hammerPointState = - new HammerPointState(hammerPointAddress, serialized); - if (!doSuperCraft) - { - Assert.Equal(useBasicRecipe ? 1 : 2, hammerPointState.HammerPoint); - } - else - { - Assert.Equal(0, hammerPointState.HammerPoint); - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - Assert.NotEmpty(slotState.Result.itemUsable.Skills); - } - } - else - { - Assert.Throws(exc, () => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - }); - } - } - - [Fact] - public void AddAndUnlockOption() - { - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment13.AddAndUnlockOption( - _agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment14Test.cs b/.Lib9c.Tests/Action/CombinationEquipment14Test.cs deleted file mode 100644 index 624e935d08..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment14Test.cs +++ /dev/null @@ -1,532 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData.Crystal; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment14Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - private readonly AgentState _agentState; - private readonly AvatarState _avatarState; - - public CombinationEquipment14Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - _agentState = new AgentState(_agentAddress); - _agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var combinationSlotState = new CombinationSlotState( - _slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - - _initialState = new MockStateDelta() - .SetState(_slotAddress, combinationSlotState.Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - // Tutorial recipe. - [InlineData(null, false, false, true, true, false, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // Migration AvatarState. - [InlineData(null, false, false, true, true, true, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // SubRecipe - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 1, true, false, false, false, false)] - // Mimisbrunnr Equipment. - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, true, true, false, false)] - // Purchase CRYSTAL. - [InlineData(null, true, true, true, true, false, 3, 0, true, 1L, 1, null, false, false, false, true, false)] - // Purchase CRYSTAL with calculate previous cost. - [InlineData(null, true, true, true, true, false, 3, 0, true, 100_800L, 1, null, false, false, true, true, true)] - // Arena round not found - [InlineData(null, false, false, true, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // UnlockEquipmentRecipe not executed. - [InlineData(typeof(FailedLoadStateException), false, true, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // CRYSTAL not paid. - [InlineData(typeof(InvalidRecipeIdException), true, false, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // AgentState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, false, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // AvatarState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, false, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - [InlineData(typeof(FailedLoadStateException), true, true, true, false, true, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // Tutorial not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 1, 0, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, true, false, 3, 5, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState locked. - [InlineData(typeof(CombinationSlotUnlockException), true, true, true, true, false, 3, 0, false, 0L, 1, null, true, false, false, false, false)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 3, 0, true, 0L, 2, null, true, false, false, false, false)] - // Not enough material. - [InlineData(typeof(NotEnoughMaterialException), true, true, true, true, false, 3, 0, true, 0L, 1, null, false, false, false, false, false)] - // Purchase CRYSTAL failed by Mimisbrunnr material. - [InlineData(typeof(ArgumentException), true, true, true, true, false, 11, 0, true, 0L, 2, 3, false, false, true, true, false)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, false, true, false, false)] - public void Execute( - Type exc, - bool unlockIdsExist, - bool crystalUnlock, - bool agentExist, - bool avatarExist, - bool migrationRequired, - int stageId, - int slotIndex, - bool slotUnlock, - long blockIndex, - int recipeId, - int? subRecipeId, - bool enoughMaterial, - bool ncgBalanceExist, - bool mimisbrunnr, - bool payByCrystal, - bool previousCostStateExist - ) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - if (unlockIdsExist) - { - var unlockIds = List.Empty.Add(1.Serialize()); - if (crystalUnlock) - { - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - } - - if (agentExist) - { - state = state.SetState(_agentAddress, _agentState.Serialize()); - - if (avatarExist) - { - _avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - if (enoughMaterial) - { - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (ncgBalanceExist && subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - } - } - - if (migrationRequired) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - } - - if (!slotUnlock) - { - // Lock slot. - state = state.SetState( - _slotAddress, - new CombinationSlotState(_slotAddress, stageId + 1).Serialize() - ); - } - } - } - - int expectedCrystal = 0; - if (payByCrystal) - { - var crystalBalance = 0; - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var costSheet = _tableSheets.CrystalMaterialCostSheet; - crystalBalance += costSheet[row.MaterialId].CRYSTAL * row.MaterialCount; - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - if (costSheet.ContainsKey(materialInfo.Id)) - { - crystalBalance += costSheet[materialInfo.Id].CRYSTAL * row.MaterialCount; - } - } - } - - if (previousCostStateExist) - { - var previousCostAddress = Addresses.GetWeeklyCrystalCostAddress(6); - var previousCostState = new CrystalCostState(previousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL * 2); - var beforePreviousCostAddress = Addresses.GetWeeklyCrystalCostAddress(5); - var beforePreviousCostState = new CrystalCostState(beforePreviousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL); - - state = state - .SetState(previousCostAddress, previousCostState.Serialize()) - .SetState(beforePreviousCostAddress, beforePreviousCostState.Serialize()); - } - - expectedCrystal = crystalBalance; - state = state.MintAsset(context, _agentAddress, expectedCrystal * CrystalCalculator.CRYSTAL); - } - - var dailyCostAddress = - Addresses.GetDailyCrystalCostAddress((int)(blockIndex / CrystalCostState.DailyIntervalIndex)); - var weeklyInterval = _tableSheets.CrystalFluctuationSheet.Values.First(r => - r.Type == CrystalFluctuationSheet.ServiceType.Combination).BlockInterval; - var weeklyCostAddress = Addresses.GetWeeklyCrystalCostAddress((int)(blockIndex / weeklyInterval)); - - Assert.Null(state.GetState(dailyCostAddress)); - Assert.Null(state.GetState(weeklyCostAddress)); - - var action = new CombinationEquipment14 - { - avatarAddress = _avatarAddress, - slotIndex = slotIndex, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = payByCrystal, - useHammerPoint = false, - }; - - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - }); - - var currency = nextState.GetGoldCurrency(); - Assert.Equal(0 * currency, nextState.GetBalance(_agentAddress, currency)); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var equipment = (Equipment)slotState.Result.itemUsable; - if (subRecipeId.HasValue) - { - Assert.True(equipment.optionCountFromCombination > 0); - - if (ncgBalanceExist) - { - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(blockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal(450 * currency, nextState.GetBalance(feeStoreAddress, currency)); - } - - Assert.Equal(mimisbrunnr, equipment.MadeWithMimisbrunnrRecipe); - Assert.Equal( - mimisbrunnr, - equipment.IsMadeWithMimisbrunnrRecipe( - _tableSheets.EquipmentItemRecipeSheet, - _tableSheets.EquipmentItemSubRecipeSheetV2, - _tableSheets.EquipmentItemOptionSheet - ) - ); - - if (mimisbrunnr) - { - Assert.Equal(ElementalType.Fire, equipment.ElementalType); - } - } - else - { - Assert.Equal(0, equipment.optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var mail = nextAvatarState.mailBox.OfType().First(); - - Assert.Equal(equipment, mail.attachment.itemUsable); - Assert.Equal(payByCrystal, !(nextState.GetState(dailyCostAddress) is null)); - Assert.Equal(payByCrystal, !(nextState.GetState(weeklyCostAddress) is null)); - - if (payByCrystal) - { - var dailyCostState = nextState.GetCrystalCostState(dailyCostAddress); - var weeklyCostState = nextState.GetCrystalCostState(weeklyCostAddress); - - Assert.Equal(0 * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - Assert.Equal(1, dailyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, dailyCostState.CRYSTAL); - Assert.Equal(1, weeklyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, weeklyCostState.CRYSTAL); - } - - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, nextState.GetBalance(Addresses.MaterialCost, CrystalCalculator.CRYSTAL)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - })); - } - } - - [Theory] - [InlineData(null, false, true, 1)] - [InlineData(null, false, false, 1)] - [InlineData(typeof(NotEnoughFungibleAssetValueException), true, true, 1)] - [InlineData(null, true, true, 1)] - [InlineData(typeof(ArgumentException), true, false, 1)] - [InlineData(typeof(NotEnoughHammerPointException), true, true, 1)] - public void ExecuteBySuperCraft( - Type exc, - bool doSuperCraft, - bool useBasicRecipe, - int recipeId) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - var unlockIds = List.Empty.Add(1.Serialize()); - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - state = state.SetState(_agentAddress, _agentState.Serialize()); - _avatarState.worldInformation = new WorldInformation(0, _tableSheets.WorldSheet, 200); - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - int? subRecipeId = useBasicRecipe ? row.SubRecipeIds.First() : row.SubRecipeIds.Skip(1).First(); - if (exc?.FullName?.Contains(nameof(ArgumentException)) ?? false) - { - subRecipeId = row.SubRecipeIds.Last(); - } - - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - var hammerPointAddress = - Addresses.GetHammerPointStateAddress(_avatarAddress, recipeId); - if (doSuperCraft) - { - var hammerPointState = new HammerPointState(hammerPointAddress, recipeId); - var hammerPointSheet = _tableSheets.CrystalHammerPointSheet; - hammerPointState.AddHammerPoint( - hammerPointSheet[recipeId].MaxPoint, - hammerPointSheet); - state = state.SetState(hammerPointAddress, hammerPointState.Serialize()); - if (exc is null) - { - var costCrystal = CrystalCalculator.CRYSTAL * - hammerPointSheet[recipeId].CRYSTAL; - state = state.MintAsset( - context, - _agentAddress, - costCrystal); - } - else if (exc.FullName!.Contains(nameof(NotEnoughHammerPointException))) - { - hammerPointState.ResetHammerPoint(); - state = state.SetState(hammerPointAddress, hammerPointState.Serialize()); - } - } - - var action = new CombinationEquipment14 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = false, - useHammerPoint = doSuperCraft, - }; - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - Assert.True(nextState.TryGetState(hammerPointAddress, out List serialized)); - var hammerPointState = - new HammerPointState(hammerPointAddress, serialized); - if (!doSuperCraft) - { - Assert.Equal(useBasicRecipe ? 1 : 2, hammerPointState.HammerPoint); - } - else - { - Assert.Equal(0, hammerPointState.HammerPoint); - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - Assert.NotEmpty(slotState.Result.itemUsable.Skills); - } - } - else - { - Assert.Throws(exc, () => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - }); - } - } - - [Fact] - public void AddAndUnlockOption() - { - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment14.AddAndUnlockOption( - _agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment15Test.cs b/.Lib9c.Tests/Action/CombinationEquipment15Test.cs deleted file mode 100644 index c935d0e7cd..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment15Test.cs +++ /dev/null @@ -1,532 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData.Crystal; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment15Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - private readonly AgentState _agentState; - private readonly AvatarState _avatarState; - - public CombinationEquipment15Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - _agentState = new AgentState(_agentAddress); - _agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var combinationSlotState = new CombinationSlotState( - _slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - - _initialState = new MockStateDelta() - .SetState(_slotAddress, combinationSlotState.Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - // Tutorial recipe. - [InlineData(null, false, false, true, true, false, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // Migration AvatarState. - [InlineData(null, false, false, true, true, true, 3, 0, true, 1L, 1, null, true, false, false, false, false)] - // SubRecipe - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 1, true, false, false, false, false)] - // Mimisbrunnr Equipment. - [InlineData(null, true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, true, true, false, false)] - // Purchase CRYSTAL. - [InlineData(null, true, true, true, true, false, 3, 0, true, 1L, 1, null, false, false, false, true, false)] - // Purchase CRYSTAL with calculate previous cost. - [InlineData(null, true, true, true, true, false, 3, 0, true, 100_800L, 1, null, false, false, true, true, true)] - // Arena round not found - [InlineData(null, false, false, true, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // UnlockEquipmentRecipe not executed. - [InlineData(typeof(FailedLoadStateException), false, true, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // CRYSTAL not paid. - [InlineData(typeof(InvalidRecipeIdException), true, false, true, true, false, 11, 0, true, 0L, 2, 1, true, false, false, false, false)] - // AgentState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, false, true, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // AvatarState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, false, false, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - [InlineData(typeof(FailedLoadStateException), true, true, true, false, true, 3, 0, true, 0L, 1, null, true, false, false, false, false)] - // Tutorial not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 1, 0, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState not exist. - [InlineData(typeof(FailedLoadStateException), true, true, true, true, false, 3, 5, true, 0L, 1, null, true, false, false, false, false)] - // CombinationSlotState locked. - [InlineData(typeof(CombinationSlotUnlockException), true, true, true, true, false, 3, 0, false, 0L, 1, null, true, false, false, false, false)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, true, true, true, false, 3, 0, true, 0L, 2, null, true, false, false, false, false)] - // Not enough material. - [InlineData(typeof(NotEnoughMaterialException), true, true, true, true, false, 3, 0, true, 0L, 1, null, false, false, false, false, false)] - // Purchase CRYSTAL failed by Mimisbrunnr material. - [InlineData(typeof(ArgumentException), true, true, true, true, false, 11, 0, true, 0L, 2, 3, false, false, true, true, false)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, true, true, false, 11, 0, true, 1L, 2, 3, true, false, true, false, false)] - public void Execute( - Type exc, - bool unlockIdsExist, - bool crystalUnlock, - bool agentExist, - bool avatarExist, - bool migrationRequired, - int stageId, - int slotIndex, - bool slotUnlock, - long blockIndex, - int recipeId, - int? subRecipeId, - bool enoughMaterial, - bool ncgBalanceExist, - bool mimisbrunnr, - bool payByCrystal, - bool previousCostStateExist - ) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - if (unlockIdsExist) - { - var unlockIds = List.Empty.Add(1.Serialize()); - if (crystalUnlock) - { - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - } - - if (agentExist) - { - state = state.SetState(_agentAddress, _agentState.Serialize()); - - if (avatarExist) - { - _avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - if (enoughMaterial) - { - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (ncgBalanceExist && subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - } - } - - if (migrationRequired) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - } - - if (!slotUnlock) - { - // Lock slot. - state = state.SetState( - _slotAddress, - new CombinationSlotState(_slotAddress, stageId + 1).Serialize() - ); - } - } - } - - int expectedCrystal = 0; - if (payByCrystal) - { - var crystalBalance = 0; - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var costSheet = _tableSheets.CrystalMaterialCostSheet; - crystalBalance += costSheet[row.MaterialId].CRYSTAL * row.MaterialCount; - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - - foreach (var materialInfo in subRow.Materials) - { - if (costSheet.ContainsKey(materialInfo.Id)) - { - crystalBalance += costSheet[materialInfo.Id].CRYSTAL * row.MaterialCount; - } - } - } - - if (previousCostStateExist) - { - var previousCostAddress = Addresses.GetWeeklyCrystalCostAddress(6); - var previousCostState = new CrystalCostState(previousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL * 2); - var beforePreviousCostAddress = Addresses.GetWeeklyCrystalCostAddress(5); - var beforePreviousCostState = new CrystalCostState(beforePreviousCostAddress, crystalBalance * CrystalCalculator.CRYSTAL); - - state = state - .SetState(previousCostAddress, previousCostState.Serialize()) - .SetState(beforePreviousCostAddress, beforePreviousCostState.Serialize()); - } - - expectedCrystal = crystalBalance; - state = state.MintAsset(context, _agentAddress, expectedCrystal * CrystalCalculator.CRYSTAL); - } - - var dailyCostAddress = - Addresses.GetDailyCrystalCostAddress((int)(blockIndex / CrystalCostState.DailyIntervalIndex)); - var weeklyInterval = _tableSheets.CrystalFluctuationSheet.Values.First(r => - r.Type == CrystalFluctuationSheet.ServiceType.Combination).BlockInterval; - var weeklyCostAddress = Addresses.GetWeeklyCrystalCostAddress((int)(blockIndex / weeklyInterval)); - - Assert.Null(state.GetState(dailyCostAddress)); - Assert.Null(state.GetState(weeklyCostAddress)); - - var action = new CombinationEquipment15 - { - avatarAddress = _avatarAddress, - slotIndex = slotIndex, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = payByCrystal, - useHammerPoint = false, - }; - - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - }); - - var currency = nextState.GetGoldCurrency(); - Assert.Equal(0 * currency, nextState.GetBalance(_agentAddress, currency)); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var equipment = (Equipment)slotState.Result.itemUsable; - if (subRecipeId.HasValue) - { - Assert.True(equipment.optionCountFromCombination > 0); - - if (ncgBalanceExist) - { - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(blockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal(450 * currency, nextState.GetBalance(feeStoreAddress, currency)); - } - - Assert.Equal(mimisbrunnr, equipment.MadeWithMimisbrunnrRecipe); - Assert.Equal( - mimisbrunnr, - equipment.IsMadeWithMimisbrunnrRecipe( - _tableSheets.EquipmentItemRecipeSheet, - _tableSheets.EquipmentItemSubRecipeSheetV2, - _tableSheets.EquipmentItemOptionSheet - ) - ); - - if (mimisbrunnr) - { - Assert.Equal(ElementalType.Fire, equipment.ElementalType); - } - } - else - { - Assert.Equal(0, equipment.optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var mail = nextAvatarState.mailBox.OfType().First(); - - Assert.Equal(equipment, mail.attachment.itemUsable); - Assert.Equal(payByCrystal, !(nextState.GetState(dailyCostAddress) is null)); - Assert.Equal(payByCrystal, !(nextState.GetState(weeklyCostAddress) is null)); - - if (payByCrystal) - { - var dailyCostState = nextState.GetCrystalCostState(dailyCostAddress); - var weeklyCostState = nextState.GetCrystalCostState(weeklyCostAddress); - - Assert.Equal(0 * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - Assert.Equal(1, dailyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, dailyCostState.CRYSTAL); - Assert.Equal(1, weeklyCostState.Count); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, weeklyCostState.CRYSTAL); - } - - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, nextState.GetBalance(Addresses.MaterialCost, CrystalCalculator.CRYSTAL)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - Random = _random, - })); - } - } - - [Theory] - [InlineData(null, false, true, 1)] - [InlineData(null, false, false, 1)] - [InlineData(typeof(NotEnoughFungibleAssetValueException), true, true, 1)] - [InlineData(null, true, true, 1)] - [InlineData(typeof(ArgumentException), true, false, 1)] - [InlineData(typeof(NotEnoughHammerPointException), true, true, 1)] - public void ExecuteBySuperCraft( - Type exc, - bool doSuperCraft, - bool useBasicRecipe, - int recipeId) - { - var context = new ActionContext(); - IAccountStateDelta state = _initialState; - var unlockIds = List.Empty.Add(1.Serialize()); - for (int i = 2; i < recipeId + 1; i++) - { - unlockIds = unlockIds.Add(i.Serialize()); - } - - state = state.SetState(_avatarAddress.Derive("recipe_ids"), unlockIds); - state = state.SetState(_agentAddress, _agentState.Serialize()); - _avatarState.worldInformation = new WorldInformation(0, _tableSheets.WorldSheet, 200); - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, row.MaterialCount); - int? subRecipeId = useBasicRecipe ? row.SubRecipeIds.First() : row.SubRecipeIds.Skip(1).First(); - if (exc?.FullName?.Contains(nameof(ArgumentException)) ?? false) - { - subRecipeId = row.SubRecipeIds.Last(); - } - - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - foreach (var materialInfo in subRow.Materials) - { - var subMaterial = ItemFactory.CreateItem( - _tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, materialInfo.Count); - } - - if (subRow.RequiredGold > 0) - { - state = state.MintAsset( - context, - _agentAddress, - subRow.RequiredGold * state.GetGoldCurrency()); - } - - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = - _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - state = state - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(inventoryAddress, _avatarState.inventory.Serialize()) - .SetState( - worldInformationAddress, - _avatarState.worldInformation.Serialize()) - .SetState(questListAddress, _avatarState.questList.Serialize()); - var hammerPointAddress = - Addresses.GetHammerPointStateAddress(_avatarAddress, recipeId); - if (doSuperCraft) - { - var hammerPointState = new HammerPointState(hammerPointAddress, recipeId); - var hammerPointSheet = _tableSheets.CrystalHammerPointSheet; - hammerPointState.AddHammerPoint( - hammerPointSheet[recipeId].MaxPoint, - hammerPointSheet); - state = state.SetState(hammerPointAddress, hammerPointState.Serialize()); - if (exc is null) - { - var costCrystal = CrystalCalculator.CRYSTAL * - hammerPointSheet[recipeId].CRYSTAL; - state = state.MintAsset( - context, - _agentAddress, - costCrystal); - } - else if (exc.FullName!.Contains(nameof(NotEnoughHammerPointException))) - { - hammerPointState.ResetHammerPoint(); - state = state.SetState(hammerPointAddress, hammerPointState.Serialize()); - } - } - - var action = new CombinationEquipment15 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - payByCrystal = false, - useHammerPoint = doSuperCraft, - }; - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - Assert.True(nextState.TryGetState(hammerPointAddress, out List serialized)); - var hammerPointState = - new HammerPointState(hammerPointAddress, serialized); - if (!doSuperCraft) - { - Assert.Equal(useBasicRecipe ? 1 : 2, hammerPointState.HammerPoint); - } - else - { - Assert.Equal(0, hammerPointState.HammerPoint); - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - Assert.NotEmpty(slotState.Result.itemUsable.Skills); - } - } - else - { - Assert.Throws(exc, () => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - }); - } - } - - [Fact] - public void AddAndUnlockOption() - { - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment15.AddAndUnlockOption( - _agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment2Test.cs b/.Lib9c.Tests/Action/CombinationEquipment2Test.cs deleted file mode 100644 index e5639c619f..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment2Test.cs +++ /dev/null @@ -1,145 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class CombinationEquipment2Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment2Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - _slotAddress, - new CombinationSlotState( - _slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000); - - foreach (var (key, value) in _sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.EquipmentItemRecipeSheet.Values.First(); - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - const int requiredStage = GameConfig.RequireClearedStageLevel.CombinationEquipmentAction; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment2() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment3Test.cs b/.Lib9c.Tests/Action/CombinationEquipment3Test.cs deleted file mode 100644 index f2f48c4ce3..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment3Test.cs +++ /dev/null @@ -1,254 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class CombinationEquipment3Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, _agentAddress, gold.Currency * 300); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.EquipmentItemRecipeSheet[109]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[255].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem2(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 21; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipmentRow = _tableSheets.EquipmentItemSheet[row.ResultEquipmentId]; - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - subRecipeId = 255, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment3() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 255, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.True(slotState.Result.itemUsable.GetOptionCount() > 0); - Assert.True(slotState.Result.itemUsable.GetOptionCount() <= 2); - - var goldCurrencyState = nextState.GetGoldCurrency(); - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - Assert.Equal(300 * currency, blackSmithGold); - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(currency * 0, agentGold); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var row = _tableSheets.EquipmentItemRecipeSheet[2]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[3].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem2(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 11; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment3() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 3, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void SelectOption() - { - var options = new Dictionary(); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheet[255]; - var equipment = - (Necklace)ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet[10411000], default, 0); - var i = 0; - while (i < 10000) - { - var ids = CombinationEquipment3.SelectOption( - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet, - subRecipe, - _random, - equipment - ); - - foreach (var id in ids) - { - if (options.ContainsKey(id)) - { - options[id] += 1; - } - else - { - options[id] = 1; - } - } - - i++; - } - - var optionIds = options - .OrderByDescending(r => r.Value) - .Select(r => r.Key) - .ToArray(); - Assert.Equal(new[] { 932, 933, 934, 935 }, optionIds); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment4Test.cs b/.Lib9c.Tests/Action/CombinationEquipment4Test.cs deleted file mode 100644 index b5a4b37f02..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment4Test.cs +++ /dev/null @@ -1,253 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class CombinationEquipment4Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, _agentAddress, gold.Currency * 300); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.EquipmentItemRecipeSheet[109]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[255].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem2(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 21; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipmentRow = _tableSheets.EquipmentItemSheet[row.ResultEquipmentId]; - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - subRecipeId = 255, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment4() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 255, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(2, slotState.Result.itemUsable.GetOptionCount()); - - var goldCurrencyState = nextState.GetGoldCurrency(); - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - Assert.Equal(300 * currency, blackSmithGold); - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(currency * 0, agentGold); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var row = _tableSheets.EquipmentItemRecipeSheet[2]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[3].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem2(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 21; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment4() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 3, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void SelectOption() - { - var options = new Dictionary(); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheet[255]; - var equipment = - (Necklace)ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet[10411000], default, 0); - var i = 0; - while (i < 10000) - { - var ids = CombinationEquipment4.SelectOption( - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet, - subRecipe, - _random, - equipment - ); - - foreach (var id in ids) - { - if (options.ContainsKey(id)) - { - options[id] += 1; - } - else - { - options[id] = 1; - } - } - - i++; - } - - var optionIds = options - .OrderByDescending(r => r.Value) - .Select(r => r.Key) - .ToArray(); - Assert.Equal(new[] { 932, 933, 934, 935 }, optionIds); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment5Test.cs b/.Lib9c.Tests/Action/CombinationEquipment5Test.cs deleted file mode 100644 index 347e8caacf..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment5Test.cs +++ /dev/null @@ -1,250 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class CombinationEquipment5Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, _agentAddress, gold.Currency * 300); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute() - { - var row = _tableSheets.EquipmentItemRecipeSheet[109]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[255].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem2(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 21; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipmentRow = _tableSheets.EquipmentItemSheet[row.ResultEquipmentId]; - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - subRecipeId = 255, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment5() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 255, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(2, slotState.Result.itemUsable.GetOptionCount()); - - var goldCurrencyState = nextState.GetGoldCurrency(); - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - Assert.Equal(300 * currency, blackSmithGold); - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(currency * 0, agentGold); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var row = _tableSheets.EquipmentItemRecipeSheet[2]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem2(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[3].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem2(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 11; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment5() - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 3, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void SelectOption() - { - var options = new Dictionary(); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheet[255]; - var equipment = - (Necklace)ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet[10411000], default, 0); - for (int i = 0; i < 10000; i++) - { - var ids = CombinationEquipment4.SelectOption( - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet, - subRecipe, - _random, - equipment - ); - - foreach (var id in ids) - { - if (options.ContainsKey(id)) - { - options[id] += 1; - } - else - { - options[id] = 1; - } - } - } - - var optionIds = options - .OrderByDescending(r => r.Value) - .Select(r => r.Key) - .ToArray(); - Assert.Equal(new[] { 932, 933, 934, 935 }, optionIds); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment6Test.cs b/.Lib9c.Tests/Action/CombinationEquipment6Test.cs deleted file mode 100644 index b05ee0e4fb..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment6Test.cs +++ /dev/null @@ -1,310 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment6Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, _agentAddress, gold.Currency * 300); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - var row = _tableSheets.EquipmentItemRecipeSheet[109]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[255].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 21; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipmentRow = _tableSheets.EquipmentItemSheet[row.ResultEquipmentId]; - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - subRecipeId = 255, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update(mail); - } - - if (backward) - { - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - _initialState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), _avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), _avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), _avatarState.questList.Serialize()) - .SetState(_avatarAddress, _avatarState.SerializeV2()); - } - - var action = new CombinationEquipment6 - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 255, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(2, slotState.Result.itemUsable.GetOptionCount()); - - var goldCurrencyState = nextState.GetGoldCurrency(); - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - Assert.Equal(300 * currency, blackSmithGold); - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(currency * 0, agentGold); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var row = _tableSheets.EquipmentItemRecipeSheet[2]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[3].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 11; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment6 - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 3, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - var action = new CombinationEquipment6 - { - AvatarAddress = _avatarAddress, - RecipeId = 1, - SlotIndex = 0, - SubRecipeId = 255, - }; - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.Blacksmith, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SelectOption() - { - var options = new Dictionary(); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheet[255]; - var equipment = - (Necklace)ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet[10411000], default, 0); - var i = 0; - while (i < 10000) - { - var ids = CombinationEquipment4.SelectOption( - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet, - subRecipe, - _random, - equipment - ); - - foreach (var id in ids) - { - if (options.ContainsKey(id)) - { - options[id] += 1; - } - else - { - options[id] = 1; - } - } - - i++; - } - - var optionIds = options - .OrderByDescending(r => r.Value) - .Select(r => r.Key) - .ToArray(); - Assert.Equal(new[] { 932, 933, 934, 935 }, optionIds); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment7Test.cs b/.Lib9c.Tests/Action/CombinationEquipment7Test.cs deleted file mode 100644 index e16bc3acdc..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment7Test.cs +++ /dev/null @@ -1,309 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment7Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly AvatarState _avatarState; - private IAccountStateDelta _initialState; - - public CombinationEquipment7Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction - ).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, _agentAddress, gold.Currency * 300); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - var row = _tableSheets.EquipmentItemRecipeSheet[109]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[255].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 21; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - var equipmentRow = _tableSheets.EquipmentItemSheet[row.ResultEquipmentId]; - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - subRecipeId = 255, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update(mail); - } - - IAccountStateDelta previousState; - if (backward) - { - previousState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), _avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), _avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), _avatarState.questList.Serialize()) - .SetState(_avatarAddress, _avatarState.SerializeV2()); - } - - var action = new CombinationEquipment7 - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 255, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - Assert.Equal(2, slotState.Result.itemUsable.GetOptionCount()); - - var goldCurrencyState = nextState.GetGoldCurrency(); - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - Assert.Equal(300 * currency, blackSmithGold); - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(currency * 0, agentGold); - } - - [Fact] - public void ExecuteThrowInsufficientBalanceException() - { - var row = _tableSheets.EquipmentItemRecipeSheet[2]; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - _avatarState.inventory.AddItem(material, count: row.MaterialCount); - - foreach (var materialInfo in _tableSheets.EquipmentItemSubRecipeSheet[3].Materials) - { - var subMaterial = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - _avatarState.inventory.AddItem(subMaterial, count: materialInfo.Count); - } - - const int requiredStage = 11; - for (var i = 1; i < requiredStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - 1, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new CombinationEquipment7 - { - AvatarAddress = _avatarAddress, - RecipeId = row.Id, - SlotIndex = 0, - SubRecipeId = 3, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - var action = new CombinationEquipment7 - { - AvatarAddress = _avatarAddress, - RecipeId = 1, - SlotIndex = 0, - SubRecipeId = 255, - }; - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.Blacksmith, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SelectOption() - { - var options = new Dictionary(); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheet[255]; - var equipment = - (Necklace)ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet[10411000], default, 0); - var i = 0; - while (i < 10000) - { - var ids = CombinationEquipment4.SelectOption( - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet, - subRecipe, - _random, - equipment - ); - - foreach (var id in ids) - { - if (options.ContainsKey(id)) - { - options[id] += 1; - } - else - { - options[id] = 1; - } - } - - i++; - } - - var optionIds = options - .OrderByDescending(r => r.Value) - .Select(r => r.Key) - .ToArray(); - Assert.Equal(new[] { 932, 933, 934, 935 }, optionIds); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment8Test.cs b/.Lib9c.Tests/Action/CombinationEquipment8Test.cs deleted file mode 100644 index c72ec39ff0..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment8Test.cs +++ /dev/null @@ -1,286 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment8Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - - public CombinationEquipment8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(false, 1, null)] - [InlineData(false, 145, 341)] - [InlineData(false, 145, 342)] - [InlineData(true, 1, null)] - [InlineData(true, 145, 341)] - [InlineData(true, 145, 342)] - public void Execute_Success(bool backward, int recipeId, int? subRecipeId) => - Execute(backward, recipeId, subRecipeId, 10000); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_InsufficientBalanceException(bool backward) - { - var subRecipeId = _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList - .First(e => e.RequiredGold > 0) - .Id; - var recipeId = _tableSheets.EquipmentItemRecipeSheet.OrderedList - .First(e => e.SubRecipeIds.Contains(subRecipeId)) - .Id; - - Assert.Throws(() => Execute( - backward, recipeId, subRecipeId, 0)); - } - - [Fact] - public void Rehearsal() - { - var action = new CombinationEquipment8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = 1, - subRecipeId = 255, - }; - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.Blacksmith, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void AddAndUnlockOption() - { - var agentState = _initialState.GetAgentState(_agentAddress); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment8.AddAndUnlockOption( - agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - - private void Execute(bool backward, int recipeId, int? subRecipeId, int mintNCG) - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var requiredStage = row.UnlockStage; - var costActionPoint = row.RequiredActionPoint; - var costNCG = row.RequiredGold * currency; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var previousActionPoint = avatarState.actionPoint; - var previousResultEquipmentCount = - avatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId); - var previousMailCount = avatarState.mailBox.Count; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - requiredStage); - - avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - costActionPoint += subRow.RequiredActionPoint; - costNCG += subRow.RequiredGold * currency; - - foreach (var materialInfo in subRow.Materials) - { - material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - } - - var context = new ActionContext(); - IAccountStateDelta previousState; - if (backward) - { - previousState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - if (mintNCG > 0) - { - previousState = previousState.MintAsset(context, _agentAddress, mintNCG * currency); - } - - var goldCurrencyState = previousState.GetGoldCurrency(); - var previousNCG = previousState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(mintNCG * currency, previousNCG); - - var action = new CombinationEquipment8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - if (subRecipeId.HasValue) - { - Assert.True(((Equipment)slotState.Result.itemUsable).optionCountFromCombination > 0); - } - else - { - Assert.Equal(0, ((Equipment)slotState.Result.itemUsable).optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(previousActionPoint - costActionPoint, nextAvatarState.actionPoint); - Assert.Equal(previousMailCount + 1, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - Assert.Equal( - previousResultEquipmentCount + 1, - nextAvatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId)); - - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(previousNCG - costNCG, agentGold); - - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); - Assert.Equal(costNCG, blackSmithGold); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment9Test.cs b/.Lib9c.Tests/Action/CombinationEquipment9Test.cs deleted file mode 100644 index 019555514b..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment9Test.cs +++ /dev/null @@ -1,286 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class CombinationEquipment9Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccountStateDelta _initialState; - - public CombinationEquipment9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - - var gameConfigState = new GameConfigState(); - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(false, 1, null)] - [InlineData(false, 145, 341)] - [InlineData(false, 145, 342)] - [InlineData(true, 1, null)] - [InlineData(true, 145, 341)] - [InlineData(true, 145, 342)] - public void Execute_Success(bool backward, int recipeId, int? subRecipeId) => - Execute(backward, recipeId, subRecipeId, 10000); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_InsufficientBalanceException(bool backward) - { - var subRecipeId = _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList - .First(e => e.RequiredGold > 0) - .Id; - var recipeId = _tableSheets.EquipmentItemRecipeSheet.OrderedList - .First(e => e.SubRecipeIds.Contains(subRecipeId)) - .Id; - - Assert.Throws(() => Execute( - backward, recipeId, subRecipeId, 0)); - } - - [Fact] - public void Rehearsal() - { - var action = new CombinationEquipment9 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = 1, - subRecipeId = 255, - }; - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.Blacksmith, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void AddAndUnlockOption() - { - var agentState = _initialState.GetAgentState(_agentAddress); - var subRecipe = _tableSheets.EquipmentItemSubRecipeSheetV2.Last; - Assert.NotNull(subRecipe); - var equipment = (Necklace)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet[10411000], - Guid.NewGuid(), - default); - Assert.Equal(0, equipment.optionCountFromCombination); - CombinationEquipment9.AddAndUnlockOption( - agentState, - equipment, - _random, - subRecipe, - _tableSheets.EquipmentItemOptionSheet, - _tableSheets.SkillSheet - ); - Assert.True(equipment.optionCountFromCombination > 0); - } - - private void Execute(bool backward, int recipeId, int? subRecipeId, int mintNCG) - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var row = _tableSheets.EquipmentItemRecipeSheet[recipeId]; - var requiredStage = row.UnlockStage; - var costActionPoint = row.RequiredActionPoint; - var costNCG = row.RequiredGold * currency; - var materialRow = _tableSheets.MaterialItemSheet[row.MaterialId]; - var material = ItemFactory.CreateItem(materialRow, _random); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var previousActionPoint = avatarState.actionPoint; - var previousResultEquipmentCount = - avatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId); - var previousMailCount = avatarState.mailBox.Count; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - requiredStage); - - avatarState.inventory.AddItem(material, row.MaterialCount); - - if (subRecipeId.HasValue) - { - var subRow = _tableSheets.EquipmentItemSubRecipeSheetV2[subRecipeId.Value]; - costActionPoint += subRow.RequiredActionPoint; - costNCG += subRow.RequiredGold * currency; - - foreach (var materialInfo in subRow.Materials) - { - material = ItemFactory.CreateItem(_tableSheets.MaterialItemSheet[materialInfo.Id], _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - } - - var context = new ActionContext(); - IAccountStateDelta previousState; - if (backward) - { - previousState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - if (mintNCG > 0) - { - previousState = previousState.MintAsset(context, _agentAddress, mintNCG * currency); - } - - var goldCurrencyState = previousState.GetGoldCurrency(); - var previousNCG = previousState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(mintNCG * currency, previousNCG); - - var action = new CombinationEquipment9 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - if (subRecipeId.HasValue) - { - Assert.True(((Equipment)slotState.Result.itemUsable).optionCountFromCombination > 0); - } - else - { - Assert.Equal(0, ((Equipment)slotState.Result.itemUsable).optionCountFromCombination); - } - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(previousActionPoint - costActionPoint, nextAvatarState.actionPoint); - Assert.Equal(previousMailCount + 1, nextAvatarState.mailBox.Count); - Assert.IsType(nextAvatarState.mailBox.First()); - Assert.Equal( - previousResultEquipmentCount + 1, - nextAvatarState.inventory.Equipments.Count(e => e.Id == row.ResultEquipmentId)); - - var agentGold = nextState.GetBalance(_agentAddress, goldCurrencyState); - Assert.Equal(previousNCG - costNCG, agentGold); - - var blackSmithGold = nextState.GetBalance(Addresses.Blacksmith, goldCurrencyState); - Assert.Equal(costNCG, blackSmithGold); - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatar2Test.cs b/.Lib9c.Tests/Action/CreateAvatar2Test.cs deleted file mode 100644 index e782aef08f..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar2Test.cs +++ /dev/null @@ -1,326 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class CreateAvatar2Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar2Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Fact] - public void Execute() - { - var action = new CreateAvatar2() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var ranking = new RankingState0(); - for (var i = 0; i < RankingState0.RankingMapCapacity; i++) - { - ranking.RankingMap[RankingState0.Derive(i)] = new HashSet
().ToImmutableHashSet(); - } - - var sheets = TableSheetsImporter.ImportSheets(); - var context = new ActionContext(); - var state = new MockStateDelta() - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .SetState( - Addresses.GoldDistribution, - GoldDistributionTest.Fixture.Select(v => v.Serialize()).Serialize() - ) - .SetState( - Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ) - .SetState(Addresses.Ranking, ranking.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000); - - foreach (var (key, value) in sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }); - - Assert.Equal( - 0, - nextState.GetBalance(default, gold.Currency).MajorUnit - ); - - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - Assert.True(nextState.TryGetAgentAvatarStates( - default, - avatarAddress, - out var agentState, - out var nextAvatarState) - ); - Assert.True(agentState.avatarAddresses.Any()); - Assert.Equal("test", nextAvatarState.name); - Assert.Equal(avatarAddress, nextState.GetRankingState().RankingMap[nextAvatarState.RankingMapAddress].First()); - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar2() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new MockStateDelta(); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - - var avatarState = new AvatarState( - avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var action = new CreateAvatar2() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new MockStateDelta().SetState(avatarAddress, avatarState.Serialize()); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public void ExecuteThrowAvatarIndexOutOfRangeException(int index) - { - var agentState = new AgentState(_agentAddress); - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar2() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void ExecuteThrowAvatarIndexAlreadyUsedException(int index) - { - var agentState = new AgentState(_agentAddress); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - agentState.avatarAddresses[index] = avatarAddress; - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar2() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void Rehearsal(int index) - { - var agentAddress = default(Address); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - index - ) - ); - - var action = new CreateAvatar2() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - Addresses.GoldCurrency, - Addresses.Ranking, - }; - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - updatedAddresses.Add(slotAddress); - } - - var state = new MockStateDelta() - .SetState(Addresses.Ranking, new RankingState0().Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal( - updatedAddresses.ToImmutableHashSet(), - nextState.Delta.UpdatedAddresses - ); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar2() - { - index = 2, - hair = 1, - ear = 4, - lens = 5, - tail = 7, - name = "test", - }; - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (CreateAvatar2)formatter.Deserialize(ms); - - Assert.Equal(2, deserialized.index); - Assert.Equal(1, deserialized.hair); - Assert.Equal(4, deserialized.ear); - Assert.Equal(5, deserialized.lens); - Assert.Equal(7, deserialized.tail); - Assert.Equal("test", deserialized.name); - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatar3Test.cs b/.Lib9c.Tests/Action/CreateAvatar3Test.cs deleted file mode 100644 index b14e79446b..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar3Test.cs +++ /dev/null @@ -1,331 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class CreateAvatar3Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar3Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Fact] - public void Execute() - { - var action = new CreateAvatar3() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var ranking = new RankingState0(); - for (var i = 0; i < RankingState0.RankingMapCapacity; i++) - { - ranking.RankingMap[RankingState0.Derive(i)] = new HashSet
().ToImmutableHashSet(); - } - - var sheets = TableSheetsImporter.ImportSheets(); - var context = new ActionContext(); - var state = new MockStateDelta() - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .SetState( - Addresses.GoldDistribution, - GoldDistributionTest.Fixture.Select(v => v.Serialize()).Serialize() - ) - .SetState( - Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ) - .SetState(Addresses.Ranking, ranking.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000); - - foreach (var (key, value) in sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }); - - Assert.Equal( - 0, - nextState.GetBalance(default, gold.Currency).MajorUnit - ); - - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar3.DeriveFormat, - 0 - ) - ); - Assert.True(nextState.TryGetAgentAvatarStatesV2( - default, - avatarAddress, - out var agentState, - out var nextAvatarState, - out _) - ); - Assert.True(agentState.avatarAddresses.Any()); - Assert.Equal("test", nextAvatarState.name); - Assert.Equal(avatarAddress, nextState.GetRankingState().RankingMap[nextAvatarState.RankingMapAddress].First()); - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar3() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new MockStateDelta(); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar3.DeriveFormat, - 0 - ) - ); - - var avatarState = new AvatarState( - avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var action = new CreateAvatar3() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new MockStateDelta().SetState(avatarAddress, avatarState.Serialize()); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public void ExecuteThrowAvatarIndexOutOfRangeException(int index) - { - var agentState = new AgentState(_agentAddress); - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar3() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void ExecuteThrowAvatarIndexAlreadyUsedException(int index) - { - var agentState = new AgentState(_agentAddress); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar3.DeriveFormat, - 0 - ) - ); - agentState.avatarAddresses[index] = avatarAddress; - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar3() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void Rehearsal(int index) - { - var agentAddress = default(Address); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar3.DeriveFormat, - index - ) - ); - - var action = new CreateAvatar3() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - Addresses.GoldCurrency, - Addresses.Ranking, - avatarAddress.Derive(LegacyInventoryKey), - avatarAddress.Derive(LegacyQuestListKey), - avatarAddress.Derive(LegacyWorldInformationKey), - }; - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - updatedAddresses.Add(slotAddress); - } - - var state = new MockStateDelta() - .SetState(Addresses.Ranking, new RankingState0().Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal( - updatedAddresses.ToImmutableHashSet(), - nextState.Delta.UpdatedAddresses - ); - } - - [Fact] - public void Serialize_With_DotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar3() - { - index = 2, - hair = 1, - ear = 4, - lens = 5, - tail = 7, - name = "test", - }; - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (CreateAvatar3)formatter.Deserialize(ms); - - Assert.Equal(2, deserialized.index); - Assert.Equal(1, deserialized.hair); - Assert.Equal(4, deserialized.ear); - Assert.Equal(5, deserialized.lens); - Assert.Equal(7, deserialized.tail); - Assert.Equal("test", deserialized.name); - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatar6Test.cs b/.Lib9c.Tests/Action/CreateAvatar6Test.cs deleted file mode 100644 index ad4dbd024d..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar6Test.cs +++ /dev/null @@ -1,331 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class CreateAvatar6Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar6Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Fact] - public void Execute() - { - var action = new CreateAvatar6() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var ranking = new RankingState0(); - for (var i = 0; i < RankingState0.RankingMapCapacity; i++) - { - ranking.RankingMap[RankingState0.Derive(i)] = new HashSet
().ToImmutableHashSet(); - } - - var sheets = TableSheetsImporter.ImportSheets(); - var context = new ActionContext(); - var state = new MockStateDelta() - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .SetState( - Addresses.GoldDistribution, - GoldDistributionTest.Fixture.Select(v => v.Serialize()).Serialize() - ) - .SetState( - Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ) - .SetState(Addresses.Ranking, ranking.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000); - - foreach (var (key, value) in sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }); - - Assert.Equal( - 0, - nextState.GetBalance(default, gold.Currency).MajorUnit - ); - - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - Assert.True(nextState.TryGetAgentAvatarStatesV2( - default, - avatarAddress, - out var agentState, - out var nextAvatarState, - out _) - ); - Assert.True(agentState.avatarAddresses.Any()); - Assert.Equal("test", nextAvatarState.name); - Assert.Equal(avatarAddress, nextState.GetRankingState().RankingMap[nextAvatarState.RankingMapAddress].First()); - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar6() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new MockStateDelta(); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - - var avatarState = new AvatarState( - avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var action = new CreateAvatar6() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new MockStateDelta().SetState(avatarAddress, avatarState.Serialize()); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public void ExecuteThrowAvatarIndexOutOfRangeException(int index) - { - var agentState = new AgentState(_agentAddress); - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar6() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void ExecuteThrowAvatarIndexAlreadyUsedException(int index) - { - var agentState = new AgentState(_agentAddress); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - agentState.avatarAddresses[index] = avatarAddress; - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar6() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void Rehearsal(int index) - { - var agentAddress = default(Address); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - index - ) - ); - - var action = new CreateAvatar6() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - Addresses.GoldCurrency, - Addresses.Ranking, - avatarAddress.Derive(LegacyInventoryKey), - avatarAddress.Derive(LegacyQuestListKey), - avatarAddress.Derive(LegacyWorldInformationKey), - }; - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - updatedAddresses.Add(slotAddress); - } - - var state = new MockStateDelta() - .SetState(Addresses.Ranking, new RankingState0().Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal( - updatedAddresses.ToImmutableHashSet(), - nextState.Delta.UpdatedAddresses - ); - } - - [Fact] - public void Serialize_With_DotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar6() - { - index = 2, - hair = 1, - ear = 4, - lens = 5, - tail = 7, - name = "test", - }; - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (CreateAvatar6)formatter.Deserialize(ms); - - Assert.Equal(2, deserialized.index); - Assert.Equal(1, deserialized.hair); - Assert.Equal(4, deserialized.ear); - Assert.Equal(5, deserialized.lens); - Assert.Equal(7, deserialized.tail); - Assert.Equal("test", deserialized.name); - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatar7Test.cs b/.Lib9c.Tests/Action/CreateAvatar7Test.cs deleted file mode 100644 index d5e47bd077..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar7Test.cs +++ /dev/null @@ -1,308 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class CreateAvatar7Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar7Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Fact] - public void Execute() - { - var action = new CreateAvatar7() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var sheets = TableSheetsImporter.ImportSheets(); - var state = new MockStateDelta() - .SetState( - Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ); - - foreach (var (key, value) in sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }); - - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - Assert.True(nextState.TryGetAgentAvatarStatesV2( - default, - avatarAddress, - out var agentState, - out var nextAvatarState, - out _) - ); - Assert.True(agentState.avatarAddresses.Any()); - Assert.Equal("test", nextAvatarState.name); - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar7() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new MockStateDelta(); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - - var avatarState = new AvatarState( - avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var action = new CreateAvatar7() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new MockStateDelta().SetState(avatarAddress, avatarState.Serialize()); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public void ExecuteThrowAvatarIndexOutOfRangeException(int index) - { - var agentState = new AgentState(_agentAddress); - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar7() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void ExecuteThrowAvatarIndexAlreadyUsedException(int index) - { - var agentState = new AgentState(_agentAddress); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - agentState.avatarAddresses[index] = avatarAddress; - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar7() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void Rehearsal(int index) - { - var agentAddress = default(Address); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - index - ) - ); - - var action = new CreateAvatar7() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - avatarAddress.Derive(LegacyInventoryKey), - avatarAddress.Derive(LegacyQuestListKey), - avatarAddress.Derive(LegacyWorldInformationKey), - }; - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - updatedAddresses.Add(slotAddress); - } - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal( - updatedAddresses.ToImmutableHashSet(), - nextState.Delta.UpdatedAddresses - ); - } - - [Fact] - public void Serialize_With_DotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar7() - { - index = 2, - hair = 1, - ear = 4, - lens = 5, - tail = 7, - name = "test", - }; - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (CreateAvatar7)formatter.Deserialize(ms); - - Assert.Equal(2, deserialized.index); - Assert.Equal(1, deserialized.hair); - Assert.Equal(4, deserialized.ear); - Assert.Equal(5, deserialized.lens); - Assert.Equal(7, deserialized.tail); - Assert.Equal("test", deserialized.name); - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatar8Test.cs b/.Lib9c.Tests/Action/CreateAvatar8Test.cs deleted file mode 100644 index 2ed1aa2140..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar8Test.cs +++ /dev/null @@ -1,307 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class CreateAvatar8Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar8Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Fact] - public void Execute() - { - var action = new CreateAvatar8() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var sheets = TableSheetsImporter.ImportSheets(); - var state = new MockStateDelta() - .SetState( - Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ); - - foreach (var (key, value) in sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - Assert.Equal(0 * CrystalCalculator.CRYSTAL, state.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }); - - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - Assert.True(nextState.TryGetAgentAvatarStatesV2( - default, - avatarAddress, - out var agentState, - out var nextAvatarState, - out _) - ); - Assert.True(agentState.avatarAddresses.Any()); - Assert.Equal("test", nextAvatarState.name); - Assert.Equal(50 * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar8() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new MockStateDelta(); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - - var avatarState = new AvatarState( - avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var action = new CreateAvatar8() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new MockStateDelta().SetState(avatarAddress, avatarState.Serialize()); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(-1)] - [InlineData(3)] - public void ExecuteThrowAvatarIndexOutOfRangeException(int index) - { - var agentState = new AgentState(_agentAddress); - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar8() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void ExecuteThrowAvatarIndexAlreadyUsedException(int index) - { - var agentState = new AgentState(_agentAddress); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - 0 - ) - ); - agentState.avatarAddresses[index] = avatarAddress; - var state = new MockStateDelta().SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar8() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(2)] - public void Rehearsal(int index) - { - var agentAddress = default(Address); - var avatarAddress = _agentAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, - index - ) - ); - - var action = new CreateAvatar8() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - avatarAddress.Derive(LegacyInventoryKey), - avatarAddress.Derive(LegacyQuestListKey), - avatarAddress.Derive(LegacyWorldInformationKey), - }; - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - updatedAddresses.Add(slotAddress); - } - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal( - updatedAddresses.ToImmutableHashSet(), - nextState.Delta.UpdatedAddresses - ); - } - - [Fact] - public void Serialize_With_DotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar8() - { - index = 2, - hair = 1, - ear = 4, - lens = 5, - tail = 7, - name = "test", - }; - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (CreateAvatar8)formatter.Deserialize(ms); - - Assert.Equal(2, deserialized.index); - Assert.Equal(1, deserialized.hair); - Assert.Equal(4, deserialized.ear); - Assert.Equal(5, deserialized.lens); - Assert.Equal(7, deserialized.tail); - Assert.Equal("test", deserialized.name); - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatarTest.cs b/.Lib9c.Tests/Action/CreateAvatarTest.cs index 10a208b807..cefb648bd7 100644 --- a/.Lib9c.Tests/Action/CreateAvatarTest.cs +++ b/.Lib9c.Tests/Action/CreateAvatarTest.cs @@ -67,7 +67,7 @@ public void Execute(long blockIndex, int expected) var avatarAddress = _agentAddress.Derive( string.Format( CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, + CreateAvatar.DeriveFormat, 0 ) ); @@ -117,7 +117,7 @@ public void ExecuteThrowInvalidAddressException() var avatarAddress = _agentAddress.Derive( string.Format( CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, + CreateAvatar.DeriveFormat, 0 ) ); @@ -188,7 +188,7 @@ public void ExecuteThrowAvatarIndexAlreadyUsedException(int index) var avatarAddress = _agentAddress.Derive( string.Format( CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, + CreateAvatar.DeriveFormat, 0 ) ); @@ -224,7 +224,7 @@ public void Rehearsal(int index) var avatarAddress = _agentAddress.Derive( string.Format( CultureInfo.InvariantCulture, - CreateAvatar2.DeriveFormat, + CreateAvatar.DeriveFormat, index ) ); diff --git a/.Lib9c.Tests/Action/DailyReward0Test.cs b/.Lib9c.Tests/Action/DailyReward0Test.cs deleted file mode 100644 index 1008aab760..0000000000 --- a/.Lib9c.Tests/Action/DailyReward0Test.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class DailyReward0Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public DailyReward0Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - actionPoint = 0, - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState().Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var dailyRewardAction = new DailyReward0 - { - avatarAddress = _avatarAddress, - }; - var nextState = dailyRewardAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Rehearsal = false, - Signer = _agentAddress, - }); - - var gameConfigState = nextState.GetGameConfigState(); - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(gameConfigState.ActionPointMax, nextAvatarState.actionPoint); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new DailyReward0 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward3Test.cs b/.Lib9c.Tests/Action/DailyReward3Test.cs deleted file mode 100644 index 55e3f1e821..0000000000 --- a/.Lib9c.Tests/Action/DailyReward3Test.cs +++ /dev/null @@ -1,107 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class DailyReward3Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public DailyReward3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - actionPoint = 0, - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState().Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var dailyRewardAction = new DailyReward3 - { - avatarAddress = _avatarAddress, - }; - var nextState = dailyRewardAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var gameConfigState = nextState.GetGameConfigState(); - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(gameConfigState.ActionPointMax, nextAvatarState.actionPoint); - Assert.Single(nextAvatarState.mailBox); - var mail = nextAvatarState.mailBox.First(); - var rewardMail = mail as DailyRewardMail; - Assert.NotNull(rewardMail); - var rewardResult = rewardMail.attachment as DailyReward2.DailyRewardResult; - Assert.NotNull(rewardResult); - Assert.Single(rewardResult.materials); - var material = rewardResult.materials.First(); - Assert.Equal(400000, material.Key.Id); - Assert.Equal(10, material.Value); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new DailyReward3 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward4Test.cs b/.Lib9c.Tests/Action/DailyReward4Test.cs deleted file mode 100644 index 7775f782b6..0000000000 --- a/.Lib9c.Tests/Action/DailyReward4Test.cs +++ /dev/null @@ -1,150 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class DailyReward4Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccountStateDelta _initialState; - - public DailyReward4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - actionPoint = 0, - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState().Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - var dailyRewardAction = new DailyReward4 - { - avatarAddress = _avatarAddress, - }; - if (!backward) - { - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - _initialState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var nextState = dailyRewardAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var gameConfigState = nextState.GetGameConfigState(); - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(gameConfigState.ActionPointMax, nextAvatarState.actionPoint); - Assert.Single(nextAvatarState.mailBox); - var mail = nextAvatarState.mailBox.First(); - var rewardMail = mail as DailyRewardMail; - Assert.NotNull(rewardMail); - var rewardResult = rewardMail.attachment as DailyReward2.DailyRewardResult; - Assert.NotNull(rewardResult); - Assert.Single(rewardResult.materials); - var material = rewardResult.materials.First(); - Assert.Equal(400000, material.Key.Id); - Assert.Equal(10, material.Value); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new DailyReward4 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Rehearsal() - { - var action = new DailyReward4 - { - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Rehearsal = true, - Signer = _agentAddress, - }); - - var updatedAddresses = new List
() - { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward5Test.cs b/.Lib9c.Tests/Action/DailyReward5Test.cs deleted file mode 100644 index 3d467c5588..0000000000 --- a/.Lib9c.Tests/Action/DailyReward5Test.cs +++ /dev/null @@ -1,172 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class DailyReward5Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly IAccountStateDelta _initialState; - - public DailyReward5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - var gameConfigState = new GameConfigState(); - gameConfigState.Set(tableSheets.GameConfigSheet); - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress) - { - actionPoint = 0, - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, gameConfigState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Rehearsal() - { - var action = new DailyReward5 - { - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Rehearsal = true, - Signer = _agentAddress, - }); - - var updatedAddresses = new List
- { - _avatarAddress, - }; - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - public void Execute(int avatarStateSerializedVersion) - { - IAccountStateDelta previousStates = null; - switch (avatarStateSerializedVersion) - { - case 1: - previousStates = _initialState; - break; - case 2: - var avatarState = _initialState.GetAvatarState(_avatarAddress); - previousStates = SetAvatarStateAsV2To(_initialState, avatarState); - break; - } - - var nextState = ExecuteInternal(previousStates, 1800); - var nextGameConfigState = nextState.GetGameConfigState(); - var nextAvatarState = avatarStateSerializedVersion switch - { - 1 => nextState.GetAvatarState(_avatarAddress), - 2 => nextState.GetAvatarStateV2(_avatarAddress), - _ => null, - }; - if (nextAvatarState is null) - { - return; - } - - Assert.Equal(nextGameConfigState.ActionPointMax, nextAvatarState.actionPoint); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() => - Assert.Throws(() => ExecuteInternal(new MockStateDelta())); - - [Theory] - [InlineData(0, 0, true)] - [InlineData(0, 1799, true)] - [InlineData(0, 1800, false)] - [InlineData(1800, 1800, true)] - [InlineData(1800, 1800 + 1799, true)] - [InlineData(1800, 1800 + 1800, false)] - public void Execute_Throw_RequiredBlockIndexException( - long dailyRewardReceivedIndex, - long executeBlockIndex, - bool throwsException) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.dailyRewardReceivedIndex = dailyRewardReceivedIndex; - var previousStates = SetAvatarStateAsV2To(_initialState, avatarState); - try - { - ExecuteInternal(previousStates, executeBlockIndex); - } - catch (RequiredBlockIndexException) - { - Assert.True(throwsException); - } - } - - private IAccountStateDelta SetAvatarStateAsV2To(IAccountStateDelta state, AvatarState avatarState) => - state - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - private IAccountStateDelta ExecuteInternal(IAccountStateDelta previousStates, long blockIndex = 0) - { - var dailyRewardAction = new DailyReward5 - { - avatarAddress = _avatarAddress, - }; - - return dailyRewardAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward6Test.cs b/.Lib9c.Tests/Action/DailyReward6Test.cs deleted file mode 100644 index d79de8f5f3..0000000000 --- a/.Lib9c.Tests/Action/DailyReward6Test.cs +++ /dev/null @@ -1,200 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class DailyReward6Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly IAccountStateDelta _initialState; - - public DailyReward6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - var gameConfigState = new GameConfigState(); - gameConfigState.Set(tableSheets.GameConfigSheet); - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress) - { - actionPoint = 0, - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, gameConfigState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Rehearsal() - { - var action = new DailyReward6 - { - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Random = new TestRandom(), - Rehearsal = true, - Signer = _agentAddress, - }); - - var updatedAddresses = new List
- { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - public void Execute(int avatarStateSerializedVersion) - { - IAccountStateDelta previousStates = null; - switch (avatarStateSerializedVersion) - { - case 1: - previousStates = _initialState; - break; - case 2: - var avatarState = _initialState.GetAvatarState(_avatarAddress); - previousStates = SetAvatarStateAsV2To(_initialState, avatarState); - break; - } - - var nextState = ExecuteInternal(previousStates, 1800); - var nextGameConfigState = nextState.GetGameConfigState(); - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.NotNull(nextAvatarState); - Assert.NotNull(nextAvatarState.inventory); - Assert.NotNull(nextAvatarState.questList); - Assert.NotNull(nextAvatarState.worldInformation); - Assert.Equal(nextGameConfigState.ActionPointMax, nextAvatarState.actionPoint); - - var avatarRuneAmount = nextState.GetBalance(_avatarAddress, RuneHelper.DailyRewardRune); - var expectedRune = RuneHelper.DailyRewardRune * nextGameConfigState.DailyRuneRewardAmount; - Assert.Equal(expectedRune, avatarRuneAmount); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() => - Assert.Throws(() => ExecuteInternal(new MockStateDelta())); - - [Theory] - [InlineData(0, 0, true)] - [InlineData(0, 1799, true)] - [InlineData(0, 1800, false)] - [InlineData(1800, 1800, true)] - [InlineData(1800, 1800 + 1799, true)] - [InlineData(1800, 1800 + 1800, false)] - public void Execute_Throw_RequiredBlockIndexException( - long dailyRewardReceivedIndex, - long executeBlockIndex, - bool throwsException) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.dailyRewardReceivedIndex = dailyRewardReceivedIndex; - var previousStates = SetAvatarStateAsV2To(_initialState, avatarState); - try - { - ExecuteInternal(previousStates, executeBlockIndex); - } - catch (RequiredBlockIndexException) - { - Assert.True(throwsException); - } - } - - [Fact] - private void Execute_Without_Runereward() - { - var gameConfigSheet = new GameConfigSheet(); - var csv = @"key,value -hourglass_per_block,3 -action_point_max,120 -daily_reward_interval,1 -daily_arena_interval,5040 -weekly_arena_interval,56000 -required_appraise_block,10 -battle_arena_interval,4 -rune_stat_slot_unlock_cost,50 -rune_skill_slot_unlock_cost,500"; - gameConfigSheet.Set(csv); - var gameConfigState = new GameConfigState(); - gameConfigState.Set(gameConfigSheet); - - var state = _initialState - .SetState(Addresses.GameConfig, gameConfigState.Serialize()); - var nextState = ExecuteInternal(state, 1800); - var avatarRuneAmount = nextState.GetBalance(_avatarAddress, RuneHelper.DailyRewardRune); - Assert.Equal(0, (int)avatarRuneAmount.MajorUnit); - } - - private IAccountStateDelta SetAvatarStateAsV2To(IAccountStateDelta state, AvatarState avatarState) => - state - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - private IAccountStateDelta ExecuteInternal(IAccountStateDelta previousStates, long blockIndex = 0) - { - var dailyRewardAction = new DailyReward6 - { - avatarAddress = _avatarAddress, - }; - - return dailyRewardAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/EventDungeonBattleV1Test.cs b/.Lib9c.Tests/Action/EventDungeonBattleV1Test.cs deleted file mode 100644 index 94afaa1763..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV1Test.cs +++ /dev/null @@ -1,438 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Exceptions; - using Nekoyume.Extensions; - using Nekoyume.Model.Event; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Nekoyume.TableData.Event; - using Xunit; - using static Lib9c.SerializeKeys; - - public class EventDungeonBattleV1Test - { - private readonly Currency _ncgCurrency; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccountStateDelta _initialStates; - - public EventDungeonBattleV1Test() - { - _initialStates = new MockStateDelta(); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _ncgCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _initialStates = _initialStates.SetState( - GoldCurrencyState.Address, - new GoldCurrencyState(_ncgCurrency).Serialize()); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialStates = _initialStates - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var inventoryAddr = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddr = _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddr = _avatarAddress.Derive(LegacyQuestListKey); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses.Add(0, _avatarAddress); - - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - new PrivateKey().ToAddress() - ) - { - level = 100, - }; - - _initialStates = _initialStates - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddr, avatarState.inventory.Serialize()) - .SetState(worldInformationAddr, avatarState.worldInformation.Serialize()) - .SetState(questListAddr, avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Success_Within_Event_Period( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0, 0, 0)] - [InlineData(1001, 10010001, 10010001, 1, 1, 1)] - [InlineData(1001, 10010001, 10010001, int.MaxValue, int.MaxValue, int.MaxValue - 1)] - public void Execute_Success_With_Ticket_Purchase( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int dungeonTicketPrice, - int dungeonTicketAdditionalPrice, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var scheduleSheet = _tableSheets.EventScheduleSheet; - Assert.True(scheduleSheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var sb = new StringBuilder(); - sb.AppendLine( - "id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_exp_seed_value,recipe_end_block_index,dungeon_ticket_price,dungeon_ticket_additional_price"); - sb.AppendLine( - $"{eventScheduleId}" + - $",\"2022 Summer Event\"" + - $",{scheduleRow.StartBlockIndex}" + - $",{scheduleRow.DungeonEndBlockIndex}" + - $",{scheduleRow.DungeonTicketsMax}" + - $",{scheduleRow.DungeonTicketsResetIntervalBlockRange}" + - $",{dungeonTicketPrice}" + - $",{dungeonTicketAdditionalPrice}" + - $",{scheduleRow.DungeonExpSeedValue}" + - $",{scheduleRow.RecipeEndBlockIndex}"); - previousStates = previousStates.SetState( - Addresses.GetSheetAddress(), - sb.ToString().Serialize()); - - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates.SetState( - eventDungeonInfoAddr, - eventDungeonInfo.Serialize()); - - Assert.True(previousStates.GetSheet() - .TryGetValue(eventScheduleId, out var newScheduleRow)); - var ncgHas = newScheduleRow.GetDungeonTicketCostV1(numberOfTicketPurchases); - if (ncgHas > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas * _ncgCurrency); - } - - var nextStates = Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex); - var nextEventDungeonInfoList = - (Bencodex.Types.List)nextStates.GetState(eventDungeonInfoAddr)!; - Assert.Equal( - numberOfTicketPurchases + 1, - nextEventDungeonInfoList[2].ToInteger()); - } - - [Theory] - [InlineData(10000001, 10010001, 10010001)] - [InlineData(10010001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventScheduleId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) => - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId)); - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_ContextBlockIndex( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex - 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - contextBlockIndex = scheduleRow.DungeonEndBlockIndex + 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - } - - [Theory] - [InlineData(1001, 10020001, 10010001)] - [InlineData(1001, 1001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10020001)] - [InlineData(1001, 10010001, 1001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonStageId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_NotEnoughEventDungeonTicketsException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo(); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0)] - [InlineData(1001, 10010001, 10010001, int.MaxValue - 1)] - public void Execute_Throw_InsufficientBalanceException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var ncgHas = scheduleRow.GetDungeonTicketCostV1(numberOfTicketPurchases) - 1; - if (ncgHas > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas * _ncgCurrency); - } - - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010002)] - public void Execute_Throw_StageNotClearedException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Fact] - public void Execute_V100301() - { - int eventScheduleId = 1001; - int eventDungeonId = 10010001; - int eventDungeonStageId = 10010001; - var csv = $@"id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_ticket_price,dungeon_ticket_additional_price,dungeon_exp_seed_value,recipe_end_block_index - 1001,2022 Summer Event,{ActionObsoleteConfig.V100301ExecutedBlockIndex},{ActionObsoleteConfig.V100301ExecutedBlockIndex + 100},5,7200,5,2,1,5018000"; - _initialStates = - _initialStates.SetState( - Addresses.GetSheetAddress(), - csv.Serialize()); - var sheet = new EventScheduleSheet(); - sheet.Set(csv); - Assert.True(sheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - private IAccountStateDelta Execute( - IAccountStateDelta previousStates, - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - bool buyTicketIfNeeded = false, - long blockIndex = 0) - { - var previousAvatarState = previousStates.GetAvatarStateV2(_avatarAddress); - var equipments = - Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment, iLock: null); - } - - var action = new EventDungeonBattleV1 - { - AvatarAddress = _avatarAddress, - EventScheduleId = eventScheduleId, - EventDungeonId = eventDungeonId, - EventDungeonStageId = eventDungeonStageId, - Equipments = equipments - .Select(e => e.NonFungibleId) - .ToList(), - Costumes = new List(), - Foods = new List(), - BuyTicketIfNeeded = buyTicketIfNeeded, - }; - - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = blockIndex, - }); - - Assert.True(nextStates.GetSheet().TryGetValue( - eventScheduleId, - out var scheduleRow)); - var nextAvatarState = nextStates.GetAvatarStateV2(_avatarAddress); - var expectExp = scheduleRow.GetStageExp( - eventDungeonStageId.ToEventDungeonStageNumber()); - Assert.Equal( - previousAvatarState.exp + expectExp, - nextAvatarState.exp); - - return nextStates; - } - } -} diff --git a/.Lib9c.Tests/Action/EventDungeonBattleV2Test.cs b/.Lib9c.Tests/Action/EventDungeonBattleV2Test.cs deleted file mode 100644 index 04a2dad3b2..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV2Test.cs +++ /dev/null @@ -1,452 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Exceptions; - using Nekoyume.Extensions; - using Nekoyume.Model.Event; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Nekoyume.TableData.Event; - using Xunit; - using static Lib9c.SerializeKeys; - - public class EventDungeonBattleV2Test - { - private readonly Currency _ncgCurrency; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccountStateDelta _initialStates; - - public EventDungeonBattleV2Test() - { - _initialStates = new MockStateDelta(); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _ncgCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _initialStates = _initialStates.SetState( - GoldCurrencyState.Address, - new GoldCurrencyState(_ncgCurrency).Serialize()); - var sheets = TableSheetsImporter.ImportSheets(); - sheets.Remove(nameof(RuneOptionSheet)); - foreach (var (key, value) in sheets) - { - _initialStates = _initialStates - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var inventoryAddr = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddr = _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddr = _avatarAddress.Derive(LegacyQuestListKey); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses.Add(0, _avatarAddress); - - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - new PrivateKey().ToAddress() - ) - { - level = 100, - }; - - _initialStates = _initialStates - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddr, avatarState.inventory.Serialize()) - .SetState(worldInformationAddr, avatarState.worldInformation.Serialize()) - .SetState(questListAddr, avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Success_Within_Event_Period( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0, 0, 0)] - [InlineData(1001, 10010001, 10010001, 1, 1, 1)] - [InlineData(1001, 10010001, 10010001, int.MaxValue, int.MaxValue, int.MaxValue - 1)] - public void Execute_Success_With_Ticket_Purchase( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int dungeonTicketPrice, - int dungeonTicketAdditionalPrice, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var scheduleSheet = _tableSheets.EventScheduleSheet; - Assert.True(scheduleSheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var sb = new StringBuilder(); - sb.AppendLine( - "id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_exp_seed_value,recipe_end_block_index,dungeon_ticket_price,dungeon_ticket_additional_price"); - sb.AppendLine( - $"{eventScheduleId}" + - $",\"2022 Summer Event\"" + - $",{scheduleRow.StartBlockIndex}" + - $",{scheduleRow.DungeonEndBlockIndex}" + - $",{scheduleRow.DungeonTicketsMax}" + - $",{scheduleRow.DungeonTicketsResetIntervalBlockRange}" + - $",{dungeonTicketPrice}" + - $",{dungeonTicketAdditionalPrice}" + - $",{scheduleRow.DungeonExpSeedValue}" + - $",{scheduleRow.RecipeEndBlockIndex}"); - previousStates = previousStates.SetState( - Addresses.GetSheetAddress(), - sb.ToString().Serialize()); - - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates.SetState( - eventDungeonInfoAddr, - eventDungeonInfo.Serialize()); - - Assert.True(previousStates.GetSheet() - .TryGetValue(eventScheduleId, out var newScheduleRow)); - var ncgHas = newScheduleRow.GetDungeonTicketCost( - numberOfTicketPurchases, - _ncgCurrency); - - if (ncgHas.Sign > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas); - } - - var nextStates = Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex); - var nextEventDungeonInfoList = - (Bencodex.Types.List)nextStates.GetState(eventDungeonInfoAddr)!; - Assert.Equal( - numberOfTicketPurchases + 1, - nextEventDungeonInfoList[2].ToInteger()); - Assert.True( - nextStates.TryGetGoldBalance( - _agentAddress, - _ncgCurrency, - out FungibleAssetValue balance - ) - ); - Assert.Equal(0 * _ncgCurrency, balance); - } - - [Theory] - [InlineData(10000001, 10010001, 10010001)] - [InlineData(10010001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventScheduleId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) => - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId)); - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_ContextBlockIndex( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex - 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - contextBlockIndex = scheduleRow.DungeonEndBlockIndex + 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - } - - [Theory] - [InlineData(1001, 10020001, 10010001)] - [InlineData(1001, 1001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10020001)] - [InlineData(1001, 10010001, 1001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonStageId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_NotEnoughEventDungeonTicketsException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo(); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0)] - [InlineData(1001, 10010001, 10010001, int.MaxValue - 1)] - public void Execute_Throw_InsufficientBalanceException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var ncgHas = scheduleRow.GetDungeonTicketCost( - numberOfTicketPurchases, - _ncgCurrency) - 1 * _ncgCurrency; - if (ncgHas.Sign > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas); - } - - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010002)] - public void Execute_Throw_StageNotClearedException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Fact] - public void Execute_V100301() - { - int eventScheduleId = 1001; - int eventDungeonId = 10010001; - int eventDungeonStageId = 10010001; - var csv = $@"id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_ticket_price,dungeon_ticket_additional_price,dungeon_exp_seed_value,recipe_end_block_index - 1001,2022 Summer Event,{ActionObsoleteConfig.V100301ExecutedBlockIndex},{ActionObsoleteConfig.V100301ExecutedBlockIndex + 100},5,7200,5,2,1,5018000"; - _initialStates = - _initialStates.SetState( - Addresses.GetSheetAddress(), - csv.Serialize()); - var sheet = new EventScheduleSheet(); - sheet.Set(csv); - Assert.True(sheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - private IAccountStateDelta Execute( - IAccountStateDelta previousStates, - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - bool buyTicketIfNeeded = false, - long blockIndex = 0) - { - var previousAvatarState = previousStates.GetAvatarStateV2(_avatarAddress); - var equipments = - Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment, iLock: null); - } - - var action = new EventDungeonBattleV2 - { - AvatarAddress = _avatarAddress, - EventScheduleId = eventScheduleId, - EventDungeonId = eventDungeonId, - EventDungeonStageId = eventDungeonStageId, - Equipments = equipments - .Select(e => e.NonFungibleId) - .ToList(), - Costumes = new List(), - Foods = new List(), - BuyTicketIfNeeded = buyTicketIfNeeded, - }; - - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = blockIndex, - }); - - Assert.True(nextStates.GetSheet().TryGetValue( - eventScheduleId, - out var scheduleRow)); - var nextAvatarState = nextStates.GetAvatarStateV2(_avatarAddress); - var expectExp = scheduleRow.GetStageExp( - eventDungeonStageId.ToEventDungeonStageNumber()); - Assert.Equal( - previousAvatarState.exp + expectExp, - nextAvatarState.exp); - - return nextStates; - } - } -} diff --git a/.Lib9c.Tests/Action/EventDungeonBattleV3Test.cs b/.Lib9c.Tests/Action/EventDungeonBattleV3Test.cs deleted file mode 100644 index a5fb074d82..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV3Test.cs +++ /dev/null @@ -1,451 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Exceptions; - using Nekoyume.Extensions; - using Nekoyume.Model.Event; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Nekoyume.TableData.Event; - using Xunit; - using static Lib9c.SerializeKeys; - - public class EventDungeonBattleV3Test - { - private readonly Currency _ncgCurrency; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccountStateDelta _initialStates; - - public EventDungeonBattleV3Test() - { - _initialStates = new MockStateDelta(); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _ncgCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _initialStates = _initialStates.SetState( - GoldCurrencyState.Address, - new GoldCurrencyState(_ncgCurrency).Serialize()); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialStates = _initialStates - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var inventoryAddr = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddr = _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddr = _avatarAddress.Derive(LegacyQuestListKey); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses.Add(0, _avatarAddress); - - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - new PrivateKey().ToAddress() - ) - { - level = 100, - }; - - _initialStates = _initialStates - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddr, avatarState.inventory.Serialize()) - .SetState(worldInformationAddr, avatarState.worldInformation.Serialize()) - .SetState(questListAddr, avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Success_Within_Event_Period( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0, 0, 0)] - [InlineData(1001, 10010001, 10010001, 1, 1, 1)] - [InlineData(1001, 10010001, 10010001, int.MaxValue, int.MaxValue, int.MaxValue - 1)] - public void Execute_Success_With_Ticket_Purchase( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int dungeonTicketPrice, - int dungeonTicketAdditionalPrice, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var scheduleSheet = _tableSheets.EventScheduleSheet; - Assert.True(scheduleSheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var sb = new StringBuilder(); - sb.AppendLine( - "id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_exp_seed_value,recipe_end_block_index,dungeon_ticket_price,dungeon_ticket_additional_price"); - sb.AppendLine( - $"{eventScheduleId}" + - $",\"2022 Summer Event\"" + - $",{scheduleRow.StartBlockIndex}" + - $",{scheduleRow.DungeonEndBlockIndex}" + - $",{scheduleRow.DungeonTicketsMax}" + - $",{scheduleRow.DungeonTicketsResetIntervalBlockRange}" + - $",{dungeonTicketPrice}" + - $",{dungeonTicketAdditionalPrice}" + - $",{scheduleRow.DungeonExpSeedValue}" + - $",{scheduleRow.RecipeEndBlockIndex}"); - previousStates = previousStates.SetState( - Addresses.GetSheetAddress(), - sb.ToString().Serialize()); - - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates.SetState( - eventDungeonInfoAddr, - eventDungeonInfo.Serialize()); - - Assert.True(previousStates.GetSheet() - .TryGetValue(eventScheduleId, out var newScheduleRow)); - var ncgHas = newScheduleRow.GetDungeonTicketCost( - numberOfTicketPurchases, - _ncgCurrency); - if (ncgHas.Sign > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas); - } - - var nextStates = Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex); - var nextEventDungeonInfoList = - (Bencodex.Types.List)nextStates.GetState(eventDungeonInfoAddr)!; - Assert.Equal( - numberOfTicketPurchases + 1, - nextEventDungeonInfoList[2].ToInteger()); - Assert.True( - nextStates.TryGetGoldBalance( - _agentAddress, - _ncgCurrency, - out FungibleAssetValue balance - ) - ); - Assert.Equal(0 * _ncgCurrency, balance); - } - - [Theory] - [InlineData(10000001, 10010001, 10010001)] - [InlineData(10010001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventScheduleId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) => - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId)); - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_ContextBlockIndex( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex - 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - contextBlockIndex = scheduleRow.DungeonEndBlockIndex + 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - } - - [Theory] - [InlineData(1001, 10020001, 10010001)] - [InlineData(1001, 1001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10020001)] - [InlineData(1001, 10010001, 1001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonStageId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_NotEnoughEventDungeonTicketsException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo(); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0)] - [InlineData(1001, 10010001, 10010001, int.MaxValue - 1)] - public void Execute_Throw_InsufficientBalanceException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var ncgHas = scheduleRow.GetDungeonTicketCost( - numberOfTicketPurchases, - _ncgCurrency) - 1 * _ncgCurrency; - if (ncgHas.Sign > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas); - } - - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010002)] - public void Execute_Throw_StageNotClearedException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Fact] - public void Execute_V100301() - { - int eventScheduleId = 1001; - int eventDungeonId = 10010001; - int eventDungeonStageId = 10010001; - var csv = $@"id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_ticket_price,dungeon_ticket_additional_price,dungeon_exp_seed_value,recipe_end_block_index - 1001,2022 Summer Event,{ActionObsoleteConfig.V100301ExecutedBlockIndex},{ActionObsoleteConfig.V100301ExecutedBlockIndex + 100},5,7200,5,2,1,5018000"; - _initialStates = - _initialStates.SetState( - Addresses.GetSheetAddress(), - csv.Serialize()); - var sheet = new EventScheduleSheet(); - sheet.Set(csv); - Assert.True(sheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - private IAccountStateDelta Execute( - IAccountStateDelta previousStates, - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - bool buyTicketIfNeeded = false, - long blockIndex = 0) - { - var previousAvatarState = previousStates.GetAvatarStateV2(_avatarAddress); - var equipments = - Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment, iLock: null); - } - - var action = new EventDungeonBattleV3 - { - AvatarAddress = _avatarAddress, - EventScheduleId = eventScheduleId, - EventDungeonId = eventDungeonId, - EventDungeonStageId = eventDungeonStageId, - Equipments = equipments - .Select(e => e.NonFungibleId) - .ToList(), - Costumes = new List(), - Foods = new List(), - RuneInfos = new List(), - BuyTicketIfNeeded = buyTicketIfNeeded, - }; - - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = blockIndex, - }); - - Assert.True(nextStates.GetSheet().TryGetValue( - eventScheduleId, - out var scheduleRow)); - var nextAvatarState = nextStates.GetAvatarStateV2(_avatarAddress); - var expectExp = scheduleRow.GetStageExp( - eventDungeonStageId.ToEventDungeonStageNumber()); - Assert.Equal( - previousAvatarState.exp + expectExp, - nextAvatarState.exp); - - return nextStates; - } - } -} diff --git a/.Lib9c.Tests/Action/EventDungeonBattleV4Test.cs b/.Lib9c.Tests/Action/EventDungeonBattleV4Test.cs deleted file mode 100644 index 39c4ca2314..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV4Test.cs +++ /dev/null @@ -1,499 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Exceptions; - using Nekoyume.Extensions; - using Nekoyume.Model.Event; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Nekoyume.TableData.Event; - using Xunit; - using static Lib9c.SerializeKeys; - - public class EventDungeonBattleV4Test - { - private readonly Currency _ncgCurrency; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccountStateDelta _initialStates; - - public EventDungeonBattleV4Test() - { - _initialStates = new MockStateDelta(); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _ncgCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _initialStates = _initialStates.SetState( - GoldCurrencyState.Address, - new GoldCurrencyState(_ncgCurrency).Serialize()); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialStates = _initialStates - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var inventoryAddr = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddr = _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddr = _avatarAddress.Derive(LegacyQuestListKey); - - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses.Add(0, _avatarAddress); - - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - new PrivateKey().ToAddress() - ) - { - level = 100, - }; - - _initialStates = _initialStates - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddr, avatarState.inventory.Serialize()) - .SetState(worldInformationAddr, avatarState.worldInformation.Serialize()) - .SetState(questListAddr, avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Success_Within_Event_Period( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0, 0, 0)] - [InlineData(1001, 10010001, 10010001, 1, 1, 1)] - [InlineData(1001, 10010001, 10010001, int.MaxValue, int.MaxValue, int.MaxValue - 1)] - public void Execute_Success_With_Ticket_Purchase( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int dungeonTicketPrice, - int dungeonTicketAdditionalPrice, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var scheduleSheet = _tableSheets.EventScheduleSheet; - Assert.True(scheduleSheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var sb = new StringBuilder(); - sb.AppendLine( - "id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_exp_seed_value,recipe_end_block_index,dungeon_ticket_price,dungeon_ticket_additional_price"); - sb.AppendLine( - $"{eventScheduleId}" + - $",\"2022 Summer Event\"" + - $",{scheduleRow.StartBlockIndex}" + - $",{scheduleRow.DungeonEndBlockIndex}" + - $",{scheduleRow.DungeonTicketsMax}" + - $",{scheduleRow.DungeonTicketsResetIntervalBlockRange}" + - $",{dungeonTicketPrice}" + - $",{dungeonTicketAdditionalPrice}" + - $",{scheduleRow.DungeonExpSeedValue}" + - $",{scheduleRow.RecipeEndBlockIndex}"); - previousStates = previousStates.SetState( - Addresses.GetSheetAddress(), - sb.ToString().Serialize()); - - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates.SetState( - eventDungeonInfoAddr, - eventDungeonInfo.Serialize()); - - Assert.True(previousStates.GetSheet() - .TryGetValue(eventScheduleId, out var newScheduleRow)); - var ncgHas = newScheduleRow.GetDungeonTicketCost( - numberOfTicketPurchases, - _ncgCurrency); - if (ncgHas.Sign > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas); - } - - var nextStates = Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex); - var nextEventDungeonInfoList = - (Bencodex.Types.List)nextStates.GetState(eventDungeonInfoAddr)!; - Assert.Equal( - numberOfTicketPurchases + 1, - nextEventDungeonInfoList[2].ToInteger()); - Assert.True( - nextStates.TryGetGoldBalance( - _agentAddress, - _ncgCurrency, - out FungibleAssetValue balance - ) - ); - Assert.Equal(0 * _ncgCurrency, balance); - } - - [Theory] - [InlineData(10000001, 10010001, 10010001)] - [InlineData(10010001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventScheduleId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) => - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId)); - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_ContextBlockIndex( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex - 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - contextBlockIndex = scheduleRow.DungeonEndBlockIndex + 1; - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex)); - } - - [Theory] - [InlineData(1001, 10020001, 10010001)] - [InlineData(1001, 1001, 10010001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10020001)] - [InlineData(1001, 10010001, 1001)] - public void Execute_Throw_InvalidActionFieldException_By_EventDungeonStageId( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001)] - public void Execute_Throw_NotEnoughEventDungeonTicketsException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo(); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010001, 0)] - [InlineData(1001, 10010001, 10010001, int.MaxValue - 1)] - public void Execute_Throw_InsufficientBalanceException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - int numberOfTicketPurchases) - { - var context = new ActionContext(); - var previousStates = _initialStates; - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = new EventDungeonInfo( - remainingTickets: 0, - numberOfTicketPurchases: numberOfTicketPurchases); - previousStates = previousStates - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - var ncgHas = scheduleRow.GetDungeonTicketCost( - numberOfTicketPurchases, - _ncgCurrency) - 1 * _ncgCurrency; - if (ncgHas.Sign > 0) - { - previousStates = previousStates.MintAsset(context, _agentAddress, ncgHas); - } - - Assert.Throws(() => - Execute( - previousStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - buyTicketIfNeeded: true, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(1001, 10010001, 10010002)] - public void Execute_Throw_StageNotClearedException( - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(eventScheduleId, out var scheduleRow)); - Assert.Throws(() => - Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: scheduleRow.StartBlockIndex)); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void Execute_DuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - Assert.True(_tableSheets.EventScheduleSheet - .TryGetValue(1001, out var scheduleRow)); - - var context = new ActionContext(); - _initialStates = _initialStates.MintAsset(context, _agentAddress, 99999 * _ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatarAddress, - SlotIndex = 1, - }; - - _initialStates = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = _initialStates, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - Assert.Throws(exception, () => - Execute( - _initialStates, - 1001, - 10010001, - 10010001, - false, - scheduleRow.StartBlockIndex, - slotIndex, - runeId, - slotIndex2, - runeId2)); - } - - [Fact] - public void Execute_V100301() - { - int eventScheduleId = 1001; - int eventDungeonId = 10010001; - int eventDungeonStageId = 10010001; - var csv = $@"id,_name,start_block_index,dungeon_end_block_index,dungeon_tickets_max,dungeon_tickets_reset_interval_block_range,dungeon_ticket_price,dungeon_ticket_additional_price,dungeon_exp_seed_value,recipe_end_block_index - 1001,2022 Summer Event,{ActionObsoleteConfig.V100301ExecutedBlockIndex},{ActionObsoleteConfig.V100301ExecutedBlockIndex + 100},5,7200,5,2,1,5018000"; - _initialStates = - _initialStates.SetState( - Addresses.GetSheetAddress(), - csv.Serialize()); - var sheet = new EventScheduleSheet(); - sheet.Set(csv); - Assert.True(sheet.TryGetValue(eventScheduleId, out var scheduleRow)); - var contextBlockIndex = scheduleRow.StartBlockIndex; - var nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - var eventDungeonInfoAddr = - EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); - var eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - - contextBlockIndex = scheduleRow.DungeonEndBlockIndex; - nextStates = Execute( - _initialStates, - eventScheduleId, - eventDungeonId, - eventDungeonStageId, - blockIndex: contextBlockIndex); - eventDungeonInfo = - new EventDungeonInfo(nextStates.GetState(eventDungeonInfoAddr)); - Assert.Equal( - scheduleRow.DungeonTicketsMax - 1, - eventDungeonInfo.RemainingTickets); - } - - private IAccountStateDelta Execute( - IAccountStateDelta previousStates, - int eventScheduleId, - int eventDungeonId, - int eventDungeonStageId, - bool buyTicketIfNeeded = false, - long blockIndex = 0, - int slotIndex = 0, - int runeId = 10002, - int slotIndex2 = 1, - int runeId2 = 30001) - { - var previousAvatarState = previousStates.GetAvatarStateV2(_avatarAddress); - var equipments = - Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment, iLock: null); - } - - var action = new EventDungeonBattleV4 - { - AvatarAddress = _avatarAddress, - EventScheduleId = eventScheduleId, - EventDungeonId = eventDungeonId, - EventDungeonStageId = eventDungeonStageId, - Equipments = equipments - .Select(e => e.NonFungibleId) - .ToList(), - Costumes = new List(), - Foods = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - BuyTicketIfNeeded = buyTicketIfNeeded, - }; - - var nextStates = action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = blockIndex, - }); - - Assert.True(nextStates.GetSheet().TryGetValue( - eventScheduleId, - out var scheduleRow)); - var nextAvatarState = nextStates.GetAvatarStateV2(_avatarAddress); - var expectExp = scheduleRow.GetStageExp( - eventDungeonStageId.ToEventDungeonStageNumber()); - Assert.Equal( - previousAvatarState.exp + expectExp, - nextAvatarState.exp); - - return nextStates; - } - } -} diff --git a/.Lib9c.Tests/Action/Factory/ClaimStakeRewardFactoryTest.cs b/.Lib9c.Tests/Action/Factory/ClaimStakeRewardFactoryTest.cs deleted file mode 100644 index 95b624a7cd..0000000000 --- a/.Lib9c.Tests/Action/Factory/ClaimStakeRewardFactoryTest.cs +++ /dev/null @@ -1,72 +0,0 @@ -#nullable enable - -namespace Lib9c.Tests.Action.Factory -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Bencodex.Types; - using Lib9c.Abstractions; - using Libplanet.Action; - using Libplanet.Crypto; - using Nekoyume.Action; - using Nekoyume.Action.Factory; - using Xunit; - - public class ClaimStakeRewardFactoryTest - { - public static IEnumerable GetAllClaimStakeRewardV1() - { - var arr = Assembly.GetAssembly(typeof(ClaimRaidReward))?.GetTypes() - .Where(type => - type.IsClass && - typeof(IClaimStakeRewardV1).IsAssignableFrom(type)) - .Select(type => - type.GetCustomAttribute()?.TypeIdentifier) - .OfType() - .ToArray() ?? Array.Empty(); - - foreach (var value in arr) - { - var str = (string)(Text)value; - var verStr = str.Replace("claim_stake_reward", string.Empty); - var ver = string.IsNullOrEmpty(verStr) - ? 1 - : int.Parse(verStr); - yield return new object[] { str, ver }; - } - } - - [Theory] - [InlineData(0L, typeof(ClaimStakeReward2))] - [InlineData(ClaimStakeReward2.ObsoletedIndex - 1, typeof(ClaimStakeReward2))] - [InlineData(ClaimStakeReward2.ObsoletedIndex, typeof(ClaimStakeReward2))] - [InlineData(ClaimStakeReward2.ObsoletedIndex + 1, typeof(ClaimStakeReward3))] - [InlineData(ClaimStakeReward3.ObsoleteBlockIndex, typeof(ClaimStakeReward3))] - [InlineData(ClaimStakeReward3.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward4))] - [InlineData(ClaimStakeReward4.ObsoleteBlockIndex, typeof(ClaimStakeReward4))] - [InlineData(ClaimStakeReward4.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward))] - [InlineData(long.MaxValue, typeof(ClaimStakeReward))] - public void Create_ByBlockIndex_Success( - long blockIndex, - Type type) - { - var addr = new PrivateKey().ToAddress(); - var action = ClaimStakeRewardFactory.CreateByBlockIndex(blockIndex, addr); - Assert.Equal(type, action.GetType()); - } - - [Theory] - [MemberData(nameof(GetAllClaimStakeRewardV1))] - public void Create_ByVersion_Success(string expectActionType, int version) - { - var addr = new PrivateKey().ToAddress(); - var action = ClaimStakeRewardFactory.CreateByVersion(version, addr); - var actualActionType = action - .GetType() - .GetCustomAttribute()?.TypeIdentifier; - Assert.Equal(new Text(expectActionType), actualActionType); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash0Test.cs b/.Lib9c.Tests/Action/HackAndSlash0Test.cs deleted file mode 100644 index d8f494a95b..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash0Test.cs +++ /dev/null @@ -1,687 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class HackAndSlash0Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash0Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 100, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(1, 1, 1, false)] - [InlineData(300, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash0() - { - costumes = new List { costumeId }, - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.NotNull(action.Result); - - Assert.NotEmpty(action.Result.OfType()); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem2(equipment); - - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem2(equipment); - avatarState.level = 0; - - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _weeklyArenaState.address, - _rankingMapAddress, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new HackAndSlash0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (HackAndSlash0)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Fact] - public void PlainValue() - { - var guid1 = new Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); - var guid2 = new Guid("936DA01F-9ABD-4d9d-80C7-02AF85C822A8"); - var action = new HackAndSlash0() - { - costumes = new List() - { - 3, - 2, - 1, - }, - equipments = new List() - { - guid2, - guid1, - }, - foods = new List() - { - guid2, - guid1, - }, - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var deserialized = new HackAndSlash0(); - deserialized.LoadPlainValue(action.PlainValue); - - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash10Test.cs b/.Lib9c.Tests/Action/HackAndSlash10Test.cs deleted file mode 100644 index c86c04179a..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash10Test.cs +++ /dev/null @@ -1,1156 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash10Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash10Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, bool backward, bool isLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - clearedStageId = playCount > 1 ? clearedStageId + 1 : clearedStageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - if (isLock) - { - orderLock = new OrderLock(Guid.NewGuid()); - } - - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash10 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - - var value = nextState.GetState(_rankingMapAddress); - if (!isClearedBefore) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Theory] - [InlineData(4, 200, 1)] - [InlineData(4, 200, 2)] - public void Execute_With_UpdateQuestList(int worldId, int stageId, int playCount) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = avatarState.address, - rankingMapAddress = _rankingMapAddress, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = default, - }; - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash10 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(true, 1, 15, 100)] - [InlineData(true, 2, 55, 100)] - [InlineData(true, 3, 111, 100)] - [InlineData(true, 4, 189, 100)] - [InlineData(false, 1, 15, 100)] - [InlineData(false, 2, 55, 100)] - [InlineData(false, 3, 111, 100)] - [InlineData(false, 4, 189, 100)] - public void CheckRewardItems(bool backward, int worldId, int stageId, int playCount) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - List equipments = new List(); - - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - - var beltId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Belt) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var belt = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[beltId], - random) - as Equipment; - equipments.Add(belt.ItemId); - previousAvatarState.inventory.AddItem(belt); - - var necklaceId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Necklace) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var necklace = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[necklaceId], - random) - as Equipment; - equipments.Add(necklace.ItemId); - previousAvatarState.inventory.AddItem(necklace); - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash10 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = (min * playCount * stageRow.DropItemMin) + questSum; - var totalMax = (max * playCount * stageRow.DropItemMax) + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash11Test.cs b/.Lib9c.Tests/Action/HackAndSlash11Test.cs deleted file mode 100644 index 8fa6674128..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash11Test.cs +++ /dev/null @@ -1,1100 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash11Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash11Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, bool backward, bool isLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - clearedStageId = playCount > 1 ? clearedStageId + 1 : clearedStageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - if (isLock) - { - orderLock = new OrderLock(Guid.NewGuid()); - } - - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash11 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200, 1)] - [InlineData(4, 200, 2)] - public void Execute_With_UpdateQuestList(int worldId, int stageId, int playCount) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash11 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(true, 1, 15, 100)] - [InlineData(true, 2, 55, 100)] - [InlineData(true, 3, 111, 100)] - [InlineData(true, 4, 189, 100)] - [InlineData(false, 1, 15, 100)] - [InlineData(false, 2, 55, 100)] - [InlineData(false, 3, 111, 100)] - [InlineData(false, 4, 189, 100)] - public void CheckRewardItems(bool backward, int worldId, int stageId, int playCount) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - List equipments = new List(); - - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - - var beltId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Belt) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var belt = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[beltId], - random) - as Equipment; - equipments.Add(belt.ItemId); - previousAvatarState.inventory.AddItem(belt); - - var necklaceId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Necklace) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var necklace = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[necklaceId], - random) - as Equipment; - equipments.Add(necklace.ItemId); - previousAvatarState.inventory.AddItem(necklace); - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash11 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = (min * playCount * stageRow.DropItemMin) + questSum; - var totalMax = (max * playCount * stageRow.DropItemMax) + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash11 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash12Test.cs b/.Lib9c.Tests/Action/HackAndSlash12Test.cs deleted file mode 100644 index 83f87d00df..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash12Test.cs +++ /dev/null @@ -1,1096 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash12Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash12Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (!keys.Contains(key)) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - clearedStageId = playCount > 1 ? clearedStageId + 1 : clearedStageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash12 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200, 1)] - [InlineData(4, 200, 2)] - public void Execute_With_UpdateQuestList(int worldId, int stageId, int playCount) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash12 - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash12 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash12 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Theory] - [InlineData(true, 1, 15, 100)] - [InlineData(true, 2, 55, 100)] - [InlineData(true, 3, 111, 100)] - [InlineData(true, 4, 189, 100)] - [InlineData(false, 1, 15, 100)] - [InlineData(false, 2, 55, 100)] - [InlineData(false, 3, 111, 100)] - [InlineData(false, 4, 189, 100)] - public void CheckRewardItems(bool backward, int worldId, int stageId, int playCount) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash12 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin + questSum; - var totalMax = max * playCount * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash12 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash13Test.cs b/.Lib9c.Tests/Action/HackAndSlash13Test.cs deleted file mode 100644 index b9942fc3c0..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash13Test.cs +++ /dev/null @@ -1,1134 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash13Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash13Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (!keys.Contains(key)) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _initialState = _initialState.SetState(arenaSheetAddress, null); - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash13 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash13 - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash13 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash13 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowActionObsoletedException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 10, - }; - - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState.SetState(Addresses.GetSheetAddress(), _tableSheets.ArenaSheet.Serialize()); - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash13 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash13 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash15Test.cs b/.Lib9c.Tests/Action/HackAndSlash15Test.cs deleted file mode 100644 index 2c762d82c6..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash15Test.cs +++ /dev/null @@ -1,1186 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash15Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash15Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (!keys.Contains(key)) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash15 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash15 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash15 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash15 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash15 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(true, false, false)] - [InlineData(true, true, false)] - [InlineData(true, true, true)] - public void CheckCrystalRandomSkillState(bool forceClear, bool skillStateExist, bool hasCrystalSkill) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = forceClear ? 400 : 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (hasCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - var action = new HackAndSlash15 - { - costumes = forceClear ? costumes : new List(), - equipments = forceClear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - stageBuffId = skillState?.SkillIds - .OrderBy(key => _tableSheets.CrystalRandomBuffSheet[key].Rank) - .FirstOrDefault(), - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var simulator = new StageSimulatorV1( - new TestRandom(ctx.Random.Seed), - previousAvatarState, - new List(), - worldId, - stageId, - _tableSheets.GetStageSimulatorSheetsV1(), - _tableSheets.CostumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080); - simulator.Simulate(1); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - Assert.Equal( - hasCrystalSkill - ? _tableSheets.CrystalStageBuffGachaSheet[stageId].MaxStar - : log.clearedWaveNumber, - nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash16Test.cs b/.Lib9c.Tests/Action/HackAndSlash16Test.cs deleted file mode 100644 index e9d1f46d48..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash16Test.cs +++ /dev/null @@ -1,1241 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.Skill; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash16Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash16Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (!keys.Contains(key)) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash16 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 3, - AvatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 2, - AvatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(5, 2)] - [InlineData(120, 25)] - public void ExecuteThrowNotEnoughActionPointException(int ap, int playCount = 1) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = ap, - }; - - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - PlayCount = playCount, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash16 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash16 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowPlayCountIsZeroException() - { - for (var playCount = -10; playCount <= 0; playCount++) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash16 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - PlayCount = playCount, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash16 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(true, false, false)] - [InlineData(true, true, false)] - [InlineData(true, true, true)] - public void CheckCrystalRandomSkillState(bool forceClear, bool skillStateExist, bool hasCrystalSkill) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = forceClear ? 400 : 3; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (hasCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - var action = new HackAndSlash16 - { - Costumes = forceClear ? costumes : new List(), - Equipments = forceClear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = skillState?.SkillIds - .OrderBy(key => _tableSheets.CrystalRandomBuffSheet[key].Rank) - .FirstOrDefault(), - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var contextRandom = new TestRandom(ctx.Random.Seed); - var simulator = new StageSimulatorV2( - contextRandom, - previousAvatarState, - new List(), - new List(), - worldId, - stageId, - _tableSheets.StageSheet[stageId], - _tableSheets.StageWaveSheet[stageId], - false, - StageRewardExpHelper.GetExp(previousAvatarState.level, stageId), - _tableSheets.GetSimulatorSheetsV1(), - _tableSheets.EnemySkillSheet, - _tableSheets.CostumeStatSheet, - StageSimulatorV3.GetWaveRewards( - contextRandom, - _tableSheets.StageSheet[stageId], - _tableSheets.MaterialItemSheet)); - simulator.Simulate(); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - Assert.Equal( - hasCrystalSkill - ? _tableSheets.CrystalStageBuffGachaSheet[stageId].MaxStar - : log.clearedWaveNumber, - nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash17Test.cs b/.Lib9c.Tests/Action/HackAndSlash17Test.cs deleted file mode 100644 index be49499dcd..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash17Test.cs +++ /dev/null @@ -1,1241 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.Skill; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash17Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash17Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (!keys.Contains(key)) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash17 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 3, - AvatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 2, - AvatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(5, 2)] - [InlineData(120, 25)] - public void ExecuteThrowNotEnoughActionPointException(int ap, int playCount = 1) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = ap, - }; - - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - PlayCount = playCount, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash17 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash17 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowPlayCountIsZeroException() - { - for (var playCount = -10; playCount <= 0; playCount++) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash17 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - PlayCount = playCount, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash17 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(true, false, false)] - [InlineData(true, true, false)] - [InlineData(true, true, true)] - public void CheckCrystalRandomSkillState(bool forceClear, bool skillStateExist, bool hasCrystalSkill) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = forceClear ? 400 : 3; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (hasCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - var action = new HackAndSlash17 - { - Costumes = forceClear ? costumes : new List(), - Equipments = forceClear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = skillState?.SkillIds - .OrderBy(key => _tableSheets.CrystalRandomBuffSheet[key].Rank) - .FirstOrDefault(), - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var contextRandom = new TestRandom(ctx.Random.Seed); - var simulator = new StageSimulatorV2( - contextRandom, - previousAvatarState, - new List(), - new List(), - worldId, - stageId, - _tableSheets.StageSheet[stageId], - _tableSheets.StageWaveSheet[stageId], - false, - StageRewardExpHelper.GetExp(previousAvatarState.level, stageId), - _tableSheets.GetSimulatorSheetsV1(), - _tableSheets.EnemySkillSheet, - _tableSheets.CostumeStatSheet, - StageSimulatorV3.GetWaveRewards( - contextRandom, - _tableSheets.StageSheet[stageId], - _tableSheets.MaterialItemSheet)); - simulator.Simulate(); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - Assert.Equal( - hasCrystalSkill - ? _tableSheets.CrystalStageBuffGachaSheet[stageId].MaxStar - : log.clearedWaveNumber, - nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash18Test.cs b/.Lib9c.Tests/Action/HackAndSlash18Test.cs deleted file mode 100644 index 688a2d3bbc..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash18Test.cs +++ /dev/null @@ -1,1433 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.Skill; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash18Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash18Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _sheets.Remove(nameof(RuneOptionSheet)); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - _weeklyArenaState = new WeeklyArenaState(0); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash18 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 3, - AvatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 2, - AvatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(5, 2)] - [InlineData(120, 25)] - public void ExecuteThrowNotEnoughActionPointException(int ap, int playCount = 1) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = ap, - }; - - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - PlayCount = playCount, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash18 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash18 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowPlayCountIsZeroException() - { - for (var playCount = -10; playCount <= 0; playCount++) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - PlayCount = playCount, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - } - - [Fact] - public void Execute_V100291() - { - var initialState = _initialState; - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (keys.Contains(key)) - { - initialState = initialState.SetState(Addresses.TableSheet.Derive(key), null!); - } - } - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var costumes = new List(); - IRandom random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - initialState = initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - initialState = initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(1) - ); - - foreach (var key in keys) - { - Assert.Null(initialState.GetState(Addresses.GetSheetAddress(key))); - } - - Assert.NotNull(initialState.GetState(Addresses.GetSheetAddress())); - - var action = new HackAndSlash18 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash18 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false, false)] - [InlineData(false, true, true, false)] - [InlineData(false, true, true, true)] - [InlineData(false, true, false, false)] - [InlineData(true, false, false, false)] - [InlineData(true, true, false, false)] - [InlineData(true, true, true, false)] - [InlineData(true, true, true, true)] - public void CheckCrystalRandomSkillState( - bool forceClear, - bool skillStateExist, - bool useCrystalSkill, - bool setSkillByArgument) - { - const int worldId = 1; - const int stageId = 10; - const int clearedStageId = 9; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = forceClear ? 400 : 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (useCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - skillState.Update(_tableSheets.CrystalRandomBuffSheet - .Select(pair => pair.Value.Id).ToList()); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - int? stageBuffId = null; - if (useCrystalSkill) - { - stageBuffId = skillState?.GetHighestRankSkill(_tableSheets.CrystalRandomBuffSheet); - Assert.NotNull(stageBuffId); - } - - if (forceClear) - { - previousAvatarState.EquipItems(costumes.Concat(equipments.Select(e => e.ItemId))); - } - - var action = new HackAndSlash18 - { - Costumes = forceClear ? costumes : new List(), - Equipments = forceClear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = setSkillByArgument - ? stageBuffId - : null, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var skillsOnWaveStart = new List(); - if (useCrystalSkill) - { - var skill = _tableSheets - .SkillSheet - .FirstOrDefault(pair => pair.Key == _tableSheets - .CrystalRandomBuffSheet[stageBuffId.Value].SkillId); - if (skill.Value != null) - { - skillsOnWaveStart.Add(SkillFactory.GetV1(skill.Value, default, 100)); - } - } - - var contextRandom = new TestRandom(ctx.Random.Seed); - var simulator = new StageSimulatorV2( - contextRandom, - previousAvatarState, - new List(), - skillsOnWaveStart, - worldId, - stageId, - _tableSheets.StageSheet[stageId], - _tableSheets.StageWaveSheet[stageId], - false, - StageRewardExpHelper.GetExp(previousAvatarState.level, stageId), - _tableSheets.GetSimulatorSheetsV1(), - _tableSheets.EnemySkillSheet, - _tableSheets.CostumeStatSheet, - StageSimulatorV2.GetWaveRewards( - contextRandom, - _tableSheets.StageSheet[stageId], - _tableSheets.MaterialItemSheet)); - simulator.Simulate(); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - skillState?.Update(log.clearedWaveNumber, _tableSheets.CrystalStageBuffGachaSheet); - Assert.Equal(skillState?.StarCount ?? log.clearedWaveNumber, nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - [Theory] - [InlineData(1, 24)] - [InlineData(2, 24)] - [InlineData(3, 30)] - [InlineData(4, 30)] - [InlineData(5, 40)] - public void CheckUsedApByStaking(int level, int playCount) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 120; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == level)?.RequiredGold ?? 0; - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()) - .SetState(stakeStateAddress, stakeState.SerializeV2()) - .SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize())) - .MintAsset(context, stakeStateAddress, requiredGold * _initialState.GetGoldCurrency()); - - var expectedAp = previousAvatarState.actionPoint - - _tableSheets.StakeActionPointCoefficientSheet.GetActionPointByStaking( - _tableSheets.StageSheet[stageId].CostAP, playCount, level); - var action = new HackAndSlash18 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = null, - PlayCount = playCount, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedAp, nextAvatar.actionPoint); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash19Test.cs b/.Lib9c.Tests/Action/HackAndSlash19Test.cs deleted file mode 100644 index d174fd9a55..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash19Test.cs +++ /dev/null @@ -1,1357 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.Skill; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash19Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash19Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - _weeklyArenaState = new WeeklyArenaState(0); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash19 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 3, - AvatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 2, - AvatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - RuneInfos = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - RuneInfos = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(5, 2)] - [InlineData(120, 25)] - public void ExecuteThrowNotEnoughActionPointException(int ap, int playCount = 1) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = ap, - }; - - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - PlayCount = playCount, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash19 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash19 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowPlayCountIsZeroException() - { - for (var playCount = -10; playCount <= 0; playCount++) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - PlayCount = playCount, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash19 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false, false)] - [InlineData(false, true, true, false)] - [InlineData(false, true, true, true)] - [InlineData(false, true, false, false)] - [InlineData(true, false, false, false)] - [InlineData(true, true, false, false)] - [InlineData(true, true, true, false)] - [InlineData(true, true, true, true)] - public void CheckCrystalRandomSkillState( - bool clear, - bool skillStateExist, - bool useCrystalSkill, - bool setSkillByArgument) - { - const int worldId = 1; - const int stageId = 10; - const int clearedStageId = 9; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = clear ? 400 : 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (useCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - skillState.Update(_tableSheets.CrystalRandomBuffSheet - .Select(pair => pair.Value.Id).ToList()); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - int? stageBuffId = null; - if (useCrystalSkill) - { - stageBuffId = skillState?.GetHighestRankSkill(_tableSheets.CrystalRandomBuffSheet); - Assert.NotNull(stageBuffId); - } - - if (clear) - { - previousAvatarState.EquipItems(costumes.Concat(equipments.Select(e => e.ItemId))); - } - - var action = new HackAndSlash19 - { - Costumes = clear ? costumes : new List(), - Equipments = clear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = setSkillByArgument - ? stageBuffId - : null, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var skillsOnWaveStart = new List(); - if (useCrystalSkill) - { - var skill = _tableSheets - .SkillSheet - .FirstOrDefault(pair => pair.Key == _tableSheets - .CrystalRandomBuffSheet[stageBuffId.Value].SkillId); - if (skill.Value != null) - { - skillsOnWaveStart.Add(SkillFactory.GetV1(skill.Value, default, 100)); - } - } - - var contextRandom = new TestRandom(ctx.Random.Seed); - var simulator = new StageSimulatorV3( - contextRandom, - previousAvatarState, - new List(), - null, - skillsOnWaveStart, - worldId, - stageId, - _tableSheets.StageSheet[stageId], - _tableSheets.StageWaveSheet[stageId], - false, - StageRewardExpHelper.GetExp(previousAvatarState.level, stageId), - _tableSheets.GetSimulatorSheets(), - _tableSheets.EnemySkillSheet, - _tableSheets.CostumeStatSheet, - StageSimulatorV3.GetWaveRewards( - contextRandom, - _tableSheets.StageSheet[stageId], - _tableSheets.MaterialItemSheet)); - simulator.Simulate(); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - skillState?.Update(log.clearedWaveNumber, _tableSheets.CrystalStageBuffGachaSheet); - Assert.Equal(skillState?.StarCount ?? log.clearedWaveNumber, nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - [Theory] - [InlineData(1, 24)] - [InlineData(2, 24)] - [InlineData(3, 30)] - [InlineData(4, 30)] - [InlineData(5, 40)] - public void CheckUsedApByStaking(int level, int playCount) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 120; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == level)?.RequiredGold ?? 0; - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()) - .SetState(stakeStateAddress, stakeState.SerializeV2()) - .SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize())) - .MintAsset(context, stakeStateAddress, requiredGold * _initialState.GetGoldCurrency()); - - var expectedAp = previousAvatarState.actionPoint - - _tableSheets.StakeActionPointCoefficientSheet.GetActionPointByStaking( - _tableSheets.StageSheet[stageId].CostAP, playCount, level); - var action = new HackAndSlash19 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = null, - PlayCount = playCount, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedAp, nextAvatar.actionPoint); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash20Test.cs b/.Lib9c.Tests/Action/HackAndSlash20Test.cs deleted file mode 100644 index 7b1ec3aa79..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash20Test.cs +++ /dev/null @@ -1,1808 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.Rune; - using Nekoyume.Model.Skill; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash20Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash20Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - _weeklyArenaState = new WeeklyArenaState(0); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 3, - AvatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 2, - AvatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - RuneInfos = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List - { - equipment.ItemId, - }, - RuneInfos = new List(), - Foods = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(5, 2)] - [InlineData(120, 25)] - public void ExecuteThrowNotEnoughActionPointException(int ap, int playCount = 1) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = ap, - }; - - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - TotalPlayCount = playCount, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowInvalidItemCountException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - TotalPlayCount = 24, - ApStoneCount = -1, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(-1, 0)] - [InlineData(0, 1)] - [InlineData(-1, 1)] - public void ExecuteThrowPlayCountIsZeroException(int totalPlayCount, int apStoneCount) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - TotalPlayCount = totalPlayCount, - ApStoneCount = apStoneCount, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowUsageLimitExceedException() - { - var state = _initialState; - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarState.address, - TotalPlayCount = 1, - ApStoneCount = 11, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughMaterialException() - { - var state = _initialState; - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarState.address, - TotalPlayCount = 1, - ApStoneCount = 1, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false, false)] - [InlineData(false, true, true, false)] - [InlineData(false, true, true, true)] - [InlineData(false, true, false, false)] - [InlineData(true, false, false, false)] - [InlineData(true, true, false, false)] - [InlineData(true, true, true, false)] - [InlineData(true, true, true, true)] - public void CheckCrystalRandomSkillState( - bool clear, - bool skillStateExist, - bool useCrystalSkill, - bool setSkillByArgument) - { - const int worldId = 1; - const int stageId = 10; - const int clearedStageId = 9; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = clear ? 400 : 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (useCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - skillState.Update(_tableSheets.CrystalRandomBuffSheet - .Select(pair => pair.Value.Id).ToList()); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - int? stageBuffId = null; - if (useCrystalSkill) - { - stageBuffId = skillState?.GetHighestRankSkill(_tableSheets.CrystalRandomBuffSheet); - Assert.NotNull(stageBuffId); - } - - if (clear) - { - previousAvatarState.EquipItems(costumes.Concat(equipments.Select(e => e.ItemId))); - } - - var action = new HackAndSlash20 - { - Costumes = clear ? costumes : new List(), - Equipments = clear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = setSkillByArgument - ? stageBuffId - : null, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var skillsOnWaveStart = new List(); - if (useCrystalSkill) - { - var skill = _tableSheets - .SkillSheet - .FirstOrDefault(pair => pair.Key == _tableSheets - .CrystalRandomBuffSheet[stageBuffId.Value].SkillId); - if (skill.Value != null) - { - skillsOnWaveStart.Add(SkillFactory.GetV1(skill.Value, default, 100)); - } - } - - var contextRandom = new TestRandom(ctx.Random.Seed); - var simulator = new StageSimulatorV3( - contextRandom, - previousAvatarState, - new List(), - null, - skillsOnWaveStart, - worldId, - stageId, - _tableSheets.StageSheet[stageId], - _tableSheets.StageWaveSheet[stageId], - false, - StageRewardExpHelper.GetExp(previousAvatarState.level, stageId), - _tableSheets.GetSimulatorSheets(), - _tableSheets.EnemySkillSheet, - _tableSheets.CostumeStatSheet, - StageSimulatorV3.GetWaveRewards( - contextRandom, - _tableSheets.StageSheet[stageId], - _tableSheets.MaterialItemSheet)); - simulator.Simulate(); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - skillState?.Update(log.clearedWaveNumber, _tableSheets.CrystalStageBuffGachaSheet); - Assert.Equal(skillState?.StarCount ?? log.clearedWaveNumber, nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - [Theory] - [InlineData(1, 24)] - [InlineData(2, 24)] - [InlineData(3, 30)] - [InlineData(4, 30)] - [InlineData(5, 40)] - public void CheckUsedApByStaking(int level, int playCount) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 120; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == level)?.RequiredGold ?? 0; - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()) - .SetState(stakeStateAddress, stakeState.SerializeV2()) - .SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize())) - .MintAsset(context, stakeStateAddress, requiredGold * _initialState.GetGoldCurrency()); - - var expectedAp = previousAvatarState.actionPoint - - _tableSheets.StakeActionPointCoefficientSheet.GetActionPointByStaking( - _tableSheets.StageSheet[stageId].CostAP, playCount, level); - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = null, - TotalPlayCount = playCount, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedAp, nextAvatar.actionPoint); - } - - [Theory] - [InlineData(1, 1, 24, 0)] - [InlineData(1, 1, 25, 5)] - [InlineData(2, 1, 24, 0)] - [InlineData(2, 1, 25, 5)] - [InlineData(3, 1, 30, 0)] - [InlineData(3, 1, 31, 4)] - [InlineData(4, 1, 30, 0)] - [InlineData(4, 1, 31, 4)] - [InlineData(5, 1, 40, 0)] - [InlineData(5, 1, 41, 3)] - public void CheckUsingApStoneWithStaking(int level, int apStoneCount, int totalRepeatCount, int expectedUsingAp) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - const int itemId = 303100; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = expectedUsingAp; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - var apStoneRow = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(apStoneRow); - previousAvatarState.inventory.AddItem(apStone, apStoneCount); - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == level)?.RequiredGold ?? 0; - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()) - .SetState(stakeStateAddress, stakeState.SerializeV2()) - .SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize())) - .MintAsset(context, stakeStateAddress, requiredGold * _initialState.GetGoldCurrency()); - - var itemCount = previousAvatarState.inventory.Items - .FirstOrDefault(i => i.item.Id == itemId)?.count ?? 0; - var expectedItemCount = itemCount + totalRepeatCount; - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - StageBuffId = null, - TotalPlayCount = totalRepeatCount, - ApStoneCount = apStoneCount, - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedItemCount, nextAvatar.inventory.Items.First(i => i.item.Id == itemId).count); - Assert.False(nextAvatar.inventory.HasItem(apStoneRow.Id)); - Assert.Equal(0, nextAvatar.actionPoint); - } - - [Fact] - public void ExecuteThrowInvalidRepeatPlayException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var apStoneRow = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(apStoneRow); - avatarState.inventory.AddItem(apStone); - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - var action = new HackAndSlash20 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = avatarState.address, - TotalPlayCount = 1, - ApStoneCount = 1, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteTwoRepetitions() - { - var avatarLevel = 50; - var worldId = 1; - var stageId = 20; - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = Math.Max(clearedStageId, stageId - 1); - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(0, 30001), - new RuneSlotInfo(1, 10002), - }, - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var action2 = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(0, 10002), - new RuneSlotInfo(1, 30001), - }, - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - action2.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - var avatarLevel = 50; - var worldId = 1; - var stageId = 20; - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = Math.Max(clearedStageId, stageId - 1); - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var ncgCurrency = state.GetGoldCurrency(); - state = state.MintAsset(context, _agentAddress, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatarAddress, - SlotIndex = 1, - }; - - state = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - var action = new HackAndSlash20 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash2Test.cs b/.Lib9c.Tests/Action/HackAndSlash2Test.cs deleted file mode 100644 index 82eafcb09f..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash2Test.cs +++ /dev/null @@ -1,696 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class HackAndSlash2Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash2Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 100, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false)] - [InlineData(100, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash2() - { - costumes = new List { costumeId }, - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.NotNull(action.Result); - - Assert.NotEmpty(action.Result.OfType()); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - if (contains) - { - Assert.True( - newWeeklyState[_avatarAddress].CombatPoint > - CPHelper.GetCP(nextAvatarState, _tableSheets.CharacterSheet) - ); - } - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem2(equipment); - - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem2(equipment); - avatarState.level = 0; - - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _weeklyArenaState.address, - _rankingMapAddress, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new HackAndSlash2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (HackAndSlash2)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Fact] - public void PlainValue() - { - var guid1 = new Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); - var guid2 = new Guid("936DA01F-9ABD-4d9d-80C7-02AF85C822A8"); - var action = new HackAndSlash2() - { - costumes = new List - { - 3, - 2, - 1, - }, - equipments = new List() - { - guid2, - guid1, - }, - foods = new List() - { - guid2, - guid1, - }, - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var deserialized = new HackAndSlash2(); - deserialized.LoadPlainValue(action.PlainValue); - - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash3Test.cs b/.Lib9c.Tests/Action/HackAndSlash3Test.cs deleted file mode 100644 index b837b06e71..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash3Test.cs +++ /dev/null @@ -1,167 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class HackAndSlash3Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash3Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 100, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false)] - [InlineData(100, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var equipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash3() - { - costumes = new List { costumeId }, - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.NotNull(action.Result); - - Assert.NotEmpty(action.Result.OfType()); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - if (contains) - { - Assert.True( - newWeeklyState[_avatarAddress].CombatPoint > - CPHelper.GetCP(nextAvatarState, _tableSheets.CharacterSheet) - ); - } - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash4Test.cs b/.Lib9c.Tests/Action/HackAndSlash4Test.cs deleted file mode 100644 index 8b4b83abc5..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash4Test.cs +++ /dev/null @@ -1,797 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class HackAndSlash4Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash4Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 100, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem2(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - previousAvatarState.inventory.AddItem2(weapon); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem2(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash4 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.NotNull(action.Result); - - Assert.NotEmpty(action.Result.OfType()); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - if (contains) - { - //Check for Costume CP. - Assert.True( - newWeeklyState[_avatarAddress].CombatPoint > - CPHelper.GetCP(nextAvatarState, _tableSheets.CharacterSheet) - ); - } - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash4 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash4 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem2(equipment); - - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem2(equipment); - avatarState.level = 0; - - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void Execute_Throw_ActionObsoletedException() - { - var action = new HackAndSlash4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - BlockIndex = ActionObsoleteConfig.V100080ObsoleteIndex + 100000, - })); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash5Test.cs b/.Lib9c.Tests/Action/HackAndSlash5Test.cs deleted file mode 100644 index f261024293..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash5Test.cs +++ /dev/null @@ -1,774 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class HackAndSlash5Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash5Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 100, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem2(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - previousAvatarState.inventory.AddItem2(weapon); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem2(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash5 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.NotNull(action.Result); - - Assert.NotEmpty(action.Result.OfType()); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - if (contains) - { - //Check for Costume CP. - Assert.True( - newWeeklyState[_avatarAddress].CombatPoint > - CPHelper.GetCP(nextAvatarState, _tableSheets.CharacterSheet) - ); - } - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash5 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash5 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem2(equipment); - - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem2(equipment); - avatarState.level = 0; - - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash6Test.cs b/.Lib9c.Tests/Action/HackAndSlash6Test.cs deleted file mode 100644 index 92a16d1464..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash6Test.cs +++ /dev/null @@ -1,841 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash6Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash6Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 100, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, true)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem2(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - previousAvatarState.inventory.AddItem2(weapon); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem2(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash6 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.NotNull(action.Result); - - Assert.NotEmpty(action.Result.OfType()); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - if (contains) - { - //Check for Costume CP. - Assert.True( - newWeeklyState[_avatarAddress].CombatPoint > - CPHelper.GetCP(nextAvatarState, _tableSheets.CharacterSheet) - ); - } - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - }; - - Assert.Null(action.Result); - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem2(equipment); - - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem2(equipment); - avatarState.level = 0; - - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _weeklyArenaState.address, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash7Test.cs b/.Lib9c.Tests/Action/HackAndSlash7Test.cs deleted file mode 100644 index a474ea71e7..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash7Test.cs +++ /dev/null @@ -1,904 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash7Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash7Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, true, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool contains, bool backward, bool isLock) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId - 1)); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem2(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - if (isLock) - { - orderLock = new OrderLock(Guid.NewGuid()); - } - - previousAvatarState.inventory.AddItem2(weapon, iLock: orderLock); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem2(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash7 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Equal(contains, newWeeklyState.ContainsKey(_avatarAddress)); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - if (contains) - { - //Check for Costume CP. - Assert.True( - newWeeklyState[_avatarAddress].CombatPoint > - CPHelper.GetCP(nextAvatarState, _tableSheets.CharacterSheet) - ); - } - - Assert.Equal(!isLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - - var value = nextState.GetState(_rankingMapAddress); - - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem2(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = avatarState.address, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - // First Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem2(equipment); - - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem2(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _weeklyArenaState.address, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash8Test.cs b/.Lib9c.Tests/Action/HackAndSlash8Test.cs deleted file mode 100644 index fa54c828d0..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash8Test.cs +++ /dev/null @@ -1,885 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash8Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash8Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - if (isLock) - { - orderLock = new OrderLock(Guid.NewGuid()); - } - - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash8 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - - var value = nextState.GetState(_rankingMapAddress); - - if (!isClearedBefore) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - worldQuestSheet = state.GetSheet(); - - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = avatarState.address, - rankingMapAddress = _rankingMapAddress, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - // First Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = default, - }; - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlash9Test.cs b/.Lib9c.Tests/Action/HackAndSlash9Test.cs deleted file mode 100644 index 8c0894e53b..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash9Test.cs +++ /dev/null @@ -1,1179 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlash9Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlash9Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, 10, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, 1, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, bool backward, bool isLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - clearedStageId = playCount > 1 ? clearedStageId + 1 : clearedStageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - List costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - List equipments = new List(); - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotWeapon) - { - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - if (isLock) - { - orderLock = new OrderLock(Guid.NewGuid()); - } - - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - } - - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterEquipmentSlotArmor) - { - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash9 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - - var value = nextState.GetState(_rankingMapAddress); - if (!isClearedBefore) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Theory] - [InlineData(4, 200, 1)] - [InlineData(4, 200, 2)] - public void Execute_With_UpdateQuestList(int worldId, int stageId, int playCount) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - var worldQuestSheetCsv = state.GetSheetCsv(); - var replaceTarget = $"{targetRow.Id},{targetRow.Goal},{targetRow.QuestRewardId}"; - var replacedWorldQuestSheetCsv = worldQuestSheetCsv.Replace(replaceTarget, $"_{string.Empty}"); - var worldQuestSheetAddress = Addresses.GetSheetAddress(); - state = state.SetState(worldQuestSheetAddress, replacedWorldQuestSheetCsv.Serialize()); - worldQuestSheet = state.GetSheet(); - - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.DoesNotContain(avatarWorldQuests, e => e.Goal == stageId); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = avatarState.address, - rankingMapAddress = _rankingMapAddress, - }; - - // First Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - Assert.DoesNotContain(avatarWorldQuests, e => e.Goal == stageId); - - // Revert WorldQuestSheet - state = state.SetState(worldQuestSheetAddress, worldQuestSheetCsv.Serialize()); - worldQuestSheet = state.GetSheet(); - Assert.Equal(avatarWorldQuests.Count + 1, worldQuestSheet.Count); - Assert.Contains(worldQuestSheet.OrderedList, e => e.Goal == stageId); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - (previousAvatarState.level - row.Value.Id) <= StageRewardExpHelper.DifferLowerLimit || - (previousAvatarState.level - row.Value.Id) > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = default, - }; - - var exec = Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }) - ); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByWorld() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 100, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidWorldException() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 2, - stageId = 51, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.False(_avatarState.worldInformation.IsStageCleared(51)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List() - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash9 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(true, 1, 15, 100)] - [InlineData(true, 2, 55, 100)] - [InlineData(true, 3, 111, 100)] - [InlineData(true, 4, 189, 100)] - [InlineData(false, 1, 15, 100)] - [InlineData(false, 2, 55, 100)] - [InlineData(false, 3, 111, 100)] - [InlineData(false, 4, 189, 100)] - public void CheckRewardItems(bool backward, int worldId, int stageId, int playCount) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - List equipments = new List(); - - var weaponId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Weapon) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var weapon = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[weaponId], - random) - as Equipment; - equipments.Add(weapon.ItemId); - OrderLock? orderLock = null; - previousAvatarState.inventory.AddItem(weapon, iLock: orderLock); - - var armorId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Armor) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var armor = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[armorId], - random) - as Equipment; - equipments.Add(armor.ItemId); - previousAvatarState.inventory.AddItem(armor); - - var beltId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Belt) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var belt = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[beltId], - random) - as Equipment; - equipments.Add(belt.ItemId); - previousAvatarState.inventory.AddItem(belt); - - var necklaceId = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == ItemSubType.Necklace) - .OrderBy(r => r.Stat.BaseValueAsInt) - .Last() - .Id; - - var necklace = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[necklaceId], - random) - as Equipment; - equipments.Add(necklace.ItemId); - previousAvatarState.inventory.AddItem(necklace); - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - var action = new HackAndSlash9 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = (min * playCount * stageRow.DropItemMin) + questSum; - var totalMax = (max * playCount * stageRow.DropItemMax) + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new HackAndSlash9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep1Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep1Test.cs deleted file mode 100644 index 2bb30efbc4..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep1Test.cs +++ /dev/null @@ -1,526 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashSweep1Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep1Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(1, 1, 1, true)] - [InlineData(1, 1, 1, false)] - [InlineData(2, 1, 30, true)] - [InlineData(2, 1, 30, false)] - [InlineData(5, 4, 199, false)] - [InlineData(5, 4, 199, true)] - [InlineData(9, 5, 250, false)] - [InlineData(9, 5, 250, true)] - public void Execute(int apStoneCount, int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), stageId), - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = - HackAndSlashSweep1.GetRewardItems(random, playCount, stageRow, _tableSheets.MaterialItemSheet); - - var action = new HackAndSlashSweep1 - { - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal(expectedRewardItems.Count(), nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep1 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep1 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep1 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_InvalidStageException() - { - var action = new HackAndSlashSweep1 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true)] - [InlineData(GameConfig.MimisbrunnrWorldId, false)] - public void Execute_InvalidWorldException(int worldId, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep1 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = 10000001, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep1 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 25, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(25, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - 25, - playCount); - - var action = new HackAndSlashSweep1 - { - avatarAddress = _avatarAddress, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 25, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(25, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - 25, - playCount); - - var action = new HackAndSlashSweep1 - { - avatarAddress = _avatarAddress, - apStoneCount = 0, - worldId = 1, - stageId = 25, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep2Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep2Test.cs deleted file mode 100644 index ad32f64a7f..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep2Test.cs +++ /dev/null @@ -1,564 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashSweep2Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep2Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(1, 1, 1, true)] - [InlineData(1, 1, 1, false)] - [InlineData(2, 1, 30, true)] - [InlineData(2, 1, 30, false)] - [InlineData(5, 4, 199, false)] - [InlineData(5, 4, 199, true)] - [InlineData(9, 5, 250, false)] - [InlineData(9, 5, 250, true)] - public void Execute(int apStoneCount, int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), stageId), - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = - HackAndSlashSweep2.GetRewardItems(random, playCount, stageRow, _tableSheets.MaterialItemSheet); - - var action = new HackAndSlashSweep2 - { - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal(expectedRewardItems.Count(), nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep2 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep2 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep2 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_StageClearedException() - { - var action = new HackAndSlashSweep2 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_InvalidStageException(bool backward) - { - var action = new HackAndSlashSweep2 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - var worldSheet = _initialState.GetSheet(); - var worldUnlockSheet = _initialState.GetSheet(); - - _avatarState.worldInformation.ClearStage(1, 2, 1, worldSheet, worldUnlockSheet); - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - state = _initialState - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarState.worldInformation.Serialize()); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true)] - [InlineData(GameConfig.MimisbrunnrWorldId, false)] - public void Execute_InvalidWorldException(int worldId, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep2 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = 10000001, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep2 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 25, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(25, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 25, - playCount); - - var action = new HackAndSlashSweep2 - { - avatarAddress = _avatarAddress, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 25, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(25, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 25, - playCount); - - var action = new HackAndSlashSweep2 - { - avatarAddress = _avatarAddress, - apStoneCount = 0, - worldId = 1, - stageId = 25, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep3Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep3Test.cs deleted file mode 100644 index 7cd7918cca..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep3Test.cs +++ /dev/null @@ -1,775 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class HackAndSlashSweep3Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep3Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _initialState = _initialState.SetState(arenaSheetAddress, null); - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level) - .Select(e => e.NonFungibleId).ToList(); - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - [Theory] - [InlineData(1, 1, 1, true)] - [InlineData(1, 1, 1, false)] - [InlineData(2, 1, 2, true)] - [InlineData(2, 1, 2, false)] - public void Execute(int apStoneCount, int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), stageId), - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = - HackAndSlashSweep3.GetRewardItems(random, playCount, stageRow, _tableSheets.MaterialItemSheet); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep3 - { - actionPoint = avatarState.actionPoint, - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal( - expectedRewardItems.Count(), - nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_StageClearedException() - { - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_InvalidStageException(bool backward) - { - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - var worldSheet = _initialState.GetSheet(); - var worldUnlockSheet = _initialState.GetSheet(); - - _avatarState.worldInformation.ClearStage(1, 2, 1, worldSheet, worldUnlockSheet); - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - state = _initialState - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarState.worldInformation.Serialize()); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true)] - [InlineData(GameConfig.MimisbrunnrWorldId, false)] - public void Execute_InvalidWorldException(int worldId, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = 10000001, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep3 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - - var action = new HackAndSlashSweep3 - { - equipments = equipments, - costumes = costumes, - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep3 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 999999, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_PlayCountIsZeroException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep3 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 0, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1, 24, true)] - [InlineData(1, 24, false)] - public void Execute_NotEnoughCombatPointException(int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - level = 1, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExpV1( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var action = new HackAndSlashSweep3 - { - costumes = new List(), - equipments = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var action = new HackAndSlashSweep3 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - - var state = _initialState.SetState(Addresses.GetSheetAddress(), _tableSheets.ArenaSheet.Serialize()); - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep4Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep4Test.cs deleted file mode 100644 index 1dd9884f59..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep4Test.cs +++ /dev/null @@ -1,779 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class HackAndSlashSweep4Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep4Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _initialState = _initialState.SetState(arenaSheetAddress, null); - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level) - .Select(e => e.NonFungibleId).ToList(); - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - [Theory] - [InlineData(1, 1, 1, false, true)] - [InlineData(1, 1, 1, false, false)] - [InlineData(2, 1, 2, false, true)] - [InlineData(2, 1, 2, false, false)] - [InlineData(2, 2, 51, false, true)] - [InlineData(2, 2, 51, false, false)] - [InlineData(2, 2, 52, false, true)] - [InlineData(2, 2, 52, false, false)] - [InlineData(2, 1, 1, true, true)] - [InlineData(2, 1, 1, true, false)] - [InlineData(2, 1, 2, true, true)] - [InlineData(2, 1, 2, true, false)] - [InlineData(2, 2, 51, true, true)] - [InlineData(2, 2, 51, true, false)] - [InlineData(2, 2, 52, true, true)] - [InlineData(2, 2, 52, true, false)] - public void Execute(int apStoneCount, int worldId, int stageId, bool challenge, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var prevStageId = stageId - 1; - var worldInformation = new WorldInformation( - 0, _initialState.GetSheet(), challenge ? prevStageId : stageId); - - if (challenge) - { - worldInformation.UnlockWorld(worldId, 0, _tableSheets.WorldSheet); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = worldInformation, - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = - HackAndSlashSweep4.GetRewardItems(random, playCount, stageRow, _tableSheets.MaterialItemSheet); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep4 - { - actionPoint = avatarState.actionPoint, - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal( - expectedRewardItems.Count(), - nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep4 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep4 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - [InlineData(2, 50)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep4 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 48, 1, 50, true)] - [InlineData(1, 48, 1, 50, false)] - [InlineData(1, 49, 2, 51, true)] - [InlineData(1, 49, 2, 51, false)] - public void Execute_InvalidStageException(int clearedWorldId, int clearedStageId, int worldId, int stageId, bool backward) - { - var action = new HackAndSlashSweep4 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - var worldSheet = _initialState.GetSheet(); - var worldUnlockSheet = _initialState.GetSheet(); - - _avatarState.worldInformation.ClearStage(clearedWorldId, clearedStageId, 1, worldSheet, worldUnlockSheet); - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - state = _initialState - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarState.worldInformation.Serialize()); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true)] - [InlineData(GameConfig.MimisbrunnrWorldId, false)] - public void Execute_InvalidWorldException(int worldId, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep4 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = 10000001, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep4 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - - var action = new HackAndSlashSweep4 - { - equipments = equipments, - costumes = costumes, - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep4 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 999999, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_PlayCountIsZeroException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep4 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 0, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1, 24, true)] - [InlineData(1, 24, false)] - public void Execute_NotEnoughCombatPointException(int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - level = 1, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var action = new HackAndSlashSweep4 - { - costumes = new List(), - equipments = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var action = new HackAndSlashSweep4 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 50, - }; - - var state = _initialState.SetState(Addresses.GetSheetAddress(), _tableSheets.ArenaSheet.Serialize()); - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep5Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep5Test.cs deleted file mode 100644 index 6327db82ef..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep5Test.cs +++ /dev/null @@ -1,787 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashSweep5Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep5Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level) - .Select(e => e.NonFungibleId).ToList(); - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - [Theory] - [InlineData(1, 1, 1, false, true)] - [InlineData(1, 1, 1, false, false)] - [InlineData(2, 1, 2, false, true)] - [InlineData(2, 1, 2, false, false)] - [InlineData(2, 2, 51, false, true)] - [InlineData(2, 2, 51, false, false)] - [InlineData(2, 2, 52, false, true)] - [InlineData(2, 2, 52, false, false)] - [InlineData(2, 1, 1, true, true)] - [InlineData(2, 1, 1, true, false)] - [InlineData(2, 1, 2, true, true)] - [InlineData(2, 1, 2, true, false)] - [InlineData(2, 2, 51, true, true)] - [InlineData(2, 2, 51, true, false)] - [InlineData(2, 2, 52, true, true)] - [InlineData(2, 2, 52, true, false)] - public void Execute(int apStoneCount, int worldId, int stageId, bool challenge, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var prevStageId = stageId - 1; - var worldInformation = new WorldInformation( - 0, _initialState.GetSheet(), challenge ? prevStageId : stageId); - - if (challenge) - { - worldInformation.UnlockWorld(worldId, 0, _tableSheets.WorldSheet); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = worldInformation, - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = - HackAndSlashSweep5.GetRewardItems(random, playCount, stageRow, _tableSheets.MaterialItemSheet); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep5 - { - actionPoint = avatarState.actionPoint, - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal( - expectedRewardItems.Count(), - nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep5 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep5 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - [InlineData(2, 50)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep5 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 48, 1, 50, true)] - [InlineData(1, 48, 1, 50, false)] - [InlineData(1, 49, 2, 51, true)] - [InlineData(1, 49, 2, 51, false)] - public void Execute_InvalidStageException(int clearedWorldId, int clearedStageId, int worldId, int stageId, bool backward) - { - var action = new HackAndSlashSweep5 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - var worldSheet = _initialState.GetSheet(); - var worldUnlockSheet = _initialState.GetSheet(); - - _avatarState.worldInformation.ClearStage(clearedWorldId, clearedStageId, 1, worldSheet, worldUnlockSheet); - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - if (backward) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - state = state - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarState.worldInformation.Serialize()); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true, 10000001, false)] - [InlineData(GameConfig.MimisbrunnrWorldId, false, 10000001, true)] - // Unlock CRYSTAL first. - [InlineData(2, false, 51, false)] - [InlineData(2, true, 51, false)] - public void Execute_InvalidWorldException(int worldId, bool backward, int stageId, bool unlockedIdsExist) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var action = new HackAndSlashSweep5 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep5 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - - var action = new HackAndSlashSweep5 - { - equipments = equipments, - costumes = costumes, - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep5 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 999999, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_PlayCountIsZeroException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep5 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 0, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1, 24, true)] - [InlineData(1, 24, false)] - public void Execute_NotEnoughCombatPointException(int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - level = 1, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var action = new HackAndSlashSweep5 - { - costumes = new List(), - equipments = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep7Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep7Test.cs deleted file mode 100644 index beaccdd980..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep7Test.cs +++ /dev/null @@ -1,876 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashSweep7Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep7Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _weeklyArenaState = new WeeklyArenaState(0); - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()) - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level) - .Select(e => e.NonFungibleId).ToList(); - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - [Theory] - [InlineData(1, 1, 1, false, true)] - [InlineData(1, 1, 1, false, false)] - [InlineData(2, 1, 2, false, true)] - [InlineData(2, 1, 2, false, false)] - [InlineData(2, 2, 51, false, true)] - [InlineData(2, 2, 51, false, false)] - [InlineData(2, 2, 52, false, true)] - [InlineData(2, 2, 52, false, false)] - [InlineData(2, 1, 1, true, true)] - [InlineData(2, 1, 1, true, false)] - [InlineData(2, 1, 2, true, true)] - [InlineData(2, 1, 2, true, false)] - [InlineData(2, 2, 51, true, true)] - [InlineData(2, 2, 51, true, false)] - [InlineData(2, 2, 52, true, true)] - [InlineData(2, 2, 52, true, false)] - public void Execute(int apStoneCount, int worldId, int stageId, bool challenge, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var prevStageId = stageId - 1; - var worldInformation = new WorldInformation( - 0, _initialState.GetSheet(), challenge ? prevStageId : stageId); - - if (challenge) - { - worldInformation.UnlockWorld(worldId, 0, _tableSheets.WorldSheet); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = worldInformation, - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = HackAndSlashSweep6.GetRewardItems( - random, - playCount, - stageRow, - _tableSheets.MaterialItemSheet); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep7 - { - actionPoint = avatarState.actionPoint, - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal( - expectedRewardItems.Count(), - nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep7 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep7 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - [InlineData(2, 50)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep7 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 48, 1, 50, true)] - [InlineData(1, 48, 1, 50, false)] - [InlineData(1, 49, 2, 51, true)] - [InlineData(1, 49, 2, 51, false)] - public void Execute_InvalidStageException(int clearedWorldId, int clearedStageId, int worldId, int stageId, bool backward) - { - var action = new HackAndSlashSweep7 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - var worldSheet = _initialState.GetSheet(); - var worldUnlockSheet = _initialState.GetSheet(); - - _avatarState.worldInformation.ClearStage(clearedWorldId, clearedStageId, 1, worldSheet, worldUnlockSheet); - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - if (backward) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - state = state - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarState.worldInformation.Serialize()); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true, 10000001, false)] - [InlineData(GameConfig.MimisbrunnrWorldId, false, 10000001, true)] - // Unlock CRYSTAL first. - [InlineData(2, false, 51, false)] - [InlineData(2, true, 51, false)] - public void Execute_InvalidWorldException(int worldId, bool backward, int stageId, bool unlockedIdsExist) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var action = new HackAndSlashSweep7 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep7 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - - var action = new HackAndSlashSweep7 - { - equipments = equipments, - costumes = costumes, - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep7 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 999999, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_PlayCountIsZeroException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep7 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 0, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1, 24, true)] - [InlineData(1, 24, false)] - public void Execute_NotEnoughCombatPointException(int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - level = 1, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var action = new HackAndSlashSweep7 - { - costumes = new List(), - equipments = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - [InlineData(4)] - [InlineData(5)] - public void ExecuteWithStake(int stakingLevel) - { - const int worldId = 1; - const int stageId = 1; - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 120, - level = 3, - }; - var itemRow = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(itemRow); - avatarState.inventory.AddItem(apStone); - - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == stakingLevel)?.RequiredGold ?? 0; - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(stakeStateAddress, stakeState.Serialize()) - .MintAsset(context, stakeStateAddress, requiredGold * _initialState.GetGoldCurrency()); - var stageSheet = _initialState.GetSheet(); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var apSheet = _initialState.GetSheet(); - var costAp = apSheet.GetActionPointByStaking(stageRow.CostAP, 1, stakingLevel); - var itemPlayCount = - gameConfigState.ActionPointMax / costAp * 1; - var apPlayCount = avatarState.actionPoint / costAp; - var playCount = apPlayCount + itemPlayCount; - var (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _initialState.GetSheet(), - stageId, - playCount); - - var action = new HackAndSlashSweep7 - { - costumes = new List(), - equipments = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedLevel, nextAvatar.level); - Assert.Equal(expectedExp, nextAvatar.exp); - } - else - { - throw new SheetRowNotFoundException(nameof(StageSheet), stageId); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep8Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep8Test.cs deleted file mode 100644 index 77abf5a2d6..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep8Test.cs +++ /dev/null @@ -1,888 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashSweep8Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep8Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _weeklyArenaState = new WeeklyArenaState(0); - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()) - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - public (List Equipments, List Costumes) GetDummyItems(AvatarState avatarState) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level) - .Select(e => e.NonFungibleId).ToList(); - var random = new TestRandom(); - var costumes = new List(); - if (avatarState.level >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - avatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - return (equipments, costumes); - } - - [Theory] - [InlineData(1, 1, 1, false, true)] - [InlineData(1, 1, 1, false, false)] - [InlineData(2, 1, 2, false, true)] - [InlineData(2, 1, 2, false, false)] - [InlineData(2, 2, 51, false, true)] - [InlineData(2, 2, 51, false, false)] - [InlineData(2, 2, 52, false, true)] - [InlineData(2, 2, 52, false, false)] - [InlineData(2, 1, 1, true, true)] - [InlineData(2, 1, 1, true, false)] - [InlineData(2, 1, 2, true, true)] - [InlineData(2, 1, 2, true, false)] - [InlineData(2, 2, 51, true, true)] - [InlineData(2, 2, 51, true, false)] - [InlineData(2, 2, 52, true, true)] - [InlineData(2, 2, 52, true, false)] - public void Execute(int apStoneCount, int worldId, int stageId, bool challenge, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var prevStageId = stageId - 1; - var worldInformation = new WorldInformation( - 0, _initialState.GetSheet(), challenge ? prevStageId : stageId); - - if (challenge) - { - worldInformation.UnlockWorld(worldId, 0, _tableSheets.WorldSheet); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = worldInformation, - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, apStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var random = new TestRandom(_random.Seed); - var expectedRewardItems = HackAndSlashSweep6.GetRewardItems( - random, - playCount, - stageRow, - _tableSheets.MaterialItemSheet); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep8 - { - actionPoint = avatarState.actionPoint, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = _random, - }); - - var nextAvatarState = state.GetAvatarStateV2(_avatarAddress); - - Assert.Equal(expectedLevel, nextAvatarState.level); - Assert.Equal(expectedExp, nextAvatarState.exp); - Assert.Equal( - expectedRewardItems.Count(), - nextAvatarState.inventory.Items.Sum(x => x.count)); - foreach (var i in nextAvatarState.inventory.Items) - { - nextAvatarState.inventory.TryGetItem(i.item.Id, out var item); - Assert.Equal(expectedRewardItems.Count(x => x.Id == i.item.Id), item.count); - } - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_FailedLoadStateException(bool backward) - { - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 999)] - [InlineData(2, 50)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(1, 48, 1, 50, true)] - [InlineData(1, 48, 1, 50, false)] - [InlineData(1, 49, 2, 51, true)] - [InlineData(1, 49, 2, 51, false)] - public void Execute_InvalidStageException(int clearedWorldId, int clearedStageId, int worldId, int stageId, bool backward) - { - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - var worldSheet = _initialState.GetSheet(); - var worldUnlockSheet = _initialState.GetSheet(); - - _avatarState.worldInformation.ClearStage(clearedWorldId, clearedStageId, 1, worldSheet, worldUnlockSheet); - - var state = _initialState.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - if (backward) - { - state = state.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - state = state - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarState.worldInformation.Serialize()); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(GameConfig.MimisbrunnrWorldId, true, 10000001, false)] - [InlineData(GameConfig.MimisbrunnrWorldId, false, 10000001, true)] - // Unlock CRYSTAL first. - [InlineData(2, false, 51, false)] - [InlineData(2, true, 51, false)] - public void Execute_InvalidWorldException(int worldId, bool backward, int stageId, bool unlockedIdsExist) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 10000001), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(99, true)] - [InlineData(99, false)] - public void Execute_UsageLimitExceedException(int apStoneCount, bool backward) - { - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Theory] - [InlineData(3, 2, true)] - [InlineData(7, 5, false)] - public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - }; - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(row); - avatarState.inventory.AddItem(apStone, holdingApStoneCount); - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * useApStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - - var action = new HackAndSlashSweep8 - { - equipments = equipments, - costumes = costumes, - runeInfos = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = useApStoneCount, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_NotEnoughActionPointException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep8 - { - runeInfos = new List(), - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 999999, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_PlayCountIsZeroException(bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - level = 400, - actionPoint = 0, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(2, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - 2, - playCount); - - var (equipments, costumes) = GetDummyItems(avatarState); - var action = new HackAndSlashSweep8 - { - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - actionPoint = 0, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1, 24, true)] - [InlineData(1, 24, false)] - public void Execute_NotEnoughCombatPointException(int worldId, int stageId, bool backward) - { - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 0, - level = 1, - }; - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var stageSheet = _initialState.GetSheet(); - var (expectedLevel, expectedExp) = (0, 0L); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var itemPlayCount = - gameConfigState.ActionPointMax / stageRow.CostAP * 1; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apPlayCount + itemPlayCount; - (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _tableSheets.CharacterLevelSheet, - stageId, - playCount); - - var action = new HackAndSlashSweep8 - { - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - [InlineData(3)] - [InlineData(4)] - [InlineData(5)] - public void ExecuteWithStake(int stakingLevel) - { - const int worldId = 1; - const int stageId = 1; - var gameConfigState = _initialState.GetGameConfigState(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _initialState.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress) - { - worldInformation = - new WorldInformation(0, _initialState.GetSheet(), 25), - actionPoint = 120, - level = 3, - }; - var itemRow = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(itemRow); - avatarState.inventory.AddItem(apStone); - - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == stakingLevel)?.RequiredGold ?? 0; - var context = new ActionContext(); - var state = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(stakeStateAddress, stakeState.Serialize()) - .MintAsset(context, stakeStateAddress, requiredGold * _initialState.GetGoldCurrency()); - var stageSheet = _initialState.GetSheet(); - if (stageSheet.TryGetValue(stageId, out var stageRow)) - { - var apSheet = _initialState.GetSheet(); - var costAp = apSheet.GetActionPointByStaking(stageRow.CostAP, 1, stakingLevel); - var itemPlayCount = - gameConfigState.ActionPointMax / costAp * 1; - var apPlayCount = avatarState.actionPoint / costAp; - var playCount = apPlayCount + itemPlayCount; - var (expectedLevel, expectedExp) = avatarState.GetLevelAndExp( - _initialState.GetSheet(), - stageId, - playCount); - - var action = new HackAndSlashSweep8 - { - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedLevel, nextAvatar.level); - Assert.Equal(expectedExp, nextAvatar.exp); - } - else - { - throw new SheetRowNotFoundException(nameof(StageSheet), stageId); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashTest14.cs b/.Lib9c.Tests/Action/HackAndSlashTest14.cs deleted file mode 100644 index c3ad3e4660..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashTest14.cs +++ /dev/null @@ -1,1177 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Quest; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashTest14 - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - - private readonly Address _inventoryAddress; - private readonly Address _worldInformationAddress; - private readonly Address _questListAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public HackAndSlashTest14() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 100, - }; - _inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - _worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - _questListAddress = _avatarAddress.Derive(LegacyQuestListKey); - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_inventoryAddress, _avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, _avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, _avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - foreach (var address in _avatarState.combinationSlotAddresses) - { - var slotState = new CombinationSlotState( - address, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - _initialState = _initialState.SetState(address, slotState.Serialize()); - } - } - - [Theory] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 2, false, true, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, true)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, true)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, false, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, false, true, false)] - [InlineData(GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot, 1, 1, true, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, false, false, false)] - [InlineData(200, 1, GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, true, false, false)] - public void Execute(int avatarLevel, int worldId, int stageId, bool backward, bool isWeaponLock, bool isClearedBefore) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = avatarLevel; - var clearedStageId = _tableSheets.StageSheet.First?.Id ?? 0; - clearedStageId = isClearedBefore ? Math.Max(clearedStageId, stageId - 1) : stageId - 1; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - IRandom random = new TestRandom(); - if (avatarLevel >= GameConfig.RequireCharacterLevel.CharacterFullCostumeSlot) - { - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - } - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - var iLock = equipment.ItemSubType == ItemSubType.Weapon && isWeaponLock - ? new OrderLock(Guid.NewGuid()) - : (ILock)null; - previousAvatarState.inventory.AddItem(equipment, iLock: iLock); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var action = new HackAndSlash14 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Equal(!isWeaponLock, nextAvatarState.inventory.Equipments.OfType().Any(w => w.equipped)); - } - - [Theory] - [InlineData(4, 200)] - public void Execute_With_UpdateQuestList(int worldId, int stageId) - { - var state = _initialState; - - // Remove stageId from WorldQuestSheet - var worldQuestSheet = state.GetSheet(); - var targetRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - Assert.NotNull(targetRow); - // Update new AvatarState - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - state.GetAvatarSheets(), - state.GetGameConfigState(), - _rankingMapAddress) - { - level = 400, - exp = state.GetSheet().OrderedList.First(e => e.Level == 400).Exp, - worldInformation = new WorldInformation(0, state.GetSheet(), stageId), - }; - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - state = state - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()) - .SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(_questListAddress, avatarState.questList.Serialize()); - Assert.Equal(400, avatarState.level); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldId)); - Assert.True(avatarState.worldInformation.IsStageCleared(stageId)); - - var avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Empty(avatarState.questList.completedQuestIds); - Assert.Equal(equipments.Count, avatarState.inventory.Items.Count); - - // HackAndSlash - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = avatarState.address, - }; - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.DoesNotContain(avatarWorldQuests, e => e.Complete); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - avatarState = state.GetAvatarStateV2(avatarState.address); - avatarWorldQuests = avatarState.questList.OfType().ToList(); - Assert.Equal(worldQuestSheet.Count, avatarWorldQuests.Count); - Assert.Single(avatarWorldQuests, e => e.Goal == stageId && e.Complete); - } - - [Fact] - public void MaxLevelTest() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - var requiredExp = expRow.ExpNeed; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp + requiredExp - 1; - - var stageId = 0; - try - { - stageId = _tableSheets.StageSheet - .FirstOrDefault(row => - previousAvatarState.level - row.Value.Id <= StageRewardExpHelper.DifferLowerLimit || - previousAvatarState.level - row.Value.Id > StageRewardExpHelper.DifferUpperLimit) - .Value.Id; - } - catch - { - // There is no stage that a avatar state which level is max can earning exp. - return; - } - - var worldRow = _tableSheets.WorldSheet - .FirstOrDefault(row => stageId >= row.Value.StageBegin && - stageId <= row.Value.StageEnd); - var worldId = worldRow.Value.Id; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - Math.Max(_tableSheets.StageSheet.First?.Id ?? 1, stageId)); - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(maxLevelExp + requiredExp - 1, nextAvatarState.exp); - Assert.Equal(previousAvatarState.level, nextAvatarState.level); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_inventoryAddress, previousAvatarState.inventory.Serialize()); - - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = backward ? new MockStateDelta() : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), null!) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), null!) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), null!); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(Addresses.TableSheet.Derive(nameof(StageSheet)), "test".Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - var worldSheet = new WorldSheet(); - worldSheet.Set("test"); - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation(0, worldSheet, false), - }; - state = state.SetState(_worldInformationAddress, avatarState.worldInformation.Serialize()); - - Assert.False(avatarState.worldInformation.IsStageCleared(0)); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - // Try challenge Mimisbrunnr. - [InlineData(GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, false)] - // Unlock CRYSTAL first. - [InlineData(2, 51, false)] - [InlineData(2, 51, true)] - public void Execute_Throw_InvalidWorldException(int worldId, int stageId, bool unlockedIdsExist) - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - IAccountStateDelta state = _initialState; - if (unlockedIdsExist) - { - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 3, - avatarAddress = _avatarAddress, - }; - - var avatarState = new AvatarState(_avatarState); - avatarState.worldInformation.ClearStage( - 1, - 1, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - - avatarState.worldInformation.TryGetWorld(1, out var world); - - Assert.True(world.IsStageCleared); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(1)); - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 2, - avatarAddress = _avatarAddress, - }; - - _avatarState.worldInformation.TryGetWorld(1, out var world); - Assert.False(world.IsStageCleared); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowInvalidEquipmentException(ItemSubType itemSubType) - { - var avatarState = new AvatarState(_avatarState); - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 100); - avatarState.inventory.AddItem(equipment); - - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void ExecuteThrowEquipmentSlotUnlockException(ItemSubType itemSubType) - { - var state = _initialState; - var avatarState = new AvatarState(_avatarState) - { - level = 0, - }; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var equipRow = _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == itemSubType); - var equipment = ItemFactory.CreateItemUsable(equipRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - state = state.SetState(_inventoryAddress, avatarState.inventory.Serialize()); - - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List - { - equipment.ItemId, - }, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPointException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 0, - }; - - var action = new HackAndSlash14 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteWithoutPlayCount() - { - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.level = 1; - var clearedStageId = 0; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var equipments = new List(); - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - - var action = new HackAndSlash14 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(1)); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void Execute_Throw_NotEnoughAvatarLevelException(int avatarLevel) - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = avatarLevel, - }; - - var state = _initialState; - var itemIds = new[] { GameConfig.DefaultAvatarWeaponId, 40100000 }; - foreach (var itemId in itemIds) - { - foreach (var requirementRow in _tableSheets.ItemRequirementSheet.OrderedList - .Where(e => e.ItemId >= itemId && e.Level > avatarState.level) - .Take(3)) - { - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - equipments.Add(((INonFungibleItem)equipment).NonFungibleId); - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new HackAndSlash14 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = avatarState.address, - }; - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - - SerializeException(exec); - } - } - } - - [Theory] - [InlineData(true, 1, 15)] - [InlineData(true, 2, 55)] - [InlineData(true, 3, 111)] - [InlineData(true, 4, 189)] - [InlineData(false, 1, 15)] - [InlineData(false, 2, 55)] - [InlineData(false, 3, 111)] - [InlineData(false, 4, 189)] - public void CheckRewardItems(bool backward, int worldId, int stageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - } - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - Enumerable.Range(1, worldId).ToList().Select(i => i.Serialize()).Serialize() - ); - - var action = new HackAndSlash14 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - var worldQuestSheet = state.GetSheet(); - var questRow = worldQuestSheet.OrderedList.FirstOrDefault(e => e.Goal == stageId); - var questRewardSheet = state.GetSheet(); - var rewardIds = questRewardSheet.First(x => x.Key == questRow.QuestRewardId).Value - .RewardIds; - var questItemRewardSheet = state.GetSheet(); - var materialItemSheet = state.GetSheet(); - var sortedMaterialItemSheet = materialItemSheet - .Where(x => - x.Value.ItemSubType == ItemSubType.EquipmentMaterial || - x.Value.ItemSubType == ItemSubType.MonsterPart).ToList(); - - var selectedIdn = new Dictionary(); - foreach (var row in questItemRewardSheet) - { - if (sortedMaterialItemSheet.Exists(x => x.Key.Equals(row.ItemId))) - { - selectedIdn.Add(row.Key, row.Count); - } - } - - var questSum = rewardIds.Where(rewardId => selectedIdn.ContainsKey(rewardId)) - .Sum(rewardId => selectedIdn[rewardId]); - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * stageRow.DropItemMin + questSum; - var totalMax = max * stageRow.DropItemMax + questSum; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(false, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(true, false, false)] - [InlineData(true, true, false)] - [InlineData(true, true, true)] - public void CheckCrystalRandomSkillState(bool forceClear, bool skillStateExist, bool hasCrystalSkill) - { - const int worldId = 1; - const int stageId = 5; - const int clearedStageId = 4; - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = forceClear ? 400 : 3; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.First(); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, default, 0); - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = mailEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - previousAvatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - previousAvatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - previousAvatarState.questList.Serialize()); - - state = state.SetState( - _avatarAddress.Derive("world_ids"), - List.Empty.Add(worldId.Serialize()) - ); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(_avatarAddress); - CrystalRandomSkillState skillState = null; - if (skillStateExist) - { - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - if (hasCrystalSkill) - { - skillState.Update(int.MaxValue, _tableSheets.CrystalStageBuffGachaSheet); - } - - state = state.SetState(skillStateAddress, skillState.Serialize()); - } - - var action = new HackAndSlash14 - { - costumes = forceClear ? costumes : new List(), - equipments = forceClear - ? equipments.Select(e => e.NonFungibleId).ToList() - : new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - stageBuffId = skillState?.SkillIds - .OrderBy(key => _tableSheets.CrystalRandomBuffSheet[key].Rank) - .FirstOrDefault(), - }; - - var ctx = new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var simulator = new StageSimulatorV1( - new TestRandom(ctx.Random.Seed), - previousAvatarState, - new List(), - worldId, - stageId, - _tableSheets.GetStageSimulatorSheetsV1(), - _tableSheets.CostumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080); - simulator.Simulate(1); - var log = simulator.Log; - var skillStateIValue = - nextState.GetState(skillStateAddress); - var serialized = skillStateIValue as List; - Assert.NotNull(serialized); - var nextSkillState = new CrystalRandomSkillState(skillStateAddress, serialized); - Assert.Equal(skillStateAddress, nextSkillState.Address); - - if (log.IsClear) - { - Assert.Equal(stageId + 1, nextSkillState.StageId); - Assert.Equal(0, nextSkillState.StarCount); - } - else - { - Assert.Equal(stageId, nextSkillState.StageId); - Assert.Equal( - hasCrystalSkill - ? _tableSheets.CrystalStageBuffGachaSheet[stageId].MaxStar - : log.clearedWaveNumber, - nextSkillState.StarCount); - } - - Assert.Empty(nextSkillState.SkillIds); - } - - private static void SerializeException(Exception exec) - where T : Exception - { - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, exec); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (T)formatter.Deserialize(ms); - - Assert.Equal(exec.Message, deserialized.Message); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement0Test.cs b/.Lib9c.Tests/Action/ItemEnhancement0Test.cs deleted file mode 100644 index 1a8e94a57b..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement0Test.cs +++ /dev/null @@ -1,521 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Xunit; - - public class ItemEnhancement0Test - { - private readonly IRandom _random; - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement0Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000)] - [InlineData(3, 4, 990)] - public void Execute(int level, int expectedLevel, int expectedGold) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = default, - materialIds = new[] { materialId }, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new ItemEnhancement0() - { - itemId = default, - materialIds = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new ItemEnhancement0() - { - itemId = default, - materialIds = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowRequiredBlockIndexException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 100, 1); - - _avatarState.inventory.AddItem2(equipment, count: 1); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = equipment.ItemId, - materialIds = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidCastException() - { - var row = _tableSheets.ConsumableItemSheet.Values.First(r => r.Grade == 1); - var consumable = (Consumable)ItemFactory.CreateItemUsable(row, default, 0, 1); - - _avatarState.inventory.AddItem2(consumable, count: 1); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = consumable.ItemId, - materialIds = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowCombinationSlotUnlockException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, 1); - - _avatarState.inventory.AddItem2(equipment, count: 1); - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 100).Serialize()); - - var action = new ItemEnhancement0() - { - itemId = equipment.ItemId, - materialIds = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowEquipmentLevelExceededException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, 10); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = equipment.ItemId, - materialIds = new[] { materialId }, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughMaterialException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0); - var materialId = Guid.NewGuid(); - - _avatarState.inventory.AddItem2(equipment); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = equipment.ItemId, - materialIds = new[] { materialId }, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowDuplicateMaterialException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, 0); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0); - - _avatarState.inventory.AddItem2(equipment); - _avatarState.inventory.AddItem2(material); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = equipment.ItemId, - materialIds = new[] { materialId, materialId }, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1, - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1 - )] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1, - "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", - ItemSubType.Armor, - 1, - 1 - )] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1, - "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", - ItemSubType.Weapon, - 2, - 1 - )] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 2, - "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", - ItemSubType.Weapon, - 1, - 1 - )] - public void ExecuteThrowInvalidMaterialException( - string equipmentGuid, - ItemSubType equipmentSubType, - int equipmentGrade, - int equipmentLevel, - string materialGuid, - ItemSubType materialSubType, - int materialGrade, - int materialLevel - ) - { - var equipmentRow = _tableSheets.EquipmentItemSheet.Values.First(r => - r.Grade == equipmentGrade && r.ItemSubType == equipmentSubType); - var materialRow = _tableSheets.EquipmentItemSheet.Values.First(r => - r.Grade == materialGrade && r.ItemSubType == materialSubType); - var equipment = (Equipment)ItemFactory.CreateItemUsable(equipmentRow, new Guid(equipmentGuid), 0, equipmentLevel); - var materialId = new Guid(materialGuid); - var material = (Equipment)ItemFactory.CreateItemUsable(materialRow, materialId, 0, materialLevel); - - _avatarState.inventory.AddItem2(equipment); - _avatarState.inventory.AddItem2(material); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement0() - { - itemId = equipment.ItemId, - materialIds = new[] { materialId, materialId }, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Deterministic() - { - var guid1 = new Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); - var guid2 = new Guid("936DA01F-9ABD-4d9d-80C7-02AF85C822A8"); - - var action = new ItemEnhancement0() - { - itemId = default, - materialIds = new[] { guid1, guid2 }, - avatarAddress = default, - slotIndex = 0, - }; - - var action2 = new ItemEnhancement0(); - action2.LoadPlainValue(action.PlainValue); - action2.materialIds = new[] { guid2, guid1 }; - - Assert.Equal(action.PlainValue, action2.PlainValue); - } - - [Fact] - public void ResultModelDeterministic() - { - var guid1 = new Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); - var guid2 = new Guid("936DA01F-9ABD-4d9d-80C7-02AF85C822A8"); - - var row = _tableSheets.EquipmentItemSheet.Values.First(); - var itemUsable = ItemFactory.CreateItemUsable(row, default, 0); - var result = new ItemEnhancement7.ResultModel() - { - id = default, - materialItemIdList = new[] { guid1, guid2 }, - gold = 0, - actionPoint = 0, - itemUsable = itemUsable, - }; - - var result2 = new ItemEnhancement7.ResultModel() - { - id = default, - materialItemIdList = new[] { guid2, guid1 }, - gold = 0, - actionPoint = 0, - itemUsable = itemUsable, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - - [Fact] - public void Rehearsal() - { - var agentAddress = default(Address); - var avatarAddress = agentAddress.Derive("avatar"); - var slotAddress = - avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var action = new ItemEnhancement0() - { - itemId = default, - materialIds = new[] { Guid.NewGuid() }, - avatarAddress = avatarAddress, - slotIndex = 0, - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - slotAddress, - Addresses.GoldCurrency, - Addresses.Blacksmith, - }; - - var state = new MockStateDelta() - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement2Test.cs b/.Lib9c.Tests/Action/ItemEnhancement2Test.cs deleted file mode 100644 index e1c0d66f77..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement2Test.cs +++ /dev/null @@ -1,486 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Xunit; - - public class ItemEnhancement2Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement2Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000)] - [InlineData(3, 4, 990)] - public void Execute(int level, int expectedLevel, int expectedGold) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement2() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new ItemEnhancement2() - { - itemId = default, - materialId = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new ItemEnhancement2() - { - itemId = default, - materialId = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowRequiredBlockIndexException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 100, 1); - - _avatarState.inventory.AddItem2(equipment, count: 1); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement2() - { - itemId = equipment.ItemId, - materialId = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidCastException() - { - var row = _tableSheets.ConsumableItemSheet.Values.First(r => r.Grade == 1); - var consumable = (Consumable)ItemFactory.CreateItemUsable(row, default, 0, 1); - - _avatarState.inventory.AddItem2(consumable, count: 1); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement2() - { - itemId = consumable.ItemId, - materialId = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowCombinationSlotUnlockException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, 1); - - _avatarState.inventory.AddItem2(equipment, count: 1); - - _initialState = _initialState - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 100).Serialize()); - - var action = new ItemEnhancement2() - { - itemId = equipment.ItemId, - materialId = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowEquipmentLevelExceededException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, 10); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement2() - { - itemId = equipment.ItemId, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowNotEnoughMaterialException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0); - var materialId = Guid.NewGuid(); - - _avatarState.inventory.AddItem2(equipment); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement2() - { - itemId = equipment.ItemId, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Theory] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1, - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1 - )] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1, - "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", - ItemSubType.Armor, - 1, - 1 - )] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 1, - "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", - ItemSubType.Weapon, - 2, - 1 - )] - [InlineData( - "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", - ItemSubType.Weapon, - 1, - 2, - "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", - ItemSubType.Weapon, - 1, - 1 - )] - public void ExecuteThrowInvalidMaterialException( - string equipmentGuid, - ItemSubType equipmentSubType, - int equipmentGrade, - int equipmentLevel, - string materialGuid, - ItemSubType materialSubType, - int materialGrade, - int materialLevel - ) - { - var equipmentRow = _tableSheets.EquipmentItemSheet.Values.First(r => - r.Grade == equipmentGrade && r.ItemSubType == equipmentSubType); - var materialRow = _tableSheets.EquipmentItemSheet.Values.First(r => - r.Grade == materialGrade && r.ItemSubType == materialSubType); - var equipment = (Equipment)ItemFactory.CreateItemUsable(equipmentRow, new Guid(equipmentGuid), 0, equipmentLevel); - var materialId = new Guid(materialGuid); - var material = (Equipment)ItemFactory.CreateItemUsable(materialRow, materialId, 0, materialLevel); - - _avatarState.inventory.AddItem2(equipment); - _avatarState.inventory.AddItem2(material); - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement2() - { - itemId = equipment.ItemId, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Deterministic() - { - var guid1 = new Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); - - var action = new ItemEnhancement2() - { - itemId = default, - materialId = guid1, - avatarAddress = default, - slotIndex = 0, - }; - - var action2 = new ItemEnhancement2(); - action2.LoadPlainValue(action.PlainValue); - action2.materialId = guid1; - - Assert.Equal(action.PlainValue, action2.PlainValue); - } - - [Fact] - public void ResultModelDeterministic() - { - var guid1 = new Guid("F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4"); - - var row = _tableSheets.EquipmentItemSheet.Values.First(); - var itemUsable = ItemFactory.CreateItemUsable(row, default, 0); - var result = new ItemEnhancement7.ResultModel() - { - id = default, - materialItemIdList = new[] { guid1 }, - gold = 0, - actionPoint = 0, - itemUsable = itemUsable, - }; - - var result2 = new ItemEnhancement7.ResultModel() - { - id = default, - materialItemIdList = new[] { guid1 }, - gold = 0, - actionPoint = 0, - itemUsable = itemUsable, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - - [Fact] - public void Rehearsal() - { - var agentAddress = default(Address); - var avatarAddress = agentAddress.Derive("avatar"); - var slotAddress = - avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var action = new ItemEnhancement2() - { - itemId = default, - materialId = Guid.NewGuid(), - avatarAddress = avatarAddress, - slotIndex = 0, - }; - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - - var updatedAddresses = new List
() - { - agentAddress, - avatarAddress, - slotAddress, - Addresses.GoldCurrency, - Addresses.Blacksmith, - }; - - var state = new MockStateDelta() - .SetState(GoldCurrencyState.Address, gold.Serialize()); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement3Test.cs b/.Lib9c.Tests/Action/ItemEnhancement3Test.cs deleted file mode 100644 index 72ccd55a74..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement3Test.cs +++ /dev/null @@ -1,143 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class ItemEnhancement3Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement3Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000)] - [InlineData(3, 4, 990)] - public void Execute(int level, int expectedLevel, int expectedGold) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement3() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement4Test.cs b/.Lib9c.Tests/Action/ItemEnhancement4Test.cs deleted file mode 100644 index 2033c10e85..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement4Test.cs +++ /dev/null @@ -1,154 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class ItemEnhancement4Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement4Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000)] - [InlineData(3, 4, 990)] - public void Execute(int level, int expectedLevel, int expectedGold) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement4() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var grade = resultEquipment.Grade; - var costRow = _tableSheets.EnhancementCostSheet - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == resultEquipment.level); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement7.ResultModel)slot.Result; - - Assert.Equal(costRow.Cost, slotResult.gold); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement5Test.cs b/.Lib9c.Tests/Action/ItemEnhancement5Test.cs deleted file mode 100644 index af896dc572..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement5Test.cs +++ /dev/null @@ -1,154 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class ItemEnhancement5Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement5Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000)] - [InlineData(3, 4, 990)] - public void Execute(int level, int expectedLevel, int expectedGold) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement5() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var grade = resultEquipment.Grade; - var costRow = _tableSheets.EnhancementCostSheet - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == resultEquipment.level); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement7.ResultModel)slot.Result; - - Assert.Equal(costRow.Cost, slotResult.gold); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement6Test.cs b/.Lib9c.Tests/Action/ItemEnhancement6Test.cs deleted file mode 100644 index fd1832490e..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement6Test.cs +++ /dev/null @@ -1,154 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - - public class ItemEnhancement6Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement6Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000)] - [InlineData(3, 4, 990)] - public void Execute(int level, int expectedLevel, int expectedGold) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem2(equipment, count: 1); - _avatarState.inventory.AddItem2(material, count: 1); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update2(mail); - } - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new ItemEnhancement6() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var grade = resultEquipment.Grade; - var costRow = _tableSheets.EnhancementCostSheet - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == resultEquipment.level); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement7.ResultModel)slot.Result; - - Assert.Equal(costRow.Cost, slotResult.gold); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement8Test.cs b/.Lib9c.Tests/Action/ItemEnhancement8Test.cs deleted file mode 100644 index bb874bfda5..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement8Test.cs +++ /dev/null @@ -1,211 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - using static Lib9c.SerializeKeys; - - public class ItemEnhancement8Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Address _slotAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccountStateDelta _initialState; - - public ItemEnhancement8Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var gold = new GoldCurrencyState(_currency); - _slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()) - .SetState(_slotAddress, new CombinationSlotState(_slotAddress, 0).Serialize()) - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); - - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(0, 1, 1000, true)] - [InlineData(3, 4, 990, true)] - [InlineData(0, 1, 1000, false)] - [InlineData(3, 4, 990, false)] - public void Execute(int level, int expectedLevel, int expectedGold, bool backward) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); - - _avatarState.inventory.AddItem(equipment, count: 1); - _avatarState.inventory.AddItem(material, count: 1); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - _avatarState.Update(mail); - } - - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - - var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); - - if (backward) - { - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - _initialState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), _avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), _avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), _avatarState.questList.Serialize()) - .SetState(_avatarAddress, _avatarState.SerializeV2()); - } - - var action = new ItemEnhancement8 - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - Random = _random, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(Addresses.Blacksmith, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var grade = resultEquipment.Grade; - var costRow = _tableSheets.EnhancementCostSheet - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == resultEquipment.level); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement7.ResultModel)slot.Result; - - Assert.Equal(costRow.Cost, slotResult.gold); - } - - [Fact] - public void Rehearsal() - { - var action = new ItemEnhancement8 - { - itemId = default, - materialId = default, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - slotAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.Blacksmith, - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle0Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle0Test.cs deleted file mode 100644 index 5fa849e288..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle0Test.cs +++ /dev/null @@ -1,599 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class MimisbrunnrBattle0Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle0Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100)] - public void Execute(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle0() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - Assert.NotNull(action.Result); - var reward = action.Result.OfType(); - Assert.NotEmpty(reward); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem2(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle0() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle0() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem2(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle0() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle0 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem2(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem2(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle0() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - - var spawnPlayer = action.Result.FirstOrDefault(e => e is SpawnPlayer); - Assert.NotNull(spawnPlayer); - Assert.True(spawnPlayer.Character is Player p); - var player = (Player)spawnPlayer.Character; - Assert.Equal(player.Costumes.First().ItemId, ((Costume)costume).ItemId); - Assert.Equal(player.Equipments.First().ItemId, equipment.ItemId); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle10Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle10Test.cs deleted file mode 100644 index fb3b7bdc5a..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle10Test.cs +++ /dev/null @@ -1,848 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle10Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly IAccountStateDelta _initialState; - private readonly Dictionary _sheets; - - public MimisbrunnrBattle10Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _sheets.Remove(nameof(RuneOptionSheet)); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle10 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle10 - { - costumes = new List { costume.ItemId }, - equipments = new List { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle10 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle10 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle10 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void ExecuteThrowHighLevelItemRequirementException(int avatarLevel) - { - var state = _initialState; - var avatarState = state.GetAvatarState(_avatarAddress); - avatarState.actionPoint = 99999999; - avatarState.level = avatarLevel; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - avatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - state = state.SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - - foreach (var requirementRow in _tableSheets.ItemRequirementSheet) - { - if (avatarState.level >= requirementRow.Level) - { - continue; - } - - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - - if (equipment.ElementalType != ElementalType.Fire) - { - continue; - } - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - if (!equipments.Any()) - { - continue; - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new MimisbrunnrBattle10 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = avatarState.address, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - } - } - - [Fact] - public void Execute_v100291() - { - var avatarLevel = 200; - var worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId; - var playCount = 1; - var clearStageId = 140; - var backward = false; - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - foreach (var (key, value) in _sheets) - { - if (keys.Contains(key)) - { - state = state.SetState(Addresses.TableSheet.Derive(key), null!); - } - } - - var action = new MimisbrunnrBattle10 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle10 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items - .Where(x => - x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000) - .ToList(); - Assert.Equal(stageRow.Rewards.Count, rewardItem.Count); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle11Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle11Test.cs deleted file mode 100644 index abff420cd2..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle11Test.cs +++ /dev/null @@ -1,744 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle11Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly IAccountStateDelta _initialState; - private readonly Dictionary _sheets; - - public MimisbrunnrBattle11Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle11 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = playCount, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle11 - { - Costumes = new List { costume.ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle11 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000002, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle11 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10011, - StageId = 10000002, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle11 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000022, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle11 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000001, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle11 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle11 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle11 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = GameConfig.MimisbrunnrWorldId, - StageId = GameConfig.MimisbrunnrStartStageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void ExecuteThrowHighLevelItemRequirementException(int avatarLevel) - { - var state = _initialState; - var avatarState = state.GetAvatarState(_avatarAddress); - avatarState.actionPoint = 99999999; - avatarState.level = avatarLevel; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - avatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - state = state.SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - - foreach (var requirementRow in _tableSheets.ItemRequirementSheet) - { - if (avatarState.level >= requirementRow.Level) - { - continue; - } - - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - - if (equipment.ElementalType != ElementalType.Fire) - { - continue; - } - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - if (!equipments.Any()) - { - continue; - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new MimisbrunnrBattle11 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = GameConfig.MimisbrunnrWorldId, - StageId = GameConfig.MimisbrunnrStartStageId, - PlayCount = 1, - AvatarAddress = avatarState.address, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - } - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle11 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = playCount, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items - .Where(x => - x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000) - .ToList(); - Assert.Equal(stageRow.Rewards.Count, rewardItem.Count); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle12Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle12Test.cs deleted file mode 100644 index 50603b0aae..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle12Test.cs +++ /dev/null @@ -1,862 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle12Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly IAccountStateDelta _initialState; - private readonly Dictionary _sheets; - - public MimisbrunnrBattle12Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle12 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = playCount, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle12 - { - Costumes = new List { costume.ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle12 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000002, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle12 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10011, - StageId = 10000002, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle12 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000022, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle12 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000001, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle12 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle12 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle12 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = GameConfig.MimisbrunnrWorldId, - StageId = GameConfig.MimisbrunnrStartStageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void ExecuteThrowHighLevelItemRequirementException(int avatarLevel) - { - var state = _initialState; - var avatarState = state.GetAvatarState(_avatarAddress); - avatarState.actionPoint = 99999999; - avatarState.level = avatarLevel; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - avatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - state = state.SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - - foreach (var requirementRow in _tableSheets.ItemRequirementSheet) - { - if (avatarState.level >= requirementRow.Level) - { - continue; - } - - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - - if (equipment.ElementalType != ElementalType.Fire) - { - continue; - } - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - if (!equipments.Any()) - { - continue; - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new MimisbrunnrBattle12 - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = GameConfig.MimisbrunnrWorldId, - StageId = GameConfig.MimisbrunnrStartStageId, - PlayCount = 1, - AvatarAddress = avatarState.address, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - } - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle12 - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = playCount, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items - .Where(x => - x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000) - .ToList(); - Assert.Equal(stageRow.Rewards.Count, rewardItem.Count); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - int avatarLevel = 200; - int worldId = GameConfig.MimisbrunnrWorldId; - int stageId = GameConfig.MimisbrunnrStartStageId; - int clearStageId = 140; - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var context = new ActionContext(); - var state = _initialState; - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var ncgCurrency = state.GetGoldCurrency(); - state = state.MintAsset(context, _agentAddress, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatarAddress, - SlotIndex = 1, - }; - - state = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - var action = new MimisbrunnrBattle12 - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle13Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle13Test.cs deleted file mode 100644 index 3f12ea00c1..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle13Test.cs +++ /dev/null @@ -1,863 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Blockchain.Policy; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle13Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly IAccountStateDelta _initialState; - private readonly Dictionary _sheets; - - public MimisbrunnrBattle13Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in _sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = playCount, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle - { - Costumes = new List { costume.ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000002, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10011, - StageId = 10000002, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000022, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 10001, - StageId = 10000001, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = new List { equipment.ItemId }, - Foods = new List(), - RuneInfos = new List(), - WorldId = GameConfig.MimisbrunnrWorldId, - StageId = GameConfig.MimisbrunnrStartStageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void ExecuteThrowHighLevelItemRequirementException(int avatarLevel) - { - var state = _initialState; - var avatarState = state.GetAvatarState(_avatarAddress); - avatarState.actionPoint = 99999999; - avatarState.level = avatarLevel; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - avatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - state = state.SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - - foreach (var requirementRow in _tableSheets.ItemRequirementSheet) - { - if (avatarState.level >= requirementRow.Level) - { - continue; - } - - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - - if (equipment.ElementalType != ElementalType.Fire) - { - continue; - } - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - if (!equipments.Any()) - { - continue; - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new MimisbrunnrBattle - { - Costumes = costumes, - Equipments = equipments, - Foods = new List(), - RuneInfos = new List(), - WorldId = GameConfig.MimisbrunnrWorldId, - StageId = GameConfig.MimisbrunnrStartStageId, - PlayCount = 1, - AvatarAddress = avatarState.address, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - } - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle - { - Costumes = costumes, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - PlayCount = playCount, - AvatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items - .Where(x => - x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000) - .ToList(); - Assert.Equal(stageRow.Rewards.Count, rewardItem.Count); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Theory] - [InlineData(0, 30001, 1, 30001, typeof(DuplicatedRuneIdException))] - [InlineData(1, 10002, 1, 30001, typeof(DuplicatedRuneSlotIndexException))] - public void ExecuteDuplicatedException(int slotIndex, int runeId, int slotIndex2, int runeId2, Type exception) - { - int avatarLevel = 200; - int worldId = GameConfig.MimisbrunnrWorldId; - int stageId = GameConfig.MimisbrunnrStartStageId; - int clearStageId = 140; - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var context = new ActionContext(); - var state = _initialState; - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - - var ncgCurrency = state.GetGoldCurrency(); - state = state.MintAsset(context, _agentAddress, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatarAddress, - SlotIndex = 1, - }; - - state = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - var action = new MimisbrunnrBattle - { - Costumes = new List { ((Costume)costume).ItemId }, - Equipments = equipments.Select(e => e.NonFungibleId).ToList(), - Foods = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - WorldId = worldId, - StageId = stageId, - PlayCount = 1, - AvatarAddress = _avatarAddress, - }; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle2Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle2Test.cs deleted file mode 100644 index ff472a5a9f..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle2Test.cs +++ /dev/null @@ -1,599 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class MimisbrunnrBattle2Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle2Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100)] - public void Execute(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle2() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - Assert.NotNull(action.Result); - var reward = action.Result.OfType(); - Assert.NotEmpty(reward); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem2(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle2() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle2() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem2(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle2() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle2 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem2(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem2(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle2() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - - var spawnPlayer = action.Result.FirstOrDefault(e => e is SpawnPlayer); - Assert.NotNull(spawnPlayer); - Assert.True(spawnPlayer.Character is Player p); - var player = (Player)spawnPlayer.Character; - Assert.Equal(player.Costumes.First().ItemId, ((Costume)costume).ItemId); - Assert.Equal(player.Equipments.First().ItemId, equipment.ItemId); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle3Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle3Test.cs deleted file mode 100644 index ef81ceeb58..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle3Test.cs +++ /dev/null @@ -1,600 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class MimisbrunnrBattle3Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle3Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100)] - public void Execute(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle3() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - Assert.NotNull(action.Result); - var reward = action.Result.OfType(); - Assert.NotEmpty(reward); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem2(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle3() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle3() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle3() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle3() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle3() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle3() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem2(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem2(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update2(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle3() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle3 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem2(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem2(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle3() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Null(action.Result); - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - - var spawnPlayer = action.Result.FirstOrDefault(e => e is SpawnPlayer); - Assert.NotNull(spawnPlayer); - Assert.True(spawnPlayer.Character is Player p); - var player = (Player)spawnPlayer.Character; - Assert.Equal(player.Costumes.First().ItemId, ((Costume)costume).ItemId); - Assert.Equal(player.Equipments.First().ItemId, equipment.ItemId); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle4Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle4Test.cs deleted file mode 100644 index ab17d20be3..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle4Test.cs +++ /dev/null @@ -1,627 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle4Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly WeeklyArenaState _weeklyArenaState; - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle4Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _weeklyArenaState = new WeeklyArenaState(0); - - _initialState = new MockStateDelta() - .SetState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle4() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var newWeeklyState = nextState.GetWeeklyArenaState(0); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle4() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = default, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle4() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle4 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle4() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Fact] - public void Rehearsal() - { - var action = new MimisbrunnrBattle4() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _avatarAddress, - _weeklyArenaState.address, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle5Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle5Test.cs deleted file mode 100644 index f9727b58da..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle5Test.cs +++ /dev/null @@ -1,611 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle5Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle5Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(mailEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(mailEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle5() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle5() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - avatarAddress = _avatarAddress, - rankingMapAddress = default, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle5() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle5 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle5() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Fact] - public void Rehearsal() - { - var action = new MimisbrunnrBattle5() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle6Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle6Test.cs deleted file mode 100644 index 2f9b7c3e9d..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle6Test.cs +++ /dev/null @@ -1,770 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle6Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle6Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - - var equipments = new List - { - equipment.ItemId, - armorEquipment.ItemId, - beltEquipment.ItemId, - necklaceEquipment.ItemId, - }; - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle6() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle6() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle6() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle6() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = default, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle6() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle6() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle6() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle6() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle6 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle6() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - List equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = armorEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle6() - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new MimisbrunnrBattle6() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle7Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle7Test.cs deleted file mode 100644 index 35ff553a7a..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle7Test.cs +++ /dev/null @@ -1,770 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle7Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly Address _rankingMapAddress; - - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle7Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - _rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - _rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - - var equipments = new List - { - equipment.ItemId, - armorEquipment.ItemId, - beltEquipment.ItemId, - necklaceEquipment.ItemId, - }; - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle7() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var value = nextState.GetState(_rankingMapAddress); - if (value != null) - { - var rankingMapState = new RankingMapState((Dictionary)value); - var info = rankingMapState.GetRankingInfos(null).First(); - - Assert.Equal(info.AgentAddress, _agentAddress); - Assert.Equal(info.AvatarAddress, _avatarAddress); - } - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle7() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle7() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowInvalidRankingMapAddress() - { - var action = new MimisbrunnrBattle7() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = default, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle7() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle7() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle7() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle7() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle7 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle7() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - List equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = armorEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle7() - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new MimisbrunnrBattle7() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - rankingMapAddress = _rankingMapAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _rankingMapAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle8Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle8Test.cs deleted file mode 100644 index 3497b86e88..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle8Test.cs +++ /dev/null @@ -1,719 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle8Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle8Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - - var equipments = new List - { - equipment.ItemId, - armorEquipment.ItemId, - beltEquipment.ItemId, - necklaceEquipment.ItemId, - }; - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle8() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle8() - { - costumes = new List { costume.ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle8() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle8() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle8() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle8() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle8() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle8 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle8() - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List() { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - var clearedStageId = stageId; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearedStageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - List equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(equipment); - - var armorEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var armorEquipment = ItemFactory.CreateItemUsable(armorEquipmentRow, Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(armorEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - previousAvatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(armorEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = armorEquipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle8() - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items.Where( - x => x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem ownedFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000); - - Assert.Equal(stageRow.Rewards.Count(), rewardItem.Count()); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new MimisbrunnrBattle8() - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle9Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle9Test.cs deleted file mode 100644 index 995732b6c6..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle9Test.cs +++ /dev/null @@ -1,770 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Elemental; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class MimisbrunnrBattle9Test - { - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - - private readonly Address _avatarAddress; - - private readonly IAccountStateDelta _initialState; - - public MimisbrunnrBattle9Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = _agentAddress.Derive("avatar"); - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - rankingMapAddress - ) - { - level = 400, - }; - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new MockStateDelta() - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, true)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, true)] - [InlineData(200, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 140, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 1, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 2, 100, false)] - [InlineData(400, GameConfig.MimisbrunnrWorldId, GameConfig.MimisbrunnrStartStageId, 3, 100, false)] - public void Execute(int avatarLevel, int worldId, int stageId, int playCount, int clearStageId, bool backward) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()) - .SetState(_avatarAddress, previousAvatarState.SerializeV2()); - } - - var action = new MimisbrunnrBattle9 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var stageId = 10000002; - var worldId = 10001; - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var previousState = _initialState.SetState( - _avatarAddress, - previousAvatarState.Serialize()); - - var costumeRow = - _tableSheets.CostumeItemSheet.Values.First(x => x.ItemSubType == ItemSubType.FullCostume); - var costume = ItemFactory.CreateCostume(costumeRow, default); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var action = new MimisbrunnrBattle9 - { - costumes = new List { costume.ItemId }, - equipments = new List { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new MimisbrunnrBattle9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowNotFound() - { - var action = new MimisbrunnrBattle9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10011, - stageId = 10000002, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowSheetRowColumn() - { - var action = new MimisbrunnrBattle9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000022, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughActionPoint() - { - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.actionPoint = 0; - - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100); - - previousAvatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - var action = new MimisbrunnrBattle9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 10001, - stageId = 10000001, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var state = _initialState; - state = state.SetState(_avatarAddress, previousAvatarState.Serialize()); - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - }); - } - - [Theory] - [InlineData(400, 10001, 10000001, 99)] - public void ExecuteThrowInvalidWorld(int avatarLevel, int worldId, int stageId, int clearStageId) - { - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out _)); - - var previousAvatarState = _initialState.GetAvatarState(_avatarAddress); - previousAvatarState.level = avatarLevel; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - clearStageId); - - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - previousAvatarState.inventory.AddItem(costume); - - var mimisbrunnrSheet = _tableSheets.MimisbrunnrSheet; - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", stageId); - } - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - previousAvatarState.inventory.AddItem(equipment); - - foreach (var equipmentId in previousAvatarState.inventory.Equipments) - { - if (previousAvatarState.inventory.TryGetNonFungibleItem(equipmentId, out ItemUsable itemUsable)) - { - var elementalType = ((Equipment)itemUsable).ElementalType; - Assert.True(mimisbrunnrSheetRow.ElementalTypes.Exists(x => x == elementalType)); - } - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - var state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - - var action = new MimisbrunnrBattle9 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List { equipment.ItemId }, - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = state, Signer = _agentAddress, Random = new TestRandom(), Rehearsal = false, - }); - }); - } - - /// - /// void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int). - /// - /// Less than stageId condition to unlock the mimisbrunnr world in `WorldUnlockSheet`. - [Theory] - [InlineData(1)] - [InlineData(99)] - public void ExecuteThrowFailedAddWorldExceptionWhenDoesNotMimisbrunnr(int alreadyClearedStageId) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - const int stageId = GameConfig.MimisbrunnrStartStageId; - var worldSheetCsv = _initialState.GetSheetCsv(); - worldSheetCsv = worldSheetCsv.Replace($"{worldId},", $"_{worldId},"); - var worldSheet = new WorldSheet(); - worldSheet.Set(worldSheetCsv); - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation(0, worldSheet, alreadyClearedStageId); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = worldId, - stageId = stageId, - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - }); - }); - } - - [Fact] - public void ExecuteEquippableItemValidation() - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 140); - - var costumeId = _tableSheets - .CostumeItemSheet - .OrderedList - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - var costume = - ItemFactory.CreateItem(_tableSheets.ItemSheet[costumeId], new TestRandom()); - avatarState.inventory.AddItem(costume); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.OrderedList.First(x => x.ElementalType == ElementalType.Fire); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, default, 0); - avatarState.inventory.AddItem(equipment); - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new MimisbrunnrBattle9 - { - costumes = new List { ((Costume)costume).ItemId }, - equipments = new List { equipment.ItemId }, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - action.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Rehearsal = false, - Random = new TestRandom(), - }); - } - - [Theory] - [InlineData(15)] - [InlineData(30)] - [InlineData(50)] - [InlineData(75)] - [InlineData(100)] - [InlineData(120)] - [InlineData(150)] - [InlineData(200)] - public void ExecuteThrowHighLevelItemRequirementException(int avatarLevel) - { - var state = _initialState; - var avatarState = state.GetAvatarState(_avatarAddress); - avatarState.actionPoint = 99999999; - avatarState.level = avatarLevel; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 100 - ); - - avatarState.worldInformation.ClearStage( - 2, - 100, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet); - - state = state.SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - - foreach (var requirementRow in _tableSheets.ItemRequirementSheet) - { - if (avatarState.level >= requirementRow.Level) - { - continue; - } - - var costumes = new List(); - var equipments = new List(); - var random = new TestRandom(DateTimeOffset.Now.Millisecond); - if (_tableSheets.EquipmentItemSheet.TryGetValue(requirementRow.ItemId, out var row)) - { - var equipment = ItemFactory.CreateItem(row, random); - avatarState.inventory.AddItem(equipment); - - if (equipment.ElementalType != ElementalType.Fire) - { - continue; - } - } - else if (_tableSheets.CostumeItemSheet.TryGetValue(requirementRow.ItemId, out var row2)) - { - var costume = ItemFactory.CreateItem(row2, random); - avatarState.inventory.AddItem(costume); - costumes.Add(((INonFungibleItem)costume).NonFungibleId); - } - - if (!equipments.Any()) - { - continue; - } - - state = state.SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - - var action = new MimisbrunnrBattle9 - { - costumes = costumes, - equipments = equipments, - foods = new List(), - worldId = GameConfig.MimisbrunnrWorldId, - stageId = GameConfig.MimisbrunnrStartStageId, - playCount = 1, - avatarAddress = avatarState.address, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = avatarState.agentAddress, - Random = random, - })); - } - } - - [Theory] - [InlineData(true, 0, 100)] - [InlineData(true, 1, 100)] - [InlineData(true, 2, 100)] - [InlineData(true, 3, 100)] - [InlineData(true, 4, 100)] - [InlineData(true, 5, 100)] - [InlineData(true, 6, 100)] - [InlineData(true, 7, 100)] - [InlineData(true, 8, 100)] - [InlineData(true, 9, 100)] - [InlineData(false, 0, 100)] - [InlineData(false, 1, 100)] - [InlineData(false, 2, 100)] - [InlineData(false, 3, 100)] - [InlineData(false, 4, 100)] - public void CheckRewardItems(bool backward, int stageIndex, int playCount) - { - const int worldId = GameConfig.MimisbrunnrWorldId; - var stageId = GameConfig.MimisbrunnrStartStageId + stageIndex; - - Assert.True(_tableSheets.WorldSheet.TryGetValue(worldId, out var worldRow)); - Assert.True(stageId >= worldRow.StageBegin); - Assert.True(stageId <= worldRow.StageEnd); - Assert.True(_tableSheets.StageSheet.TryGetValue(stageId, out var stageRow)); - - var previousAvatarState = _initialState.GetAvatarStateV2(_avatarAddress); - previousAvatarState.actionPoint = 999999; - previousAvatarState.level = 400; - previousAvatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - stageId); - - var costumes = new List(); - var random = new TestRandom(); - var costumeId = _tableSheets - .CostumeItemSheet - .Values - .First(r => r.ItemSubType == ItemSubType.FullCostume) - .Id; - - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[costumeId], random); - previousAvatarState.inventory.AddItem(costume); - costumes.Add(costume.ItemId); - - var elementalType = _tableSheets.MimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrRow) - ? mimisbrunnrRow.ElementalTypes.First() - : ElementalType.Normal; - var equipments = Doomfist.GetAllParts(_tableSheets, previousAvatarState.level, elementalType); - foreach (var equipment in equipments) - { - previousAvatarState.inventory.AddItem(equipment); - } - - var result = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipments.First(), - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - previousAvatarState.Update(mail); - } - - IAccountStateDelta state; - if (backward) - { - state = _initialState.SetState(_avatarAddress, previousAvatarState.Serialize()); - } - else - { - state = _initialState - .SetState(_avatarAddress, previousAvatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), previousAvatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), previousAvatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), previousAvatarState.questList.Serialize()); - } - - var action = new MimisbrunnrBattle9 - { - costumes = costumes, - equipments = equipments.Select(e => e.NonFungibleId).ToList(), - foods = new List(), - worldId = worldId, - stageId = stageId, - playCount = playCount, - avatarAddress = _avatarAddress, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.True(nextAvatarState.worldInformation.IsStageCleared(stageId)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var rewardItem = nextAvatarState.inventory.Items - .Where(x => - x.item.ItemSubType != ItemSubType.FoodMaterial && - x.item is IFungibleItem && - x.item.Id != 400000 && x.item.Id != 500000) - .ToList(); - Assert.Equal(stageRow.Rewards.Count, rewardItem.Count); - - var min = stageRow.Rewards.OrderBy(x => x.Min).First().Min; - var max = stageRow.Rewards.OrderBy(x => x.Max).First().Max; - var totalMin = min * playCount * stageRow.DropItemMin; - var totalMax = max * playCount * stageRow.DropItemMax; - var totalCount = rewardItem.Sum(x => x.count); - Assert.InRange(totalCount, totalMin, totalMax); - } - - [Fact] - public void Rehearsal() - { - var action = new MimisbrunnrBattle9 - { - costumes = new List(), - equipments = new List(), - foods = new List(), - worldId = 1, - stageId = 1, - playCount = 1, - avatarAddress = _avatarAddress, - }; - - var updatedAddresses = new List
- { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MonsterCollect0Test.cs b/.Lib9c.Tests/Action/MonsterCollect0Test.cs deleted file mode 100644 index e5656fcb38..0000000000 --- a/.Lib9c.Tests/Action/MonsterCollect0Test.cs +++ /dev/null @@ -1,255 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class MonsterCollect0Test - { - private readonly TableSheets _tableSheets; - private readonly Address _signer; - private IAccountStateDelta _initialState; - - public MonsterCollect0Test() - { - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - _signer = default; -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - foreach ((string key, string value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()) - .SetState(_signer, new AgentState(_signer).Serialize()); - } - } - - [Theory] - [InlineData(true, 2, 1, 1, 1)] - [InlineData(true, 5, 2, 2, MonsterCollectionState0.RewardInterval)] - [InlineData(false, 1, 3, 0, MonsterCollectionState0.RewardInterval * 3)] - [InlineData(false, 3, 4, 0, MonsterCollectionState0.RewardInterval * 4)] - public void Execute(bool exist, int level, int monsterCollectionRound, int prevLevel, long blockIndex) - { - Address monsterCollectionAddress = MonsterCollectionState0.DeriveAddress(_signer, monsterCollectionRound); - if (exist) - { - List rewards = _tableSheets.MonsterCollectionRewardSheet[prevLevel].Rewards; - MonsterCollectionState0 prevMonsterCollectionState = new MonsterCollectionState0(monsterCollectionAddress, prevLevel, 0, _tableSheets.MonsterCollectionRewardSheet); - _initialState = _initialState.SetState(monsterCollectionAddress, prevMonsterCollectionState.Serialize()); - Assert.All(prevMonsterCollectionState.RewardLevelMap, kv => Assert.Equal(rewards, kv.Value)); - } - - AgentState prevAgentState = _initialState.GetAgentState(_signer); - while (prevAgentState.MonsterCollectionRound < monsterCollectionRound) - { - prevAgentState.IncreaseCollectionRound(); - } - - var context = new ActionContext(); - _initialState = _initialState.SetState(_signer, prevAgentState.Serialize()); - - Currency currency = _initialState.GetGoldCurrency(); - - for (int i = 1; i < level + 1; i++) - { - if (i > prevLevel) - { - MonsterCollectionSheet.Row row = _tableSheets.MonsterCollectionSheet[i]; - _initialState = _initialState.MintAsset(context, _signer, row.RequiredGold * currency); - } - } - - MonsterCollect0 action = new MonsterCollect0 - { - level = level, - collectionRound = monsterCollectionRound, - }; - - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = blockIndex, - }); - - MonsterCollectionState0 nextMonsterCollectionState = new MonsterCollectionState0((Dictionary)nextState.GetState(monsterCollectionAddress)); - AgentState nextAgentState = nextState.GetAgentState(_signer); - Assert.Equal(level, nextMonsterCollectionState.Level); - Assert.Equal(0 * currency, nextState.GetBalance(_signer, currency)); - Assert.Equal(monsterCollectionRound, nextAgentState.MonsterCollectionRound); - long rewardLevel = nextMonsterCollectionState.GetRewardLevel(blockIndex); - for (long i = rewardLevel; i < 4; i++) - { - List expected = _tableSheets.MonsterCollectionRewardSheet[level].Rewards; - Assert.Equal(expected, nextMonsterCollectionState.RewardLevelMap[i + 1]); - } - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - MonsterCollect0 action = new MonsterCollect0 - { - level = 1, - collectionRound = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(2, 1)] - [InlineData(1, 2)] - public void Execute_Throw_InvalidMonsterCollectionRoundException(int agentCollectionRound, int collectionRound) - { - AgentState prevAgentState = _initialState.GetAgentState(_signer); - while (prevAgentState.MonsterCollectionRound < agentCollectionRound) - { - prevAgentState.IncreaseCollectionRound(); - } - - _initialState = _initialState.SetState(_signer, prevAgentState.Serialize()); - - MonsterCollect0 action = new MonsterCollect0 - { - level = 1, - collectionRound = collectionRound, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_Throw_SheetRowNotFoundException() - { - int level = 100; - - Assert.False(_tableSheets.MonsterCollectionSheet.Keys.Contains(level)); - - MonsterCollect0 action = new MonsterCollect0 - { - level = level, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - MonsterCollect0 action = new MonsterCollect0 - { - level = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_Throw_MonsterCollectionExpiredException() - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 prevMonsterCollectionState = new MonsterCollectionState0(collectionAddress, 1, 0, _tableSheets.MonsterCollectionRewardSheet); - Assert.Equal(MonsterCollectionState0.ExpirationIndex, prevMonsterCollectionState.ExpiredBlockIndex); - - _initialState = _initialState.SetState(collectionAddress, prevMonsterCollectionState.Serialize()); - - MonsterCollect0 action = new MonsterCollect0 - { - level = 2, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = prevMonsterCollectionState.ExpiredBlockIndex + 1, - })); - } - - [Theory] - [InlineData(2, 1)] - [InlineData(2, 2)] - public void Execute_Throw_InvalidLevelException(int prevLevel, int level) - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 0); - MonsterCollectionState0 prevMonsterCollectionState = new MonsterCollectionState0(collectionAddress, prevLevel, 0, _tableSheets.MonsterCollectionRewardSheet); - _initialState = _initialState.SetState(collectionAddress, prevMonsterCollectionState.Serialize()); - - MonsterCollect0 action = new MonsterCollect0 - { - level = level, - collectionRound = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Rehearsal() - { - Address collectionAddress = MonsterCollectionState0.DeriveAddress(_signer, 1); - MonsterCollect0 action = new MonsterCollect0 - { - level = 1, - collectionRound = 1, - }; - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - Rehearsal = true, - }); - - List
updatedAddresses = new List
() - { - _signer, - collectionAddress, - }; - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MonsterCollect2Test.cs b/.Lib9c.Tests/Action/MonsterCollect2Test.cs deleted file mode 100644 index 94c9f6d5b1..0000000000 --- a/.Lib9c.Tests/Action/MonsterCollect2Test.cs +++ /dev/null @@ -1,184 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class MonsterCollect2Test - { - private readonly TableSheets _tableSheets; - private readonly Address _signer; - private IAccountStateDelta _initialState; - - public MonsterCollect2Test() - { - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - _signer = default; -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - foreach ((string key, string value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()) - .SetState(_signer, new AgentState(_signer).Serialize()); - } - } - - [Theory] - [InlineData(1, 2, 1, null, 500 + 1800)] - [InlineData(1, 2, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), null)] - [InlineData(2, 4, 1, null, 500 + 1800 + 7200 + 54000)] - [InlineData(2, 4, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), null)] - [InlineData(3, 2, 1, typeof(RequiredBlockIndexException), null)] - [InlineData(3, 2, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), null)] - [InlineData(3, 3, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionLevelException), null)] - [InlineData(3, 0, 1, typeof(RequiredBlockIndexException), null)] - [InlineData(3, 0, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), 0)] - [InlineData(null, 1, 1, null, 500)] - [InlineData(null, 3, MonsterCollectionState.LockUpInterval, null, 500 + 1800 + 7200)] - [InlineData(null, -1, 1, typeof(MonsterCollectionLevelException), null)] - [InlineData(null, 100, 1, typeof(MonsterCollectionLevelException), null)] - [InlineData(null, 0, 1, null, 0)] - public void Execute(int? prevLevel, int level, long blockIndex, Type exc, int? expectedStakings) - { - Address monsterCollectionAddress = MonsterCollectionState.DeriveAddress(_signer, 0); - Currency currency = _initialState.GetGoldCurrency(); - FungibleAssetValue balance = currency * 10000000; - FungibleAssetValue staked = currency * 0; - var context = new ActionContext(); - if (prevLevel is { } prevLevelNotNull) - { - List rewards = _tableSheets.MonsterCollectionRewardSheet[prevLevelNotNull].Rewards; - var prevMonsterCollectionState = new MonsterCollectionState( - address: monsterCollectionAddress, - level: prevLevelNotNull, - blockIndex: 0, - monsterCollectionRewardSheet: _tableSheets.MonsterCollectionRewardSheet - ); - _initialState = _initialState.SetState(monsterCollectionAddress, prevMonsterCollectionState.Serialize()); - for (int i = 0; i < prevLevel; i++) - { - MonsterCollectionSheet.Row row = _tableSheets.MonsterCollectionSheet[i + 1]; - staked += row.RequiredGold * currency; - _initialState = _initialState.MintAsset(context, monsterCollectionAddress, row.RequiredGold * currency); - } - } - - balance -= staked; - - _initialState = _initialState.MintAsset(context, _signer, balance); - var action = new MonsterCollect2 - { - level = level, - }; - - if (exc is { } excType) - { - Assert.Throws(excType, () => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = blockIndex, - })); - } - else - { - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = blockIndex, - }); - - Assert.Equal(expectedStakings * currency, nextState.GetBalance(monsterCollectionAddress, currency)); - Assert.Equal(balance + staked - (expectedStakings * currency), nextState.GetBalance(_signer, currency)); - - if (level == 0) - { - Assert.Equal(Null.Value, nextState.GetState(monsterCollectionAddress)); - } - else - { - var nextMonsterCollectionState = new MonsterCollectionState((Dictionary)nextState.GetState(monsterCollectionAddress)); - Assert.Equal(level, nextMonsterCollectionState.Level); - Assert.Equal(blockIndex, nextMonsterCollectionState.StartedBlockIndex); - Assert.Equal(0, nextMonsterCollectionState.ReceivedBlockIndex); - Assert.Equal(0, nextMonsterCollectionState.ExpiredBlockIndex); - } - } - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new MonsterCollect2 - { - level = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - var action = new MonsterCollect2 - { - level = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Rehearsal() - { - var action = new MonsterCollect2 - { - level = 1, - }; - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - Rehearsal = true, - }); - - List
updatedAddresses = new List
() - { - _signer, - MonsterCollectionState.DeriveAddress(_signer, 0), - MonsterCollectionState.DeriveAddress(_signer, 1), - MonsterCollectionState.DeriveAddress(_signer, 2), - MonsterCollectionState.DeriveAddress(_signer, 3), - }; - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/MonsterCollectTest.cs b/.Lib9c.Tests/Action/MonsterCollectTest.cs deleted file mode 100644 index ba6b82e58f..0000000000 --- a/.Lib9c.Tests/Action/MonsterCollectTest.cs +++ /dev/null @@ -1,205 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class MonsterCollectTest - { - private readonly TableSheets _tableSheets; - private readonly Address _signer; - private IAccountStateDelta _initialState; - - public MonsterCollectTest() - { - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - _signer = default; -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new MockStateDelta() - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - foreach ((string key, string value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()) - .SetState(_signer, new AgentState(_signer).Serialize()); - } - } - - [Theory] - [InlineData(500 + 1800 + 7200 + 54000, 1, 2, 1, null, 500 + 1800)] - [InlineData(500 + 1800 + 7200 + 54000, 1, 2, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 2, 4, 1, null, 500 + 1800 + 7200 + 54000)] - [InlineData(500 + 1800 + 7200 + 54000 - 1, 2, 4, 1, typeof(InsufficientBalanceException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 2, 4, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 3, 2, 1, typeof(RequiredBlockIndexException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 3, 2, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 3, 3, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionLevelException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 3, 0, 1, typeof(RequiredBlockIndexException), null)] - [InlineData(500 + 1800 + 7200 + 54000, 3, 0, MonsterCollectionState.LockUpInterval, typeof(MonsterCollectionExistingClaimableException), 0)] - [InlineData(500 + 1800 + 7200 + 54000, null, 1, 1, null, 500)] - [InlineData(500 + 1800 + 7200 + 54000, null, 3, MonsterCollectionState.LockUpInterval, null, 500 + 1800 + 7200)] - [InlineData(500 + 1800 + 7200 + 54000, null, -1, 1, typeof(MonsterCollectionLevelException), null)] - [InlineData(500 + 1800 + 7200 + 54000, null, 100, 1, typeof(MonsterCollectionLevelException), null)] - [InlineData(500 + 1800 + 7200 + 54000, null, 0, 1, null, 0)] - public void Execute(int balance, int? prevLevel, int level, long blockIndex, Type exc, int? expectedStakings) - { - Address monsterCollectionAddress = MonsterCollectionState.DeriveAddress(_signer, 0); - Currency currency = _initialState.GetGoldCurrency(); - FungibleAssetValue balanceFav = currency * balance; - FungibleAssetValue staked = currency * 0; - var context = new ActionContext(); - if (prevLevel is { } prevLevelNotNull) - { - List rewards = _tableSheets.MonsterCollectionRewardSheet[prevLevelNotNull].Rewards; - var prevMonsterCollectionState = new MonsterCollectionState( - address: monsterCollectionAddress, - level: prevLevelNotNull, - blockIndex: 0, - monsterCollectionRewardSheet: _tableSheets.MonsterCollectionRewardSheet - ); - _initialState = _initialState.SetState(monsterCollectionAddress, prevMonsterCollectionState.Serialize()); - for (int i = 0; i < prevLevel; i++) - { - MonsterCollectionSheet.Row row = _tableSheets.MonsterCollectionSheet[i + 1]; - staked += row.RequiredGold * currency; - _initialState = _initialState.MintAsset(context, monsterCollectionAddress, row.RequiredGold * currency); - } - } - - balanceFav -= staked; - - _initialState = _initialState.MintAsset(context, _signer, balanceFav); - var action = new MonsterCollect - { - level = level, - }; - - if (exc is { } excType) - { - Assert.Throws(excType, () => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = blockIndex, - })); - } - else - { - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = blockIndex, - }); - - Assert.Equal(expectedStakings * currency, nextState.GetBalance(monsterCollectionAddress, currency)); - Assert.Equal(balanceFav + staked - (expectedStakings * currency), nextState.GetBalance(_signer, currency)); - - if (level == 0) - { - Assert.Equal(Null.Value, nextState.GetState(monsterCollectionAddress)); - } - else - { - var nextMonsterCollectionState = new MonsterCollectionState((Dictionary)nextState.GetState(monsterCollectionAddress)); - Assert.Equal(level, nextMonsterCollectionState.Level); - Assert.Equal(blockIndex, nextMonsterCollectionState.StartedBlockIndex); - Assert.Equal(0, nextMonsterCollectionState.ReceivedBlockIndex); - Assert.Equal(0, nextMonsterCollectionState.ExpiredBlockIndex); - } - } - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - MonsterCollect action = new MonsterCollect - { - level = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - MonsterCollect action = new MonsterCollect - { - level = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_Throw_InvalidOperationException() - { - var stakeAddress = StakeState.DeriveAddress(_signer); - var states = _initialState.SetState( - stakeAddress, - new StakeState(stakeAddress, 0).SerializeV2()); - MonsterCollect action = new MonsterCollect - { - level = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signer, - BlockIndex = 1, - })); - } - - [Fact] - public void Rehearsal() - { - MonsterCollect action = new MonsterCollect - { - level = 1, - }; - IAccountStateDelta nextState = action.Execute(new ActionContext - { - PreviousState = new MockStateDelta(), - Signer = _signer, - Rehearsal = true, - }); - - List
updatedAddresses = new List
() - { - _signer, - MonsterCollectionState.DeriveAddress(_signer, 0), - MonsterCollectionState.DeriveAddress(_signer, 1), - MonsterCollectionState.DeriveAddress(_signer, 2), - MonsterCollectionState.DeriveAddress(_signer, 3), - }; - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid1Test.cs b/.Lib9c.Tests/Action/Raid1Test.cs deleted file mode 100644 index e4036f3264..0000000000 --- a/.Lib9c.Tests/Action/Raid1Test.cs +++ /dev/null @@ -1,569 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model.Arena; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class Raid1Test - { - private readonly Dictionary _sheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly Currency _goldCurrency; - - public Raid1Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = new PrivateKey().ToAddress(); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _goldCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - } - - [Theory] - // Join new raid. - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval)] - // Refill by interval. - [InlineData(null, true, true, false, true, 0, -WorldBossHelper.RefillInterval, false, false, 0, false, false, false, Raid4.RequiredInterval)] - // Refill by NCG. - [InlineData(null, true, true, false, true, 0, 200L, true, true, 0, false, false, false, Raid4.RequiredInterval)] - [InlineData(null, true, true, false, true, 0, 200L, true, true, 1, false, false, false, Raid4.RequiredInterval)] - // Boss level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, false, Raid4.RequiredInterval)] - // Update RaidRewardInfo. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, true, Raid4.RequiredInterval)] - // Boss skip level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, false, false, Raid4.RequiredInterval)] - // AvatarState null. - [InlineData(typeof(FailedLoadStateException), false, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval)] - // Insufficient CRYSTAL. - [InlineData(typeof(InsufficientBalanceException), true, true, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, false, true, 0, 0L, true, false, 0, false, false, false, Raid4.RequiredInterval)] - // Wait interval. - [InlineData(typeof(RequiredBlockIntervalException), true, true, false, true, 3, 10L, false, false, 0, false, false, false, Raid4.RequiredInterval - 4L)] - // Exceed purchase limit. - [InlineData(typeof(ExceedTicketPurchaseLimitException), true, true, false, true, 0, 100L, true, false, 1_000, false, false, false, Raid4.RequiredInterval)] - // Exceed challenge count. - [InlineData(typeof(ExceedPlayCountException), true, true, false, true, 0, 100L, false, false, 0, false, false, false, Raid4.RequiredInterval)] - public void Execute( - Type exc, - bool avatarExist, - bool stageCleared, - bool crystalExist, - bool raiderStateExist, - int remainChallengeCount, - long refillBlockIndexOffset, - bool payNcg, - bool ncgExist, - int purchaseCount, - bool kill, - bool levelUp, - bool rewardRecordExist, - long executeOffset - ) - { - var blockIndex = _tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var action = new Raid1 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - PayNcg = payNcg, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - int raidId = _tableSheets.WorldBossListSheet.FindRaidIdByBlockIndex(blockIndex); - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - WorldBossListSheet.Row worldBossRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - var hpSheet = _tableSheets.WorldBossGlobalHpSheet; - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - int level = 1; - if (kill & !levelUp) - { - level = hpSheet.OrderedList.Last().Level; - } - - var fee = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - - var context = new ActionContext(); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - if (avatarExist) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - if (stageCleared) - { - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - if (crystalExist) - { - var price = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - state = state.MintAsset(context, _agentAddress, price * crystal); - } - - if (raiderStateExist) - { - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = blockIndex + refillBlockIndexOffset; - raiderState.RemainChallengeCount = remainChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.HighScore = 0; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = purchaseCount; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - raiderState.UpdatedBlockIndex = blockIndex; - - state = state.SetState(raiderAddress, raiderState.Serialize()); - } - - if (rewardRecordExist) - { - var rewardRecord = new WorldBossKillRewardRecord - { - [0] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - if (ncgExist) - { - var row = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - state = state.MintAsset(context, _agentAddress, (row.TicketPrice + row.AdditionalTicketPrice * purchaseCount) * _goldCurrency); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - } - - if (kill) - { - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[level]) - { - CurrentHp = 0, - Level = level, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - } - - if (exc is null) - { - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - var nextState = action.Execute(ctx); - - var random = new TestRandom(randomSeed); - var bossListRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(ctx.BlockIndex); - var raidSimulatorSheets = _tableSheets.GetRaidSimulatorSheetsV1(); - var simulator = new RaidSimulatorV1( - bossListRow.BossId, - random, - avatarState, - action.FoodIds, - raidSimulatorSheets, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - var score = simulator.DamageDealt; - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - if (rewardRecordExist) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[bossListRow.BossId]; - Assert.True(state.TryGetState(bossAddress, out List prevRawBoss)); - var prevBossState = new WorldBossState(prevRawBoss); - int rank = WorldBossHelper.CalculateRank(bossRow, raiderStateExist ? 1_000 : 0); - var rewards = RuneHelper.CalculateReward( - rank, - prevBossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - foreach (var reward in rewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - } - - if (rewardMap.ContainsKey(crystal)) - { - Assert.Equal(rewardMap[crystal], nextState.GetBalance(_agentAddress, crystal)); - } - - if (crystalExist) - { - Assert.Equal(fee * crystal, nextState.GetBalance(bossAddress, crystal)); - } - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var raiderState = new RaiderState(rawRaider); - int expectedTotalScore = raiderStateExist ? 1_000 + score : score; - int expectedRemainChallenge = payNcg ? 0 : 2; - int expectedTotalChallenge = raiderStateExist ? 2 : 1; - - Assert.Equal(score, raiderState.HighScore); - Assert.Equal(expectedTotalScore, raiderState.TotalScore); - Assert.Equal(expectedRemainChallenge, raiderState.RemainChallengeCount); - Assert.Equal(expectedTotalChallenge, raiderState.TotalChallengeCount); - Assert.Equal(1, raiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, raiderState.IconId); - Assert.True(raiderState.Cp > 0); - - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var bossState = new WorldBossState(rawBoss); - int expectedLevel = level; - if (kill & levelUp) - { - expectedLevel++; - } - - Assert.Equal(expectedLevel, bossState.Level); - Assert.Equal(expectedLevel, raiderState.LatestBossLevel); - if (kill) - { - Assert.Equal(hpSheet[expectedLevel].Hp, bossState.CurrentHp); - } - - if (payNcg) - { - Assert.Equal(0 * _goldCurrency, nextState.GetBalance(_agentAddress, _goldCurrency)); - Assert.Equal(purchaseCount + 1, nextState.GetRaiderState(raiderAddress).PurchaseCount); - } - - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var rewardRecord = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.Contains(expectedLevel, rewardRecord.Keys); - if (rewardRecordExist) - { - Assert.True(rewardRecord[0]); - } - else - { - if (expectedLevel == 1) - { - Assert.False(rewardRecord[1]); - } - else - { - Assert.DoesNotContain(1, rewardRecord.Keys); - } - } - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid1 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - PayNcg = false, - }; - - var worldBossRow = _tableSheets.WorldBossListSheet.First().Value; - int raidId = worldBossRow.Id; - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = 0; - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = 0; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var rewardRecord = new WorldBossKillRewardRecord - { - [1] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[2]) - { - CurrentHp = 0, - Level = 2, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - var randomSeed = 0; - var random = new TestRandom(randomSeed); - - var simulator = new RaidSimulatorV1( - worldBossRow.BossId, - random, - avatarState, - action.FoodIds, - _tableSheets.GetRaidSimulatorSheetsV1(), - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - List killRewards = RuneHelper.CalculateReward( - 0, - bossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - var nextState = action.Execute(new ActionContext - { - BlockIndex = worldBossRow.StartedBlockIndex + Raid4.RequiredInterval, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }); - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var nextRaiderState = new RaiderState(rawRaider); - Assert.Equal(simulator.DamageDealt, nextRaiderState.HighScore); - - foreach (var reward in killRewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - - Assert.Equal(1, nextRaiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, nextRaiderState.IconId); - Assert.True(nextRaiderState.Cp > 0); - Assert.Equal(3, nextRaiderState.LatestBossLevel); - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var nextBossState = new WorldBossState(rawBoss); - Assert.Equal(3, nextBossState.Level); - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var nextRewardInfo = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.True(nextRewardInfo[1]); - } - - [Fact] - public void Execute_Throw_ActionObsoletedException() - { - var action = new Raid1 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - PayNcg = false, - }; - var row = _tableSheets.WorldBossListSheet.Values.First(r => r.Id > 1); - long blockIndex = row.StartedBlockIndex; - int raidId = row.Id; - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - WorldBossListSheet.Row worldBossRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = state, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid2Test.cs b/.Lib9c.Tests/Action/Raid2Test.cs deleted file mode 100644 index 99f5ba8f8b..0000000000 --- a/.Lib9c.Tests/Action/Raid2Test.cs +++ /dev/null @@ -1,529 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model.Arena; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class Raid2Test - { - private readonly Dictionary _sheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly Currency _goldCurrency; - - public Raid2Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = new PrivateKey().ToAddress(); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _goldCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - } - - [Theory] - // Join new raid. - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, true)] - // Refill by interval. - [InlineData(null, true, true, false, true, 0, -WorldBossHelper.RefillInterval, false, false, 0, false, false, false, Raid4.RequiredInterval, true)] - // Refill by NCG. - [InlineData(null, true, true, false, true, 0, 200L, true, true, 0, false, false, false, Raid4.RequiredInterval, true)] - [InlineData(null, true, true, false, true, 0, 200L, true, true, 1, false, false, false, Raid4.RequiredInterval, true)] - // Boss level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, false, Raid4.RequiredInterval, true)] - // Update RaidRewardInfo. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, true, Raid4.RequiredInterval, true)] - // Boss skip level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, false, false, Raid4.RequiredInterval, true)] - // AvatarState null. - [InlineData(typeof(FailedLoadStateException), false, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Insufficient CRYSTAL. - [InlineData(typeof(InsufficientBalanceException), true, true, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, false, true, 0, 0L, true, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Wait interval. - [InlineData(typeof(RequiredBlockIntervalException), true, true, false, true, 3, 10L, false, false, 0, false, false, false, Raid4.RequiredInterval - 4L, false)] - // Exceed purchase limit. - [InlineData(typeof(ExceedTicketPurchaseLimitException), true, true, false, true, 0, 100L, true, false, 1_000, false, false, false, Raid4.RequiredInterval, false)] - // Exceed challenge count. - [InlineData(typeof(ExceedPlayCountException), true, true, false, true, 0, 100L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - public void Execute( - Type exc, - bool avatarExist, - bool stageCleared, - bool crystalExist, - bool raiderStateExist, - int remainChallengeCount, - long refillBlockIndexOffset, - bool payNcg, - bool ncgExist, - int purchaseCount, - bool kill, - bool levelUp, - bool rewardRecordExist, - long executeOffset, - bool raiderListExist - ) - { - var blockIndex = _tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var action = new Raid2 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - PayNcg = payNcg, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - int raidId = _tableSheets.WorldBossListSheet.FindRaidIdByBlockIndex(blockIndex); - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - WorldBossListSheet.Row worldBossRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - var hpSheet = _tableSheets.WorldBossGlobalHpSheet; - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - int level = 1; - if (kill & !levelUp) - { - level = hpSheet.OrderedList.Last().Level; - } - - var fee = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - - var context = new ActionContext(); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - if (avatarExist) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - if (stageCleared) - { - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - if (crystalExist) - { - var price = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - state = state.MintAsset(context, _agentAddress, price * crystal); - } - - if (raiderStateExist) - { - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = blockIndex + refillBlockIndexOffset; - raiderState.RemainChallengeCount = remainChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.HighScore = 0; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = purchaseCount; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - raiderState.UpdatedBlockIndex = blockIndex; - - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var raiderList = new List().Add(raiderAddress.Serialize()); - - if (raiderListExist) - { - raiderList = raiderList.Add(new PrivateKey().ToAddress().Serialize()); - } - - state = state.SetState(raiderListAddress, raiderList); - } - - if (rewardRecordExist) - { - var rewardRecord = new WorldBossKillRewardRecord - { - [0] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - if (ncgExist) - { - var row = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - state = state.MintAsset(context, _agentAddress, (row.TicketPrice + row.AdditionalTicketPrice * purchaseCount) * _goldCurrency); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - } - - if (kill) - { - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[level]) - { - CurrentHp = 0, - Level = level, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - } - - if (exc is null) - { - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - var nextState = action.Execute(ctx); - - var random = new TestRandom(randomSeed); - var bossListRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(ctx.BlockIndex); - var raidSimulatorSheets = _tableSheets.GetRaidSimulatorSheets(); - var simulator = new RaidSimulatorV2( - bossListRow.BossId, - random, - avatarState, - action.FoodIds, - null, - raidSimulatorSheets, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - var score = simulator.DamageDealt; - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - if (rewardRecordExist) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[bossListRow.BossId]; - Assert.True(state.TryGetState(bossAddress, out List prevRawBoss)); - var prevBossState = new WorldBossState(prevRawBoss); - int rank = WorldBossHelper.CalculateRank(bossRow, raiderStateExist ? 1_000 : 0); - var rewards = RuneHelper.CalculateReward( - rank, - prevBossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - foreach (var reward in rewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - } - - if (rewardMap.ContainsKey(crystal)) - { - Assert.Equal(rewardMap[crystal], nextState.GetBalance(_agentAddress, crystal)); - } - - if (crystalExist) - { - Assert.Equal(fee * crystal, nextState.GetBalance(bossAddress, crystal)); - } - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var raiderState = new RaiderState(rawRaider); - int expectedTotalScore = raiderStateExist ? 1_000 + score : score; - int expectedRemainChallenge = payNcg ? 0 : 2; - int expectedTotalChallenge = raiderStateExist ? 2 : 1; - - Assert.Equal(score, raiderState.HighScore); - Assert.Equal(expectedTotalScore, raiderState.TotalScore); - Assert.Equal(expectedRemainChallenge, raiderState.RemainChallengeCount); - Assert.Equal(expectedTotalChallenge, raiderState.TotalChallengeCount); - Assert.Equal(1, raiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, raiderState.IconId); - Assert.True(raiderState.Cp > 0); - - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var bossState = new WorldBossState(rawBoss); - int expectedLevel = level; - if (kill & levelUp) - { - expectedLevel++; - } - - Assert.Equal(expectedLevel, bossState.Level); - Assert.Equal(expectedLevel, raiderState.LatestBossLevel); - if (kill) - { - Assert.Equal(hpSheet[expectedLevel].Hp, bossState.CurrentHp); - } - - if (payNcg) - { - Assert.Equal(0 * _goldCurrency, nextState.GetBalance(_agentAddress, _goldCurrency)); - Assert.Equal(purchaseCount + 1, nextState.GetRaiderState(raiderAddress).PurchaseCount); - } - - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var rewardRecord = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.Contains(expectedLevel, rewardRecord.Keys); - if (rewardRecordExist) - { - Assert.True(rewardRecord[0]); - } - else - { - if (expectedLevel == 1) - { - Assert.False(rewardRecord[1]); - } - else - { - Assert.DoesNotContain(1, rewardRecord.Keys); - } - } - - Assert.True(nextState.TryGetState(raiderListAddress, out List rawRaiderList)); - List
raiderList = rawRaiderList.ToList(StateExtensions.ToAddress); - - Assert.Contains(raiderAddress, raiderList); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid2 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - PayNcg = false, - }; - - var worldBossRow = _tableSheets.WorldBossListSheet.First().Value; - int raidId = worldBossRow.Id; - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = 0; - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = 0; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var rewardRecord = new WorldBossKillRewardRecord - { - [1] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[2]) - { - CurrentHp = 0, - Level = 2, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - var randomSeed = 0; - var random = new TestRandom(randomSeed); - - var simulator = new RaidSimulatorV2( - worldBossRow.BossId, - random, - avatarState, - action.FoodIds, - null, - _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - List killRewards = RuneHelper.CalculateReward( - 0, - bossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - var nextState = action.Execute(new ActionContext - { - BlockIndex = worldBossRow.StartedBlockIndex + Raid4.RequiredInterval, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }); - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var nextRaiderState = new RaiderState(rawRaider); - Assert.Equal(simulator.DamageDealt, nextRaiderState.HighScore); - - foreach (var reward in killRewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - - Assert.Equal(1, nextRaiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, nextRaiderState.IconId); - Assert.True(nextRaiderState.Cp > 0); - Assert.Equal(3, nextRaiderState.LatestBossLevel); - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var nextBossState = new WorldBossState(rawBoss); - Assert.Equal(3, nextBossState.Level); - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var nextRewardInfo = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.True(nextRewardInfo[1]); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid3Test.cs b/.Lib9c.Tests/Action/Raid3Test.cs deleted file mode 100644 index eece78e4ef..0000000000 --- a/.Lib9c.Tests/Action/Raid3Test.cs +++ /dev/null @@ -1,531 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model.Arena; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class Raid3Test - { - private readonly Dictionary _sheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly Currency _goldCurrency; - - public Raid3Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = new PrivateKey().ToAddress(); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _goldCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - } - - [Theory] - // Join new raid. - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, true)] - // Refill by interval. - [InlineData(null, true, true, false, true, 0, -WorldBossHelper.RefillInterval, false, false, 0, false, false, false, Raid4.RequiredInterval, true)] - // Refill by NCG. - [InlineData(null, true, true, false, true, 0, 200L, true, true, 0, false, false, false, Raid4.RequiredInterval, true)] - [InlineData(null, true, true, false, true, 0, 200L, true, true, 1, false, false, false, Raid4.RequiredInterval, true)] - // Boss level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, false, Raid4.RequiredInterval, true)] - // Update RaidRewardInfo. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, true, Raid4.RequiredInterval, true)] - // Boss skip level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, false, false, Raid4.RequiredInterval, true)] - // AvatarState null. - [InlineData(typeof(FailedLoadStateException), false, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Insufficient CRYSTAL. - [InlineData(typeof(InsufficientBalanceException), true, true, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, false, true, 0, 0L, true, false, 0, false, false, false, Raid4.RequiredInterval, false)] - // Wait interval. - [InlineData(typeof(RequiredBlockIntervalException), true, true, false, true, 3, 10L, false, false, 0, false, false, false, Raid4.RequiredInterval - 4L, false)] - // Exceed purchase limit. - [InlineData(typeof(ExceedTicketPurchaseLimitException), true, true, false, true, 0, 100L, true, false, 1_000, false, false, false, Raid4.RequiredInterval, false)] - // Exceed challenge count. - [InlineData(typeof(ExceedPlayCountException), true, true, false, true, 0, 100L, false, false, 0, false, false, false, Raid4.RequiredInterval, false)] - public void Execute( - Type exc, - bool avatarExist, - bool stageCleared, - bool crystalExist, - bool raiderStateExist, - int remainChallengeCount, - long refillBlockIndexOffset, - bool payNcg, - bool ncgExist, - int purchaseCount, - bool kill, - bool levelUp, - bool rewardRecordExist, - long executeOffset, - bool raiderListExist - ) - { - var blockIndex = _tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var action = new Raid3 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List(), - PayNcg = payNcg, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - int raidId = _tableSheets.WorldBossListSheet.FindRaidIdByBlockIndex(blockIndex); - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - WorldBossListSheet.Row worldBossRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - var hpSheet = _tableSheets.WorldBossGlobalHpSheet; - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - int level = 1; - if (kill & !levelUp) - { - level = hpSheet.OrderedList.Last().Level; - } - - var fee = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - - var context = new ActionContext(); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - if (avatarExist) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - if (stageCleared) - { - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - if (crystalExist) - { - var price = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - state = state.MintAsset(context, _agentAddress, price * crystal); - } - - if (raiderStateExist) - { - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = blockIndex + refillBlockIndexOffset; - raiderState.RemainChallengeCount = remainChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.HighScore = 0; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = purchaseCount; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - raiderState.UpdatedBlockIndex = blockIndex; - - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var raiderList = new List().Add(raiderAddress.Serialize()); - - if (raiderListExist) - { - raiderList = raiderList.Add(new PrivateKey().ToAddress().Serialize()); - } - - state = state.SetState(raiderListAddress, raiderList); - } - - if (rewardRecordExist) - { - var rewardRecord = new WorldBossKillRewardRecord - { - [0] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - if (ncgExist) - { - var row = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - state = state.MintAsset(context, _agentAddress, (row.TicketPrice + row.AdditionalTicketPrice * purchaseCount) * _goldCurrency); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - } - - if (kill) - { - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[level]) - { - CurrentHp = 0, - Level = level, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - } - - if (exc is null) - { - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - var nextState = action.Execute(ctx); - - var random = new TestRandom(randomSeed); - var bossListRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(ctx.BlockIndex); - var raidSimulatorSheets = _tableSheets.GetRaidSimulatorSheets(); - var simulator = new RaidSimulatorV2( - bossListRow.BossId, - random, - avatarState, - action.FoodIds, - null, - raidSimulatorSheets, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - var score = simulator.DamageDealt; - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - if (rewardRecordExist) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[bossListRow.BossId]; - Assert.True(state.TryGetState(bossAddress, out List prevRawBoss)); - var prevBossState = new WorldBossState(prevRawBoss); - int rank = WorldBossHelper.CalculateRank(bossRow, raiderStateExist ? 1_000 : 0); - var rewards = RuneHelper.CalculateReward( - rank, - prevBossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - foreach (var reward in rewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - } - - if (rewardMap.ContainsKey(crystal)) - { - Assert.Equal(rewardMap[crystal], nextState.GetBalance(_agentAddress, crystal)); - } - - if (crystalExist) - { - Assert.Equal(fee * crystal, nextState.GetBalance(bossAddress, crystal)); - } - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var raiderState = new RaiderState(rawRaider); - int expectedTotalScore = raiderStateExist ? 1_000 + score : score; - int expectedRemainChallenge = payNcg ? 0 : 2; - int expectedTotalChallenge = raiderStateExist ? 2 : 1; - - Assert.Equal(score, raiderState.HighScore); - Assert.Equal(expectedTotalScore, raiderState.TotalScore); - Assert.Equal(expectedRemainChallenge, raiderState.RemainChallengeCount); - Assert.Equal(expectedTotalChallenge, raiderState.TotalChallengeCount); - Assert.Equal(1, raiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, raiderState.IconId); - Assert.True(raiderState.Cp > 0); - - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var bossState = new WorldBossState(rawBoss); - int expectedLevel = level; - if (kill & levelUp) - { - expectedLevel++; - } - - Assert.Equal(expectedLevel, bossState.Level); - Assert.Equal(expectedLevel, raiderState.LatestBossLevel); - if (kill) - { - Assert.Equal(hpSheet[expectedLevel].Hp, bossState.CurrentHp); - } - - if (payNcg) - { - Assert.Equal(0 * _goldCurrency, nextState.GetBalance(_agentAddress, _goldCurrency)); - Assert.Equal(purchaseCount + 1, nextState.GetRaiderState(raiderAddress).PurchaseCount); - } - - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var rewardRecord = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.Contains(expectedLevel, rewardRecord.Keys); - if (rewardRecordExist) - { - Assert.True(rewardRecord[0]); - } - else - { - if (expectedLevel == 1) - { - Assert.False(rewardRecord[1]); - } - else - { - Assert.DoesNotContain(1, rewardRecord.Keys); - } - } - - Assert.True(nextState.TryGetState(raiderListAddress, out List rawRaiderList)); - List
raiderList = rawRaiderList.ToList(StateExtensions.ToAddress); - - Assert.Contains(raiderAddress, raiderList); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid3 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List(), - PayNcg = false, - }; - - var worldBossRow = _tableSheets.WorldBossListSheet.First().Value; - int raidId = worldBossRow.Id; - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = 0; - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = 0; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var rewardRecord = new WorldBossKillRewardRecord - { - [1] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[2]) - { - CurrentHp = 0, - Level = 2, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - var randomSeed = 0; - var random = new TestRandom(randomSeed); - - var simulator = new RaidSimulatorV2( - worldBossRow.BossId, - random, - avatarState, - action.FoodIds, - null, - _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - List killRewards = RuneHelper.CalculateReward( - 0, - bossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - var nextState = action.Execute(new ActionContext - { - BlockIndex = worldBossRow.StartedBlockIndex + Raid4.RequiredInterval, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }); - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var nextRaiderState = new RaiderState(rawRaider); - Assert.Equal(simulator.DamageDealt, nextRaiderState.HighScore); - - foreach (var reward in killRewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - - Assert.Equal(1, nextRaiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, nextRaiderState.IconId); - Assert.True(nextRaiderState.Cp > 0); - Assert.Equal(3, nextRaiderState.LatestBossLevel); - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var nextBossState = new WorldBossState(rawBoss); - Assert.Equal(3, nextBossState.Level); - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var nextRewardInfo = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.True(nextRewardInfo[1]); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid4Test.cs b/.Lib9c.Tests/Action/Raid4Test.cs deleted file mode 100644 index 6fe6bc3c9f..0000000000 --- a/.Lib9c.Tests/Action/Raid4Test.cs +++ /dev/null @@ -1,638 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model.Arena; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class Raid4Test - { - private readonly Dictionary _sheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly Currency _goldCurrency; - - public Raid4Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = new PrivateKey().ToAddress(); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _goldCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - } - - [Theory] - // Join new raid. - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - // Refill by interval. - [InlineData(null, true, true, false, true, 0, -WorldBossHelper.RefillInterval, false, false, 0, false, false, false, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - // Refill by NCG. - [InlineData(null, true, true, false, true, 0, 200L, true, true, 0, false, false, false, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - [InlineData(null, true, true, false, true, 0, 200L, true, true, 1, false, false, false, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - // Boss level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, false, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - // Update RaidRewardInfo. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, true, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - // Boss skip level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, false, false, Raid4.RequiredInterval, true, 0, 10002, 1, 30001)] - // AvatarState null. - [InlineData(typeof(FailedLoadStateException), false, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, false, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - // Insufficient CRYSTAL. - [InlineData(typeof(InsufficientBalanceException), true, true, false, false, 0, 0L, false, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, false, true, 0, 0L, true, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - // Wait interval. - [InlineData(typeof(RequiredBlockIntervalException), true, true, false, true, 3, 10L, false, false, 0, false, false, false, Raid4.RequiredInterval - 4L, false, 0, 10002, 1, 30001)] - // Exceed purchase limit. - [InlineData(typeof(ExceedTicketPurchaseLimitException), true, true, false, true, 0, 100L, true, false, 1_000, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - // Exceed challenge count. - [InlineData(typeof(ExceedPlayCountException), true, true, false, true, 0, 100L, false, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 10002, 1, 30001)] - [InlineData(typeof(DuplicatedRuneIdException), true, true, false, true, 3, 100L, true, false, 0, false, false, false, Raid4.RequiredInterval, false, 0, 30001, 1, 30001)] - [InlineData(typeof(DuplicatedRuneSlotIndexException), true, true, false, true, 3, 100L, true, false, 0, false, false, false, Raid4.RequiredInterval, false, 1, 10002, 1, 30001)] - public void Execute( - Type exc, - bool avatarExist, - bool stageCleared, - bool crystalExist, - bool raiderStateExist, - int remainChallengeCount, - long refillBlockIndexOffset, - bool payNcg, - bool ncgExist, - int purchaseCount, - bool kill, - bool levelUp, - bool rewardRecordExist, - long executeOffset, - bool raiderListExist, - int slotIndex, - int runeId, - int slotIndex2, - int runeId2 - ) - { - var blockIndex = _tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First(x => - { - if (exc == typeof(InsufficientBalanceException)) - { - return ncgExist ? x.TicketPrice > 0 : x.EntranceFee > 0; - } - - return true; - }) - .StartedBlockIndex; - - var action = new Raid4 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - PayNcg = payNcg, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - int raidId = _tableSheets.WorldBossListSheet.FindRaidIdByBlockIndex(blockIndex); - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - WorldBossListSheet.Row worldBossRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - var hpSheet = _tableSheets.WorldBossGlobalHpSheet; - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - int level = 1; - if (kill & !levelUp) - { - level = hpSheet.OrderedList.Last().Level; - } - - var fee = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - - var context = new ActionContext(); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - if (avatarExist) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - if (stageCleared) - { - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - if (crystalExist) - { - var price = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - state = state.MintAsset(context, _agentAddress, price * crystal); - } - - if (raiderStateExist) - { - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = blockIndex + refillBlockIndexOffset; - raiderState.RemainChallengeCount = remainChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.HighScore = 0; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = purchaseCount; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - raiderState.UpdatedBlockIndex = blockIndex; - - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var raiderList = new List().Add(raiderAddress.Serialize()); - - if (raiderListExist) - { - raiderList = raiderList.Add(new PrivateKey().ToAddress().Serialize()); - } - - state = state.SetState(raiderListAddress, raiderList); - } - - if (rewardRecordExist) - { - var rewardRecord = new WorldBossKillRewardRecord - { - [0] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - if (ncgExist) - { - var row = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - state = state.MintAsset(context, _agentAddress, (row.TicketPrice + row.AdditionalTicketPrice * purchaseCount) * _goldCurrency); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - } - - if (kill) - { - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[level]) - { - CurrentHp = 0, - Level = level, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - } - - if (exc is null) - { - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - var nextState = action.Execute(ctx); - - var random = new TestRandom(randomSeed); - var bossListRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(ctx.BlockIndex); - var raidSimulatorSheets = _tableSheets.GetRaidSimulatorSheets(); - var simulator = new RaidSimulatorV2( - bossListRow.BossId, - random, - avatarState, - action.FoodIds, - null, - raidSimulatorSheets, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - var score = simulator.DamageDealt; - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - if (rewardRecordExist) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[bossListRow.BossId]; - Assert.True(state.TryGetState(bossAddress, out List prevRawBoss)); - var prevBossState = new WorldBossState(prevRawBoss); - int rank = WorldBossHelper.CalculateRank(bossRow, raiderStateExist ? 1_000 : 0); - var rewards = RuneHelper.CalculateReward( - rank, - prevBossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - foreach (var reward in rewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - } - - if (rewardMap.ContainsKey(crystal)) - { - Assert.Equal(rewardMap[crystal], nextState.GetBalance(_agentAddress, crystal)); - } - - if (crystalExist) - { - Assert.Equal(fee * crystal, nextState.GetBalance(bossAddress, crystal)); - } - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var raiderState = new RaiderState(rawRaider); - int expectedTotalScore = raiderStateExist ? 1_000 + score : score; - int expectedRemainChallenge = payNcg ? 0 : 2; - int expectedTotalChallenge = raiderStateExist ? 2 : 1; - - Assert.Equal(score, raiderState.HighScore); - Assert.Equal(expectedTotalScore, raiderState.TotalScore); - Assert.Equal(expectedRemainChallenge, raiderState.RemainChallengeCount); - Assert.Equal(expectedTotalChallenge, raiderState.TotalChallengeCount); - Assert.Equal(1, raiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, raiderState.IconId); - Assert.True(raiderState.Cp > 0); - - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var bossState = new WorldBossState(rawBoss); - int expectedLevel = level; - if (kill & levelUp) - { - expectedLevel++; - } - - Assert.Equal(expectedLevel, bossState.Level); - Assert.Equal(expectedLevel, raiderState.LatestBossLevel); - if (kill) - { - Assert.Equal(hpSheet[expectedLevel].Hp, bossState.CurrentHp); - } - - if (payNcg) - { - Assert.Equal(0 * _goldCurrency, nextState.GetBalance(_agentAddress, _goldCurrency)); - Assert.Equal(purchaseCount + 1, nextState.GetRaiderState(raiderAddress).PurchaseCount); - } - - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var rewardRecord = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.Contains(expectedLevel, rewardRecord.Keys); - if (rewardRecordExist) - { - Assert.True(rewardRecord[0]); - } - else - { - if (expectedLevel == 1) - { - Assert.False(rewardRecord[1]); - } - else - { - Assert.DoesNotContain(1, rewardRecord.Keys); - } - } - - Assert.True(nextState.TryGetState(raiderListAddress, out List rawRaiderList)); - List
raiderList = rawRaiderList.ToList(StateExtensions.ToAddress); - - Assert.Contains(raiderAddress, raiderList); - } - else - { - if (exc == typeof(DuplicatedRuneIdException) || exc == typeof(DuplicatedRuneSlotIndexException)) - { - var ncgCurrency = state.GetGoldCurrency(); - state = state.MintAsset(context, _agentAddress, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatarAddress, - SlotIndex = 1, - }; - - state = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - } - - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid4 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List(), - PayNcg = false, - }; - - var worldBossRow = _tableSheets.WorldBossListSheet.First().Value; - int raidId = worldBossRow.Id; - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = 0; - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = 0; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var rewardRecord = new WorldBossKillRewardRecord - { - [1] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); - - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[2]) - { - CurrentHp = 0, - Level = 2, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - var randomSeed = 0; - var random = new TestRandom(randomSeed); - - var simulator = new RaidSimulatorV2( - worldBossRow.BossId, - random, - avatarState, - action.FoodIds, - null, - _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - List killRewards = RuneHelper.CalculateReward( - 0, - bossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - var nextState = action.Execute(new ActionContext - { - BlockIndex = worldBossRow.StartedBlockIndex + Raid4.RequiredInterval, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }); - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var nextRaiderState = new RaiderState(rawRaider); - Assert.Equal(simulator.DamageDealt, nextRaiderState.HighScore); - - foreach (var reward in killRewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - - Assert.Equal(1, nextRaiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, nextRaiderState.IconId); - Assert.True(nextRaiderState.Cp > 0); - Assert.Equal(3, nextRaiderState.LatestBossLevel); - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var nextBossState = new WorldBossState(rawBoss); - Assert.Equal(3, nextBossState.Level); - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var nextRewardInfo = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.True(nextRewardInfo[1]); - } - - [Fact] - public void Execute_With_Free_Crystal_Fee() - { - var action = new Raid4 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List(), - PayNcg = false, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - - _sheets[nameof(WorldBossListSheet)] = - "id,boss_id,started_block_index,ended_block_index,fee,ticket_price,additional_ticket_price,max_purchase_count\r\n" + - "1,900002,0,100,0,1,1,40"; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var blockIndex = Raid4.RequiredInterval; - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - IAccountStateDelta nextState; - var exception = Record.Exception(() => nextState = action.Execute(ctx)); - Assert.Null(exception); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid5Test.cs b/.Lib9c.Tests/Action/Raid5Test.cs deleted file mode 100644 index ed9269b230..0000000000 --- a/.Lib9c.Tests/Action/Raid5Test.cs +++ /dev/null @@ -1,640 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Extensions; - using Nekoyume.Helper; - using Nekoyume.Model.Arena; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class Raid5Test - { - private readonly Dictionary _sheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly Currency _goldCurrency; - - public Raid5Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = new PrivateKey().ToAddress(); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _goldCurrency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - } - - [Theory] - // Join new raid. - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, 5, false, 0, 10002, 1, 30001)] - [InlineData(null, true, true, true, false, 0, 0L, false, false, 0, false, false, false, 5, true, 0, 10002, 1, 30001)] - // Refill by interval. - [InlineData(null, true, true, false, true, 0, -WorldBossHelper.RefillInterval, false, false, 0, false, false, false, 5, true, 0, 10002, 1, 30001)] - // Refill by NCG. - [InlineData(null, true, true, false, true, 0, 200L, true, true, 0, false, false, false, 5, true, 0, 10002, 1, 30001)] - [InlineData(null, true, true, false, true, 0, 200L, true, true, 1, false, false, false, 5, true, 0, 10002, 1, 30001)] - // Boss level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, false, 5, true, 0, 10002, 1, 30001)] - // Update RaidRewardInfo. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, true, true, 5, true, 0, 10002, 1, 30001)] - // Boss skip level up. - [InlineData(null, true, true, false, true, 3, 100L, false, false, 0, true, false, false, 5, true, 0, 10002, 1, 30001)] - // AvatarState null. - [InlineData(typeof(FailedLoadStateException), false, false, false, false, 0, 0L, false, false, 0, false, false, false, 5, false, 0, 10002, 1, 30001)] - // Stage not cleared. - [InlineData(typeof(NotEnoughClearedStageLevelException), true, false, false, false, 0, 0L, false, false, 0, false, false, false, 5, false, 0, 10002, 1, 30001)] - // Insufficient CRYSTAL. - [InlineData(typeof(InsufficientBalanceException), true, true, false, false, 0, 0L, false, false, 0, false, false, false, 5, false, 0, 10002, 1, 30001)] - // Insufficient NCG. - [InlineData(typeof(InsufficientBalanceException), true, true, false, true, 0, 0L, true, false, 0, false, false, false, 5, false, 0, 10002, 1, 30001)] - // Wait interval. - [InlineData(typeof(RequiredBlockIntervalException), true, true, false, true, 3, 10L, false, false, 0, false, false, false, 1, false, 0, 10002, 1, 30001)] - // Exceed purchase limit. - [InlineData(typeof(ExceedTicketPurchaseLimitException), true, true, false, true, 0, 100L, true, false, 1_000, false, false, false, 5, false, 0, 10002, 1, 30001)] - // Exceed challenge count. - [InlineData(typeof(ExceedPlayCountException), true, true, false, true, 0, 100L, false, false, 0, false, false, false, 5, false, 0, 10002, 1, 30001)] - [InlineData(typeof(DuplicatedRuneIdException), true, true, false, true, 3, 100L, true, false, 0, false, false, false, 5, false, 0, 30001, 1, 30001)] - [InlineData(typeof(DuplicatedRuneSlotIndexException), true, true, false, true, 3, 100L, true, false, 0, false, false, false, 5, false, 1, 10002, 1, 30001)] - public void Execute( - Type exc, - bool avatarExist, - bool stageCleared, - bool crystalExist, - bool raiderStateExist, - int remainChallengeCount, - long refillBlockIndexOffset, - bool payNcg, - bool ncgExist, - int purchaseCount, - bool kill, - bool levelUp, - bool rewardRecordExist, - long executeOffset, - bool raiderListExist, - int slotIndex, - int runeId, - int slotIndex2, - int runeId2 - ) - { - var blockIndex = _tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First(x => - { - if (exc == typeof(InsufficientBalanceException)) - { - return ncgExist ? x.TicketPrice > 0 : x.EntranceFee > 0; - } - - return true; - }) - .StartedBlockIndex; - - var action = new Raid5 - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - PayNcg = payNcg, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - int raidId = _tableSheets.WorldBossListSheet.FindRaidIdByBlockIndex(blockIndex); - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - WorldBossListSheet.Row worldBossRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - var hpSheet = _tableSheets.WorldBossGlobalHpSheet; - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - int level = 1; - if (kill & !levelUp) - { - level = hpSheet.OrderedList.Last().Level; - } - - var fee = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - - var context = new ActionContext(); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - if (avatarExist) - { - var equipments = Doomfist.GetAllParts(_tableSheets, avatarState.level); - foreach (var equipment in equipments) - { - avatarState.inventory.AddItem(equipment); - } - - if (stageCleared) - { - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - if (crystalExist) - { - var price = _tableSheets.WorldBossListSheet[raidId].EntranceFee; - state = state.MintAsset(context, _agentAddress, price * crystal); - } - - if (raiderStateExist) - { - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = blockIndex + refillBlockIndexOffset; - raiderState.RemainChallengeCount = remainChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.HighScore = 0; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = purchaseCount; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - raiderState.UpdatedBlockIndex = blockIndex; - - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var raiderList = new List().Add(raiderAddress.Serialize()); - - if (raiderListExist) - { - raiderList = raiderList.Add(new PrivateKey().ToAddress().Serialize()); - } - - state = state.SetState(raiderListAddress, raiderList); - } - - if (rewardRecordExist) - { - var rewardRecord = new WorldBossKillRewardRecord - { - [0] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - if (ncgExist) - { - var row = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); - state = state.MintAsset(context, _agentAddress, (row.TicketPrice + row.AdditionalTicketPrice * purchaseCount) * _goldCurrency); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - } - - if (kill) - { - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[level]) - { - CurrentHp = 0, - Level = level, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - } - - if (exc is null) - { - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - var nextState = action.Execute(ctx); - - var random = new TestRandom(randomSeed); - var bossListRow = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(ctx.BlockIndex); - var raidSimulatorSheets = _tableSheets.GetRaidSimulatorSheets(); - var simulator = new RaidSimulatorV2( - bossListRow.BossId, - random, - avatarState, - action.FoodIds, - null, - raidSimulatorSheets, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - var score = simulator.DamageDealt; - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - if (rewardRecordExist) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[bossListRow.BossId]; - Assert.True(state.TryGetState(bossAddress, out List prevRawBoss)); - var prevBossState = new WorldBossState(prevRawBoss); - int rank = WorldBossHelper.CalculateRank(bossRow, raiderStateExist ? 1_000 : 0); - var rewards = RuneHelper.CalculateReward( - rank, - prevBossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - foreach (var reward in rewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - } - - if (rewardMap.ContainsKey(crystal)) - { - Assert.Equal(rewardMap[crystal], nextState.GetBalance(_agentAddress, crystal)); - } - - if (crystalExist) - { - Assert.Equal(fee * crystal, nextState.GetBalance(bossAddress, crystal)); - } - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var raiderState = new RaiderState(rawRaider); - int expectedTotalScore = raiderStateExist ? 1_000 + score : score; - int expectedRemainChallenge = payNcg ? 0 : 2; - int expectedTotalChallenge = raiderStateExist ? 2 : 1; - - Assert.Equal(score, raiderState.HighScore); - Assert.Equal(expectedTotalScore, raiderState.TotalScore); - Assert.Equal(expectedRemainChallenge, raiderState.RemainChallengeCount); - Assert.Equal(expectedTotalChallenge, raiderState.TotalChallengeCount); - Assert.Equal(1, raiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, raiderState.IconId); - Assert.True(raiderState.Cp > 0); - - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var bossState = new WorldBossState(rawBoss); - int expectedLevel = level; - if (kill & levelUp) - { - expectedLevel++; - } - - Assert.Equal(expectedLevel, bossState.Level); - Assert.Equal(expectedLevel, raiderState.LatestBossLevel); - if (kill) - { - Assert.Equal(hpSheet[expectedLevel].Hp, bossState.CurrentHp); - } - - if (payNcg) - { - Assert.Equal(0 * _goldCurrency, nextState.GetBalance(_agentAddress, _goldCurrency)); - Assert.Equal(purchaseCount + 1, nextState.GetRaiderState(raiderAddress).PurchaseCount); - } - - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var rewardRecord = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.Contains(expectedLevel, rewardRecord.Keys); - if (rewardRecordExist) - { - Assert.True(rewardRecord[0]); - } - else - { - if (expectedLevel == 1) - { - Assert.False(rewardRecord[1]); - } - else - { - Assert.DoesNotContain(1, rewardRecord.Keys); - } - } - - Assert.True(nextState.TryGetState(raiderListAddress, out List rawRaiderList)); - List
raiderList = rawRaiderList.ToList(StateExtensions.ToAddress); - - Assert.Contains(raiderAddress, raiderList); - } - else - { - if (exc == typeof(DuplicatedRuneIdException) || exc == typeof(DuplicatedRuneSlotIndexException)) - { - var ncgCurrency = state.GetGoldCurrency(); - state = state.MintAsset(context, _agentAddress, 99999 * ncgCurrency); - - var unlockRuneSlot = new UnlockRuneSlot() - { - AvatarAddress = _avatarAddress, - SlotIndex = 1, - }; - - state = unlockRuneSlot.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = state, - Signer = _agentAddress, - Random = new TestRandom(), - }); - } - - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List(), - PayNcg = false, - }; - - var worldBossRow = _tableSheets.WorldBossListSheet.First().Value; - int raidId = worldBossRow.Id; - Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - Address bossAddress = Addresses.GetWorldBossAddress(raidId); - Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); - - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - var raiderState = new RaiderState(); - raiderState.RefillBlockIndex = 0; - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.TotalScore = 1_000; - raiderState.TotalChallengeCount = 1; - raiderState.PurchaseCount = 0; - raiderState.Cp = 0; - raiderState.Level = 0; - raiderState.IconId = 0; - raiderState.AvatarName = "hash"; - raiderState.AvatarAddress = _avatarAddress; - state = state.SetState(raiderAddress, raiderState.Serialize()); - - var rewardRecord = new WorldBossKillRewardRecord - { - [1] = false, - }; - state = state.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var bossState = - new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[2]) - { - CurrentHp = 0, - Level = 2, - }; - state = state.SetState(bossAddress, bossState.Serialize()); - var randomSeed = 0; - var random = new TestRandom(randomSeed); - - var simulator = new RaidSimulatorV2( - worldBossRow.BossId, - random, - avatarState, - action.FoodIds, - null, - _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - Dictionary rewardMap - = new Dictionary(); - foreach (var reward in simulator.AssetReward) - { - rewardMap[reward.Currency] = reward; - } - - List killRewards = RuneHelper.CalculateReward( - 0, - bossState.Id, - _tableSheets.RuneWeightSheet, - _tableSheets.WorldBossKillRewardSheet, - _tableSheets.RuneSheet, - random - ); - - var nextState = action.Execute(new ActionContext - { - BlockIndex = worldBossRow.StartedBlockIndex + gameConfigState.WorldBossRequiredInterval, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }); - - Assert.True(nextState.TryGetState(raiderAddress, out List rawRaider)); - var nextRaiderState = new RaiderState(rawRaider); - Assert.Equal(simulator.DamageDealt, nextRaiderState.HighScore); - - foreach (var reward in killRewards) - { - if (!rewardMap.ContainsKey(reward.Currency)) - { - rewardMap[reward.Currency] = reward; - } - else - { - rewardMap[reward.Currency] += reward; - } - } - - foreach (var reward in rewardMap) - { - if (reward.Key.Equals(CrystalCalculator.CRYSTAL)) - { - Assert.Equal(reward.Value, nextState.GetBalance(_agentAddress, reward.Key)); - } - else - { - Assert.Equal(reward.Value, nextState.GetBalance(_avatarAddress, reward.Key)); - } - } - - Assert.Equal(1, nextRaiderState.Level); - Assert.Equal(GameConfig.DefaultAvatarArmorId, nextRaiderState.IconId); - Assert.True(nextRaiderState.Cp > 0); - Assert.Equal(3, nextRaiderState.LatestBossLevel); - Assert.True(nextState.TryGetState(bossAddress, out List rawBoss)); - var nextBossState = new WorldBossState(rawBoss); - Assert.Equal(3, nextBossState.Level); - Assert.True(nextState.TryGetState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); - var nextRewardInfo = new WorldBossKillRewardRecord(rawRewardInfo); - Assert.True(nextRewardInfo[1]); - } - - [Fact] - public void Execute_With_Free_Crystal_Fee() - { - var action = new Raid - { - AvatarAddress = _avatarAddress, - EquipmentIds = new List(), - CostumeIds = new List(), - FoodIds = new List(), - RuneInfos = new List(), - PayNcg = false, - }; - Currency crystal = CrystalCalculator.CRYSTAL; - - _sheets[nameof(WorldBossListSheet)] = - "id,boss_id,started_block_index,ended_block_index,fee,ticket_price,additional_ticket_price,max_purchase_count\r\n" + - "1,900002,0,100,0,1,1,40"; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - IAccountStateDelta state = new MockStateDelta() - .SetState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetState(_agentAddress, new AgentState(_agentAddress).Serialize()); - - foreach (var (key, value) in _sheets) - { - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - for (int i = 0; i < 50; i++) - { - avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - - state = state - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); - - var blockIndex = gameConfigState.WorldBossRequiredInterval; - var randomSeed = 0; - var ctx = new ActionContext - { - BlockIndex = blockIndex, - PreviousState = state, - Random = new TestRandom(randomSeed), - Rehearsal = false, - Signer = _agentAddress, - }; - - IAccountStateDelta nextState; - var exception = Record.Exception(() => nextState = action.Execute(ctx)); - Assert.Null(exception); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle0Test.cs b/.Lib9c.Tests/Action/RankingBattle0Test.cs deleted file mode 100644 index 12ca1b075a..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle0Test.cs +++ /dev/null @@ -1,427 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class RankingBattle0Test - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - - private readonly Address _avatar2Address; - - private readonly Address _weeklyArenaAddress; - - public RankingBattle0Test() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.Set(avatar1State, _tableSheets.CharacterSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.Set(avatar2State, _tableSheets.CharacterSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Fact] - public void Execute() - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarState(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > - previousWeeklyState[_avatar1Address].Score); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar1Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle0 - { - AvatarAddress = avatarAddress, - EnemyAddress = enemyAddress, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException( - int caseIndex) - { - Address targetAddress = default; - switch (caseIndex) - { - case 0: - targetAddress = _avatar1Address; - break; - case 1: - targetAddress = _avatar2Address; - break; - } - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughFungibleAssetValueException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - previousWeeklyArenaState.Update(new ArenaInfo(arenaInfo)); - - var context = new ActionContext(); - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var goldCurrency = Currency.Legacy("NCG", 2, Addresses.GoldCurrency); -#pragma warning restore CS0618 - var previousAgentGoldState = _initialState.GetBalance( - _agent1Address, - goldCurrency); - - if (previousAgentGoldState.Sign > 0) - { - previousState = _initialState.TransferAsset( - context, - _agent1Address, - Addresses.GoldCurrency, - previousAgentGoldState); - } - - var action = new RankingBattle0 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle10Test.cs b/.Lib9c.Tests/Action/RankingBattle10Test.cs deleted file mode 100644 index 2d5fc8cf33..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle10Test.cs +++ /dev/null @@ -1,547 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class RankingBattle10Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle10Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var keys = new List - { - nameof(SkillActionBuffSheet), - nameof(ActionBuffSheet), - nameof(StatBuffSheet), - }; - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - if (!keys.Contains(key)) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, false, false)] - public void Execute(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem(enemyCostume); - - Address worldInformationAddress = _avatar1Address.Derive(LegacyWorldInformationKey); - if (avatarBackward) - { - previousState = - previousState.SetState(_avatar1Address, previousAvatar1State.Serialize()); - } - else - { - previousState = previousState - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - previousAvatar1State.inventory.Serialize()) - .SetState( - worldInformationAddress, - previousAvatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - previousAvatar1State.questList.Serialize()) - .SetState(_avatar1Address, previousAvatar1State.SerializeV2()); - } - - if (enemyBackward) - { - previousState = - previousState.SetState(_avatar2Address, enemyAvatarState.Serialize()); - } - else - { - previousState = previousState - .SetState( - _avatar2Address.Derive(LegacyInventoryKey), - enemyAvatarState.inventory.Serialize()) - .SetState( - _avatar2Address.Derive(LegacyWorldInformationKey), - enemyAvatarState.worldInformation.Serialize()) - .SetState( - _avatar2Address.Derive(LegacyQuestListKey), - enemyAvatarState.questList.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.SerializeV2()); - } - - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - var nextArenaInfo = nextWeeklyState[_avatar1Address]; - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.ArenaInfo); - Assert.NotNull(action.EnemyArenaInfo); - Assert.True(nextArenaInfo.Score > prevScore); - - // Check simulation result equal. - var player = new Player( - previousAvatar1State, - _tableSheets.CharacterSheet, - _tableSheets.CharacterLevelSheet, - _tableSheets.EquipmentItemSetEffectSheet); - var simulator = new RankingSimulatorV1( - new TestRandom(), - player, - action.EnemyPlayerDigest, - new List(), - _tableSheets.GetRankingSimulatorSheetsV1(), - RankingBattle10.StageId, - action.ArenaInfo, - action.EnemyArenaInfo, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - Assert.Equal(nextArenaInfo.Score, simulator.Log.score); - Assert.Equal(previousAvatar1State.SerializeV2(), nextAvatar1State.SerializeV2()); - Assert.Equal(previousAvatar1State.worldInformation.Serialize(), nextAvatar1State.worldInformation.Serialize()); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar1Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle10 - { - avatarAddress = avatarAddress, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void Rehearsal() - { - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - var updatedAddresses = new List
- { - _avatar1Address, - _weeklyArenaAddress, - _avatar1Address.Derive(LegacyInventoryKey), - _avatar1Address.Derive(LegacyWorldInformationKey), - _avatar1Address.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle10 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle2Test.cs b/.Lib9c.Tests/Action/RankingBattle2Test.cs deleted file mode 100644 index ff0c3f2cfd..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle2Test.cs +++ /dev/null @@ -1,398 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class RankingBattle2Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private IAccountStateDelta _initialState; - - public RankingBattle2Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = RankingBattle11Test.GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = RankingBattle11Test.GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Fact] - public void Execute() - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem2(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem2(enemyCostume); - - previousState = previousState - .SetState(_avatar1Address, previousAvatar1State.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.Serialize()); - - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.Id }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarState(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > - previousWeeklyState[_avatar1Address].Score); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar1Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle2 - { - AvatarAddress = avatarAddress, - EnemyAddress = enemyAddress, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException( - int caseIndex) - { - Address targetAddress = default; - switch (caseIndex) - { - case 0: - targetAddress = _avatar1Address; - break; - case 1: - targetAddress = _avatar2Address; - break; - } - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new RankingBattle2 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (RankingBattle2)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle3Test.cs b/.Lib9c.Tests/Action/RankingBattle3Test.cs deleted file mode 100644 index 0fa4577deb..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle3Test.cs +++ /dev/null @@ -1,455 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class RankingBattle3Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private IAccountStateDelta _initialState; - - public RankingBattle3Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = RankingBattle11Test.GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = RankingBattle11Test.GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Fact] - public void Execute() - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem2(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem2(enemyCostume); - - previousState = previousState - .SetState(_avatar1Address, previousAvatar1State.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.Serialize()); - - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarState(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > - previousWeeklyState[_avatar1Address].Score); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar1Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle3 - { - AvatarAddress = avatarAddress, - EnemyAddress = enemyAddress, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException( - int caseIndex) - { - Address targetAddress = default; - switch (caseIndex) - { - case 0: - targetAddress = _avatar1Address; - break; - case 1: - targetAddress = _avatar2Address; - break; - } - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (RankingBattle3)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle3 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle4Test.cs b/.Lib9c.Tests/Action/RankingBattle4Test.cs deleted file mode 100644 index e8c14deed5..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle4Test.cs +++ /dev/null @@ -1,479 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class RankingBattle4Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle4Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool isNew) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem2(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem2(enemyCostume); - - previousState = previousState - .SetState(_avatar1Address, previousAvatar1State.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.Serialize()); - - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarState(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > prevScore); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar1Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle4 - { - AvatarAddress = avatarAddress, - EnemyAddress = enemyAddress, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (RankingBattle4)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle4 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle5Test.cs b/.Lib9c.Tests/Action/RankingBattle5Test.cs deleted file mode 100644 index 45835afc6a..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle5Test.cs +++ /dev/null @@ -1,544 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class RankingBattle5Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle5Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, false, false)] - public void Execute(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem2(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem2(enemyCostume); - - if (avatarBackward) - { - previousState = previousState.SetState(_avatar1Address, previousAvatar1State.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatar1Address.Derive(LegacyInventoryKey), previousAvatar1State.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), previousAvatar1State.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), previousAvatar1State.questList.Serialize()) - .SetState(_avatar1Address, previousAvatar1State.SerializeV2()); - } - - if (enemyBackward) - { - previousState = previousState.SetState(_avatar2Address, enemyAvatarState.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatar2Address.Derive(LegacyInventoryKey), enemyAvatarState.inventory.Serialize()) - .SetState(_avatar2Address.Derive(LegacyWorldInformationKey), enemyAvatarState.worldInformation.Serialize()) - .SetState(_avatar2Address.Derive(LegacyQuestListKey), enemyAvatarState.questList.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.SerializeV2()); - } - - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > prevScore); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar1Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle5 - { - AvatarAddress = avatarAddress, - EnemyAddress = enemyAddress, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void Rehearsal() - { - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - var updatedAddresses = new List
() - { - _avatar1Address, - _weeklyArenaAddress, - _avatar1Address.Derive(LegacyInventoryKey), - _avatar1Address.Derive(LegacyWorldInformationKey), - _avatar1Address.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agent1Address, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (RankingBattle5)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem2(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle5 - { - AvatarAddress = _avatar1Address, - EnemyAddress = _avatar2Address, - WeeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle6Test.cs b/.Lib9c.Tests/Action/RankingBattle6Test.cs deleted file mode 100644 index f272cb2efb..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle6Test.cs +++ /dev/null @@ -1,544 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class RankingBattle6Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle6Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, false, false)] - public void Execute(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem(enemyCostume); - - if (avatarBackward) - { - previousState = previousState.SetState(_avatar1Address, previousAvatar1State.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatar1Address.Derive(LegacyInventoryKey), previousAvatar1State.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), previousAvatar1State.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), previousAvatar1State.questList.Serialize()) - .SetState(_avatar1Address, previousAvatar1State.SerializeV2()); - } - - if (enemyBackward) - { - previousState = previousState.SetState(_avatar2Address, enemyAvatarState.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatar2Address.Derive(LegacyInventoryKey), enemyAvatarState.inventory.Serialize()) - .SetState(_avatar2Address.Derive(LegacyWorldInformationKey), enemyAvatarState.worldInformation.Serialize()) - .SetState(_avatar2Address.Derive(LegacyQuestListKey), enemyAvatarState.questList.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.SerializeV2()); - } - - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > prevScore); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar1Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle6 - { - avatarAddress = avatarAddress, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void Rehearsal() - { - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - var updatedAddresses = new List
() - { - _avatar1Address, - _weeklyArenaAddress, - _avatar1Address.Derive(LegacyInventoryKey), - _avatar1Address.Derive(LegacyWorldInformationKey), - _avatar1Address.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agent1Address, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (RankingBattle6)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle6 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle7Test.cs b/.Lib9c.Tests/Action/RankingBattle7Test.cs deleted file mode 100644 index 561f1d1c69..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle7Test.cs +++ /dev/null @@ -1,544 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class RankingBattle7Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle7Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, false, false)] - public void Execute(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem(enemyCostume); - - if (avatarBackward) - { - previousState = previousState.SetState(_avatar1Address, previousAvatar1State.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatar1Address.Derive(LegacyInventoryKey), previousAvatar1State.inventory.Serialize()) - .SetState(_avatar1Address.Derive(LegacyWorldInformationKey), previousAvatar1State.worldInformation.Serialize()) - .SetState(_avatar1Address.Derive(LegacyQuestListKey), previousAvatar1State.questList.Serialize()) - .SetState(_avatar1Address, previousAvatar1State.SerializeV2()); - } - - if (enemyBackward) - { - previousState = previousState.SetState(_avatar2Address, enemyAvatarState.Serialize()); - } - else - { - previousState = previousState - .SetState(_avatar2Address.Derive(LegacyInventoryKey), enemyAvatarState.inventory.Serialize()) - .SetState(_avatar2Address.Derive(LegacyWorldInformationKey), enemyAvatarState.worldInformation.Serialize()) - .SetState(_avatar2Address.Derive(LegacyQuestListKey), enemyAvatarState.questList.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.SerializeV2()); - } - - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > prevScore); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar1Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle7 - { - avatarAddress = avatarAddress, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void Rehearsal() - { - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - var updatedAddresses = new List
() - { - _avatar1Address, - _weeklyArenaAddress, - _avatar1Address.Derive(LegacyInventoryKey), - _avatar1Address.Derive(LegacyWorldInformationKey), - _avatar1Address.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agent1Address, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var formatter = new BinaryFormatter(); - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - ms.Seek(0, SeekOrigin.Begin); - - var deserialized = (RankingBattle7)formatter.Deserialize(ms); - Assert.Equal(action.PlainValue, deserialized.PlainValue); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle7 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle8Test.cs b/.Lib9c.Tests/Action/RankingBattle8Test.cs deleted file mode 100644 index 42e0394904..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle8Test.cs +++ /dev/null @@ -1,552 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using MessagePack; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class RankingBattle8Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle8Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, false, false)] - public void Execute(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem(enemyCostume); - - if (avatarBackward) - { - previousState = - previousState.SetState(_avatar1Address, previousAvatar1State.Serialize()); - } - else - { - previousState = previousState - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - previousAvatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - previousAvatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - previousAvatar1State.questList.Serialize()) - .SetState(_avatar1Address, previousAvatar1State.SerializeV2()); - } - - if (enemyBackward) - { - previousState = - previousState.SetState(_avatar2Address, enemyAvatarState.Serialize()); - } - else - { - previousState = previousState - .SetState( - _avatar2Address.Derive(LegacyInventoryKey), - enemyAvatarState.inventory.Serialize()) - .SetState( - _avatar2Address.Derive(LegacyWorldInformationKey), - enemyAvatarState.worldInformation.Serialize()) - .SetState( - _avatar2Address.Derive(LegacyQuestListKey), - enemyAvatarState.questList.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.SerializeV2()); - } - - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.NotNull(action.ArenaInfo); - Assert.NotNull(action.EnemyArenaInfo); - Assert.NotNull(action.EnemyAvatarState); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > prevScore); - - // Check simulation result equal. - var simulator = new RankingSimulatorV1( - new TestRandom(), - previousAvatar1State, - action.EnemyAvatarState, - action.consumableIds, - _tableSheets.GetRankingSimulatorSheetsV1(), - RankingBattle8.StageId, - action.ArenaInfo, - action.EnemyArenaInfo, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - BattleLog log = simulator.Log; - BattleLog result = action.Result; - Assert.Equal(result.score, log.score); - Assert.Equal(result.Count, log.Count); - Assert.Equal(result.result, log.result); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar1Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle8 - { - avatarAddress = avatarAddress, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void Rehearsal() - { - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - var updatedAddresses = new List
() - { - _avatar1Address, - _weeklyArenaAddress, - _avatar1Address.Derive(LegacyInventoryKey), - _avatar1Address.Derive(LegacyWorldInformationKey), - _avatar1Address.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agent1Address, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle8 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle9Test.cs b/.Lib9c.Tests/Action/RankingBattle9Test.cs deleted file mode 100644 index 0508acc627..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle9Test.cs +++ /dev/null @@ -1,552 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using MessagePack; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.BattleStatus; - using Nekoyume.Model.Item; - using Nekoyume.Model.Stat; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class RankingBattle9Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccountStateDelta _initialState; - - public RankingBattle9Test(ITestOutputHelper outputHelper) - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - var rankingMapAddress = new PrivateKey().ToAddress(); - - var (agent1State, avatar1State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - _agent1Address = agent1State.address; - _avatar1Address = avatar1State.address; - - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - sheets, - _tableSheets, - rankingMapAddress); - var agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - - var weeklyArenaState = new WeeklyArenaState(0); - weeklyArenaState.SetV2(avatar1State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar1Address].Activate(); - weeklyArenaState.SetV2(avatar2State, _tableSheets.CharacterSheet, _tableSheets.CostumeStatSheet); - weeklyArenaState[_avatar2Address].Activate(); - _weeklyArenaAddress = weeklyArenaState.address; - - _initialState = _initialState - .SetState(_agent1Address, agent1State.Serialize()) - .SetState(_avatar1Address, avatar1State.Serialize()) - .SetState(agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - public static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress) - { - var agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(agentAddress); - - var avatarAddress = agentAddress.Derive("avatar"); - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - Math.Max( - tableSheets.StageSheet.First?.Id ?? 1, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard)), - }; - agentState.avatarAddresses.Add(0, avatarAddress); - - return (agentState, avatarState); - } - - [Theory] - [InlineData(true, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - [InlineData(true, false, false)] - [InlineData(false, true, true)] - [InlineData(false, true, false)] - [InlineData(false, false, true)] - [InlineData(false, false, false)] - public void Execute(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(0); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - if (isNew) - { - previousWeeklyState.Remove(_avatar1Address); - } - - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var itemIds = _tableSheets.WeeklyArenaRewardSheet.Values - .Select(r => r.Reward.ItemId) - .ToList(); - - Assert.All(itemIds, id => Assert.False(previousAvatar1State.inventory.HasItem(id))); - - var row = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); - var costume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row.CostumeId], new TestRandom()); - costume.equipped = true; - previousAvatar1State.inventory.AddItem(costume); - - var row2 = _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.DEF); - var enemyCostume = (Costume)ItemFactory.CreateItem( - _tableSheets.ItemSheet[row2.CostumeId], new TestRandom()); - enemyCostume.equipped = true; - var enemyAvatarState = _initialState.GetAvatarState(_avatar2Address); - enemyAvatarState.inventory.AddItem(enemyCostume); - - if (avatarBackward) - { - previousState = - previousState.SetState(_avatar1Address, previousAvatar1State.Serialize()); - } - else - { - previousState = previousState - .SetState( - _avatar1Address.Derive(LegacyInventoryKey), - previousAvatar1State.inventory.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyWorldInformationKey), - previousAvatar1State.worldInformation.Serialize()) - .SetState( - _avatar1Address.Derive(LegacyQuestListKey), - previousAvatar1State.questList.Serialize()) - .SetState(_avatar1Address, previousAvatar1State.SerializeV2()); - } - - if (enemyBackward) - { - previousState = - previousState.SetState(_avatar2Address, enemyAvatarState.Serialize()); - } - else - { - previousState = previousState - .SetState( - _avatar2Address.Derive(LegacyInventoryKey), - enemyAvatarState.inventory.Serialize()) - .SetState( - _avatar2Address.Derive(LegacyWorldInformationKey), - enemyAvatarState.worldInformation.Serialize()) - .SetState( - _avatar2Address.Derive(LegacyQuestListKey), - enemyAvatarState.questList.Serialize()) - .SetState(_avatar2Address, enemyAvatarState.SerializeV2()); - } - - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(0); - - Assert.Contains(nextAvatar1State.inventory.Materials, i => itemIds.Contains(i.Id)); - Assert.NotNull(action.Result); - Assert.NotNull(action.ArenaInfo); - Assert.NotNull(action.EnemyArenaInfo); - Assert.NotNull(action.EnemyAvatarState); - Assert.Contains(typeof(GetReward), action.Result.Select(e => e.GetType())); - Assert.Equal(BattleLog.Result.Win, action.Result.result); - Assert.True(nextWeeklyState[_avatar1Address].Score > prevScore); - - // Check simulation result equal. - var simulator = new RankingSimulatorV1( - new TestRandom(), - previousAvatar1State, - action.EnemyAvatarState, - action.consumableIds, - _tableSheets.GetRankingSimulatorSheetsV1(), - RankingBattle9.StageId, - action.ArenaInfo, - action.EnemyArenaInfo, - _tableSheets.CostumeStatSheet); - simulator.Simulate(); - - BattleLog log = simulator.Log; - BattleLog result = action.Result; - Assert.Equal(result.score, log.score); - Assert.Equal(result.Count, log.Count); - Assert.Equal(result.result, log.result); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar1Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - public void ExecuteThrowFailedLoadStateException(int caseIndex) - { - Address signer = default; - Address avatarAddress = default; - Address enemyAddress = default; - - switch (caseIndex) - { - case 0: - signer = new PrivateKey().ToAddress(); - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().ToAddress(); - break; - } - - var action = new RankingBattle9 - { - avatarAddress = avatarAddress, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = signer, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateNotContainsAvatarAddressException() - { - var targetAddress = _avatar2Address; - - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Remove(targetAddress); - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void ExecuteThrowNotEnoughWeeklyArenaChallengeCountException() - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - while (true) - { - var arenaInfo = previousWeeklyArenaState.GetArenaInfo(_avatar1Address); - arenaInfo.UpdateV3(previousAvatarState, arenaInfo, BattleLog.Result.Lose); - if (arenaInfo.DailyChallengeCount == 0) - { - break; - } - } - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = previousState, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - } - - [Fact] - public void Rehearsal() - { - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - consumableIds = new List(), - }; - - var updatedAddresses = new List
() - { - _avatar1Address, - _weeklyArenaAddress, - _avatar1Address.Derive(LegacyInventoryKey), - _avatar1Address.Derive(LegacyWorldInformationKey), - _avatar1Address.Derive(LegacyQuestListKey), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agent1Address, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(ItemSubType.Weapon, GameConfig.MaxEquipmentSlotCount.Weapon)] - [InlineData(ItemSubType.Armor, GameConfig.MaxEquipmentSlotCount.Armor)] - [InlineData(ItemSubType.Belt, GameConfig.MaxEquipmentSlotCount.Belt)] - [InlineData(ItemSubType.Necklace, GameConfig.MaxEquipmentSlotCount.Necklace)] - [InlineData(ItemSubType.Ring, GameConfig.MaxEquipmentSlotCount.Ring)] - public void MultipleEquipmentTest(ItemSubType type, int maxCount) - { - var previousAvatarState = _initialState.GetAvatarState(_avatar1Address); - var maxLevel = _tableSheets.CharacterLevelSheet.Max(row => row.Value.Level); - var expRow = _tableSheets.CharacterLevelSheet[maxLevel]; - var maxLevelExp = expRow.Exp; - - previousAvatarState.level = maxLevel; - previousAvatarState.exp = maxLevelExp; - - var weaponRows = _tableSheets - .EquipmentItemSheet - .Values - .Where(r => r.ItemSubType == type) - .Take(maxCount + 1); - - var equipments = new List(); - foreach (var row in weaponRows) - { - var equipment = ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet[row.Id], - new TestRandom()) - as Equipment; - - equipments.Add(equipment.ItemId); - previousAvatarState.inventory.AddItem(equipment); - } - - var state = _initialState.SetState(_avatar1Address, previousAvatarState.Serialize()); - - var action = new RankingBattle9 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - consumableIds = new List(), - }; - - Assert.Null(action.Result); - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - Random = new TestRandom(), - Rehearsal = false, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination2Test.cs b/.Lib9c.Tests/Action/RapidCombination2Test.cs deleted file mode 100644 index f90b9adbf2..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination2Test.cs +++ /dev/null @@ -1,405 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class RapidCombination2Test - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination2Test() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var material = ItemFactory.CreateMaterial( - _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass)); - avatarState.inventory.AddItem2(material); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem2(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, requiredBlockIndex); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination2 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var item = nextAvatarState.inventory.Equipments.First(); - - Assert.Empty(nextAvatarState.inventory.Materials.Select(r => r.ItemSubType == ItemSubType.Hourglass)); - Assert.Equal(equipment.ItemId, item.ItemId); - Assert.Equal(1, item.RequiredBlockIndex); - } - - [Fact] - public void ExecuteThrowCombinationSlotResultNullException() - { - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, 0); - slotState.Update(null, 0, 0); - - var tempState = _initialState - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination2 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 2)] - public void ExecuteThrowCombinationSlotUnlockException(int avatarClearedStage, int slotStateUnlockStage) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - 100); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination2 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(10, 100)] - public void ExecuteThrowRequiredBlockIndexException(int itemRequiredBlockIndex, int contextBlockIndex) - { - const int avatarClearedStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - itemRequiredBlockIndex); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, avatarClearedStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination2 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = contextBlockIndex, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(100, 101)] - public void ExecuteThrowNotEnoughMaterialException(int alreadyHasCount, int requiredCount) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var material = ItemFactory.CreateMaterial( - _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass)); - avatarState.inventory.AddItem2(material, count: alreadyHasCount); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * requiredCount; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem2(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination2 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var r = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - var result = new RapidCombination0.ResultModel((Dictionary)r.Serialize()) - { - cost = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - }; - - var r2 = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new RapidCombination0.ResultModel((Dictionary)r2.Serialize()) - { - cost = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination3Test.cs b/.Lib9c.Tests/Action/RapidCombination3Test.cs deleted file mode 100644 index c6d46c539d..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination3Test.cs +++ /dev/null @@ -1,414 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - - public class RapidCombination3Test - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination3Test() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem2(ItemFactory.CreateMaterial(row)); - avatarState.inventory.AddItem2(ItemFactory.CreateTradableMaterial(row)); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 2)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 2; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem2(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, requiredBlockIndex); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination3 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var item = nextAvatarState.inventory.Equipments.First(); - - Assert.Empty(nextAvatarState.inventory.Materials.Select(r => r.ItemSubType == ItemSubType.Hourglass)); - Assert.Equal(equipment.ItemId, item.ItemId); - Assert.Equal(1, item.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_CombinationSlotResultNullException() - { - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, 0); - slotState.Update(null, 0, 0); - - var tempState = _initialState - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination3 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 2)] - public void Execute_Throw_NotEnoughClearedStageLevelException(int avatarClearedStage, int slotStateUnlockStage) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - 100); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination3 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(10, 100)] - public void Execute_Throw_RequiredBlockIndexException(int itemRequiredBlockIndex, int contextBlockIndex) - { - const int avatarClearedStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - itemRequiredBlockIndex); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, avatarClearedStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination3 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = contextBlockIndex, - })); - } - - [Theory] - [InlineData(0, 0, 0, 1)] - [InlineData(0, 1, 2, 1)] - [InlineData(100, 0, 0, 101)] - [InlineData(0, 100, 0, 101)] - [InlineData(0, 100, 2, 101)] - [InlineData(1, 99, 2, 101)] - public void Execute_Throw_NotEnoughMaterialException(int materialCount, int tradableCount, long blockIndex, int requiredCount) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem2(ItemFactory.CreateMaterial(row), count: materialCount); - if (tradableCount > 0) - { - var material = ItemFactory.CreateTradableMaterial(row); - material.RequiredBlockIndex = blockIndex; - avatarState.inventory.AddItem2(material, count: tradableCount); - } - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * requiredCount; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem2(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination3 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var r = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - var result = new RapidCombination0.ResultModel((Dictionary)r.Serialize()) - { - cost = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - }; - - var r2 = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new RapidCombination0.ResultModel((Dictionary)r2.Serialize()) - { - cost = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination4Test.cs b/.Lib9c.Tests/Action/RapidCombination4Test.cs deleted file mode 100644 index 27ef87e61d..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination4Test.cs +++ /dev/null @@ -1,468 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class RapidCombination4Test - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination4Test() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row)); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row)); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 2)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 2; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()); - - if (backward) - { - tempState = tempState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - tempState = tempState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var action = new RapidCombination4 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var item = nextAvatarState.inventory.Equipments.First(); - - Assert.Empty(nextAvatarState.inventory.Materials.Select(r => r.ItemSubType == ItemSubType.Hourglass)); - Assert.Equal(equipment.ItemId, item.ItemId); - Assert.Equal(1, item.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_CombinationSlotResultNullException() - { - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, 0); - slotState.Update(null, 0, 0); - - var tempState = _initialState - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination4 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 2)] - public void Execute_Throw_NotEnoughClearedStageLevelException(int avatarClearedStage, int slotStateUnlockStage) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - 100); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination4 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(10, 100)] - public void Execute_Throw_RequiredBlockIndexException(int itemRequiredBlockIndex, int contextBlockIndex) - { - const int avatarClearedStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - itemRequiredBlockIndex); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, avatarClearedStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination4 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = contextBlockIndex, - })); - } - - [Theory] - [InlineData(0, 0, 0, 1)] - [InlineData(0, 1, 2, 1)] - [InlineData(100, 0, 0, 101)] - [InlineData(0, 100, 0, 101)] - [InlineData(0, 100, 2, 101)] - [InlineData(1, 99, 2, 101)] - public void Execute_Throw_NotEnoughMaterialException(int materialCount, int tradableCount, long blockIndex, int requiredCount) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: materialCount); - if (tradableCount > 0) - { - var material = ItemFactory.CreateTradableMaterial(row); - material.RequiredBlockIndex = blockIndex; - avatarState.inventory.AddItem(material, count: tradableCount); - } - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * requiredCount; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination4 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Fact] - public void Rehearsal() - { - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
() - { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - slotAddress, - }; - - var state = new MockStateDelta(); - - var action = new RapidCombination4 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var r = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - var result = new RapidCombination0.ResultModel((Dictionary)r.Serialize()) - { - cost = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - }; - - var r2 = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new RapidCombination0.ResultModel((Dictionary)r2.Serialize()) - { - cost = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination6Test.cs b/.Lib9c.Tests/Action/RapidCombination6Test.cs deleted file mode 100644 index 54c19fd085..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination6Test.cs +++ /dev/null @@ -1,722 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class RapidCombination6Test - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination6Test() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), 83); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row), 100); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 183)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 200; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()); - - if (backward) - { - tempState = tempState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - tempState = tempState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var item = nextAvatarState.inventory.Equipments.First(); - - Assert.Empty(nextAvatarState.inventory.Materials.Select(r => r.ItemSubType == ItemSubType.Hourglass)); - Assert.Equal(equipment.ItemId, item.ItemId); - Assert.Equal(51, item.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_CombinationSlotResultNullException() - { - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, 0); - slotState.Update(null, 0, 0); - - var tempState = _initialState - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 2)] - public void Execute_Throw_NotEnoughClearedStageLevelException(int avatarClearedStage, int slotStateUnlockStage) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - 100); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(10, 100)] - public void Execute_Throw_RequiredBlockIndexException(int itemRequiredBlockIndex, int contextBlockIndex) - { - const int avatarClearedStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - itemRequiredBlockIndex); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, avatarClearedStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = contextBlockIndex, - })); - } - - [Theory] - [InlineData(0, 0, 0, 40)] - [InlineData(0, 1, 2, 40)] - [InlineData(22, 0, 0, 40)] - [InlineData(0, 22, 0, 40)] - [InlineData(0, 22, 2, 40)] - [InlineData(2, 10, 2, 40)] - public void Execute_Throw_NotEnoughMaterialException(int materialCount, int tradableCount, long blockIndex, int requiredCount) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: materialCount); - if (tradableCount > 0) - { - var material = ItemFactory.CreateTradableMaterial(row); - material.RequiredBlockIndex = blockIndex; - avatarState.inventory.AddItem(material, count: tradableCount); - } - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * requiredCount; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - })); - } - - [Fact] - public void Rehearsal() - { - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
() - { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - slotAddress, - }; - - var state = new MockStateDelta(); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var r = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - var result = new RapidCombination0.ResultModel((Dictionary)r.Serialize()) - { - cost = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - }; - - var r2 = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new RapidCombination0.ResultModel((Dictionary)r2.Serialize()) - { - cost = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - - [Fact] - public void Execute_Throw_RequiredAppraiseBlockException() - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: 22); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 40; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(7, false)] - [InlineData(9, true)] - [InlineData(10, true)] - [InlineData(11, false)] - public void Execute_Throw_InvalidOperationException_When_TargetSlotCreatedBy( - int itemEnhancementResultModelNumber, - bool shouldThrow) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), 83); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row), 100); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 183)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet - .OrderedList.First(e => e.Grade >= 1); - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 200; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - var materialEquipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - avatarState.inventory.AddItem(materialEquipment); - - AttachmentActionResult resultModel = null; - var random = new TestRandom(); - var mailId = random.GenerateRandomGuid(); - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - switch (itemEnhancementResultModelNumber) - { - case 7: - { - equipment = ItemEnhancement7.UpgradeEquipment(equipment); - resultModel = new ItemEnhancement7.ResultModel - { - id = mailId, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - }; - - break; - } - - case 9: - { - Assert.True(ItemEnhancement9.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement9.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement9.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement9.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement9.EnhancementResult.GreatSuccess, - }; - - break; - } - - case 10: - { - Assert.True(ItemEnhancement10.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement10.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement10.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement10.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement10.EnhancementResult.GreatSuccess, - }; - - break; - } - - case 11: - { - Assert.True(ItemEnhancement.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, - CRYSTAL = 0 * CrystalCalculator.CRYSTAL, - }; - - break; - } - - default: - break; - } - - // NOTE: Do not update `mail`, because this test assumes that the `mail` was removed. - { - // var mail = new ItemEnhanceMail(resultModel, 0, random.GenerateRandomGuid(), requiredBlockIndex); - // avatarState.Update(mail); - } - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(resultModel, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new RapidCombination6 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - if (shouldThrow) - { - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - }); - } - else - { - action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - } - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination8Test.cs b/.Lib9c.Tests/Action/RapidCombination8Test.cs deleted file mode 100644 index 9666c06142..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination8Test.cs +++ /dev/null @@ -1,706 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class RapidCombination8Test - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination8Test() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), 83); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row), 100); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 183)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 200; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()); - - if (backward) - { - tempState = tempState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - tempState = tempState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var item = nextAvatarState.inventory.Equipments.First(); - - Assert.Empty(nextAvatarState.inventory.Materials.Select(r => r.ItemSubType == ItemSubType.Hourglass)); - Assert.Equal(equipment.ItemId, item.ItemId); - Assert.Equal(51, item.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_CombinationSlotResultNullException() - { - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, 0); - slotState.Update(null, 0, 0); - - var tempState = _initialState - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 2)] - public void Execute_Throw_NotEnoughClearedStageLevelException(int avatarClearedStage, int slotStateUnlockStage) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - 100); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(10, 100)] - public void Execute_Throw_RequiredBlockIndexException(int itemRequiredBlockIndex, int contextBlockIndex) - { - const int avatarClearedStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - itemRequiredBlockIndex); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, avatarClearedStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = contextBlockIndex, - })); - } - - [Theory] - [InlineData(0, 0, 0, 40)] - [InlineData(0, 1, 2, 40)] - [InlineData(22, 0, 0, 40)] - [InlineData(0, 22, 0, 40)] - [InlineData(0, 22, 2, 40)] - [InlineData(2, 10, 2, 40)] - public void Execute_Throw_NotEnoughMaterialException(int materialCount, int tradableCount, long blockIndex, int requiredCount) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: materialCount); - if (tradableCount > 0) - { - var material = ItemFactory.CreateTradableMaterial(row); - material.RequiredBlockIndex = blockIndex; - avatarState.inventory.AddItem(material, count: tradableCount); - } - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * requiredCount; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - })); - } - - [Fact] - public void Rehearsal() - { - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
() - { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - slotAddress, - }; - - var state = new MockStateDelta(); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var r = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - var result = new RapidCombination0.ResultModel((Dictionary)r.Serialize()) - { - cost = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - }; - - var r2 = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new RapidCombination0.ResultModel((Dictionary)r2.Serialize()) - { - cost = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - - [Fact] - public void Execute_Throw_RequiredAppraiseBlockException() - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: 22); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 40; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(7)] - [InlineData(9)] - [InlineData(10)] - [InlineData(11)] - public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( - int itemEnhancementResultModelNumber) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), 83); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row), 100); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 183)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet - .OrderedList.First(e => e.Grade >= 1); - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 200; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - var materialEquipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - avatarState.inventory.AddItem(materialEquipment); - - AttachmentActionResult resultModel = null; - var random = new TestRandom(); - var mailId = random.GenerateRandomGuid(); - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - switch (itemEnhancementResultModelNumber) - { - case 7: - { - equipment = ItemEnhancement7.UpgradeEquipment(equipment); - resultModel = new ItemEnhancement7.ResultModel - { - id = mailId, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - }; - - break; - } - - case 9: - { - Assert.True(ItemEnhancement9.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement9.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement9.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement9.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement9.EnhancementResult.GreatSuccess, - }; - - break; - } - - case 10: - { - Assert.True(ItemEnhancement10.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement10.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement10.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement10.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement10.EnhancementResult.GreatSuccess, - }; - - break; - } - - case 11: - { - Assert.True(ItemEnhancement.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, - CRYSTAL = 0 * CrystalCalculator.CRYSTAL, - }; - - break; - } - - default: - break; - } - - // NOTE: Do not update `mail`, because this test assumes that the `mail` was removed. - { - // var mail = new ItemEnhanceMail(resultModel, 0, random.GenerateRandomGuid(), requiredBlockIndex); - // avatarState.Update(mail); - } - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(resultModel, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new RapidCombination8 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombinationTest7.cs b/.Lib9c.Tests/Action/RapidCombinationTest7.cs deleted file mode 100644 index 917bfd5687..0000000000 --- a/.Lib9c.Tests/Action/RapidCombinationTest7.cs +++ /dev/null @@ -1,706 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class RapidCombinationTest7 - { - private readonly IAccountStateDelta _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombinationTest7() - { - _initialState = new MockStateDelta(); - - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute(bool backward) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), 83); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row), 100); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 183)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 200; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()); - - if (backward) - { - tempState = tempState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - tempState = tempState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - var item = nextAvatarState.inventory.Equipments.First(); - - Assert.Empty(nextAvatarState.inventory.Materials.Select(r => r.ItemSubType == ItemSubType.Hourglass)); - Assert.Equal(equipment.ItemId, item.ItemId); - Assert.Equal(51, item.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_CombinationSlotResultNullException() - { - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, 0); - slotState.Update(null, 0, 0); - - var tempState = _initialState - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 1)] - [InlineData(1, 2)] - public void Execute_Throw_NotEnoughClearedStageLevelException(int avatarClearedStage, int slotStateUnlockStage) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - 100); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(10, 100)] - public void Execute_Throw_RequiredBlockIndexException(int itemRequiredBlockIndex, int contextBlockIndex) - { - const int avatarClearedStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - avatarClearedStage); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - itemRequiredBlockIndex); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, avatarClearedStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = contextBlockIndex, - })); - } - - [Theory] - [InlineData(0, 0, 0, 40)] - [InlineData(0, 1, 2, 40)] - [InlineData(22, 0, 0, 40)] - [InlineData(0, 22, 0, 40)] - [InlineData(0, 22, 2, 40)] - [InlineData(2, 10, 2, 40)] - public void Execute_Throw_NotEnoughMaterialException(int materialCount, int tradableCount, long blockIndex, int requiredCount) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: materialCount); - if (tradableCount > 0) - { - var material = ItemFactory.CreateTradableMaterial(row); - material.RequiredBlockIndex = blockIndex; - avatarState.inventory.AddItem(material, count: tradableCount); - } - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * requiredCount; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - })); - } - - [Fact] - public void Rehearsal() - { - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - - var updatedAddresses = new List
() - { - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - slotAddress, - }; - - var state = new MockStateDelta(); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - - [Theory] - [InlineData(null)] - [InlineData(1)] - public void ResultModelDeterministic(int? subRecipeId) - { - var row = _tableSheets.MaterialItemSheet.Values.First(); - var row2 = _tableSheets.MaterialItemSheet.Values.Last(); - - Assert.True(row.Id < row2.Id); - - var material = ItemFactory.CreateMaterial(row); - var material2 = ItemFactory.CreateMaterial(row2); - - var itemUsable = ItemFactory.CreateItemUsable(_tableSheets.EquipmentItemSheet.Values.First(), default, 0); - var r = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - itemUsable = itemUsable, - }; - var result = new RapidCombination0.ResultModel((Dictionary)r.Serialize()) - { - cost = new Dictionary - { - [material] = 1, - [material2] = 1, - }, - }; - - var r2 = new CombinationConsumable5.ResultModel - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - subRecipeId = subRecipeId, - materials = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - itemUsable = itemUsable, - }; - - var result2 = new RapidCombination0.ResultModel((Dictionary)r2.Serialize()) - { - cost = new Dictionary - { - [material2] = 1, - [material] = 1, - }, - }; - - Assert.Equal(result.Serialize(), result2.Serialize()); - } - - [Fact] - public void Execute_Throw_RequiredAppraiseBlockException() - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), count: 22); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet.First; - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 40; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = 0, - gold = 0, - materials = new Dictionary(), - itemUsable = equipment, - recipeId = 0, - itemType = ItemType.Equipment, - }; - - var mail = new CombinationMail(result, 0, default, requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(result, 0, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(7)] - [InlineData(9)] - [InlineData(10)] - [InlineData(11)] - public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( - int itemEnhancementResultModelNumber) - { - const int slotStateUnlockStage = 1; - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.worldInformation = new WorldInformation( - 0, - _initialState.GetSheet(), - slotStateUnlockStage); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.Hourglass); - avatarState.inventory.AddItem(ItemFactory.CreateMaterial(row), 83); - avatarState.inventory.AddItem(ItemFactory.CreateTradableMaterial(row), 100); - Assert.True(avatarState.inventory.HasFungibleItem(row.ItemId, 0, 183)); - - var firstEquipmentRow = _tableSheets.EquipmentItemSheet - .OrderedList.First(e => e.Grade >= 1); - Assert.NotNull(firstEquipmentRow); - - var gameConfigState = _initialState.GetGameConfigState(); - var requiredBlockIndex = gameConfigState.HourglassPerBlock * 200; - var equipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - var materialEquipment = (Equipment)ItemFactory.CreateItemUsable( - firstEquipmentRow, - Guid.NewGuid(), - requiredBlockIndex); - avatarState.inventory.AddItem(equipment); - avatarState.inventory.AddItem(materialEquipment); - - AttachmentActionResult resultModel = null; - var random = new TestRandom(); - var mailId = random.GenerateRandomGuid(); - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - switch (itemEnhancementResultModelNumber) - { - case 7: - { - equipment = ItemEnhancement7.UpgradeEquipment(equipment); - resultModel = new ItemEnhancement7.ResultModel - { - id = mailId, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - }; - - break; - } - - case 9: - { - Assert.True(ItemEnhancement9.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement9.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement9.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement9.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement9.EnhancementResult.GreatSuccess, - }; - - break; - } - - case 10: - { - Assert.True(ItemEnhancement10.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement10.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement10.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement10.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement10.EnhancementResult.GreatSuccess, - }; - - break; - } - - case 11: - { - Assert.True(ItemEnhancement.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, - CRYSTAL = 0 * CrystalCalculator.CRYSTAL, - }; - - break; - } - - default: - break; - } - - // NOTE: Do not update `mail`, because this test assumes that the `mail` was removed. - { - // var mail = new ItemEnhanceMail(resultModel, 0, random.GenerateRandomGuid(), requiredBlockIndex); - // avatarState.Update(mail); - } - - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0)); - var slotState = new CombinationSlotState(slotAddress, slotStateUnlockStage); - slotState.Update(resultModel, 0, requiredBlockIndex); - - var tempState = _initialState.SetState(slotAddress, slotState.Serialize()) - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new RapidCombination7 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs index 484ab70d43..2803f20638 100644 --- a/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs @@ -122,37 +122,6 @@ public IAccountStateDelta JoinArena( return _state; } - public IAccountStateDelta BattleArena( - IRandom random, - Address signer, - Address myAvatarAddress, - Address enemyAvatarAddress, - ArenaSheet.RoundData roundData, - int ticket, - long blockIndex) - { - var action = new BattleArena6() - { - myAvatarAddress = myAvatarAddress, - enemyAvatarAddress = enemyAvatarAddress, - championshipId = roundData.ChampionshipId, - round = roundData.Round, - ticket = ticket, - costumes = new List(), - equipments = new List(), - }; - - _state = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = signer, - Random = random, - Rehearsal = false, - BlockIndex = blockIndex, - }); - return _state; - } - public (AgentState Agent, AvatarState Avatar) CreateAccount() { var clearStageId = Math.Max( diff --git a/.Lib9c.Tests/Action/Scenario/CombinationAndRapidCombinationTest.cs b/.Lib9c.Tests/Action/Scenario/CombinationAndRapidCombinationTest.cs index 10b46a0797..43a6c39a14 100644 --- a/.Lib9c.Tests/Action/Scenario/CombinationAndRapidCombinationTest.cs +++ b/.Lib9c.Tests/Action/Scenario/CombinationAndRapidCombinationTest.cs @@ -275,7 +275,7 @@ public void Case(int randomSeed, int[] optionNumbers) Assert.Equal(hourglassCount, hourglasses.Sum(e => e.count)); nextState = nextState.SetState(_inventoryAddress, inventoryState.Serialize()); - var rapidCombinationAction = new RapidCombination8 + var rapidCombinationAction = new RapidCombination { avatarAddress = _avatarAddress, slotIndex = 0, diff --git a/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs b/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs deleted file mode 100644 index d76c806ede..0000000000 --- a/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs +++ /dev/null @@ -1,203 +0,0 @@ -namespace Lib9c.Tests.Action.Scenario -{ - using System; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static SerializeKeys; - - public class SellAndCancellationAndSellTest - { - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly IAccountStateDelta _initialState; - - public SellAndCancellationAndSellTest(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); -#pragma warning restore CS0618 - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - - _agentAddress = new PrivateKey().ToAddress(); - _avatarAddress = _agentAddress.Derive("avatar"); - var agentState = new AgentState(_agentAddress); - agentState.avatarAddresses[0] = _avatarAddress; - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 1, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - - _initialState = new Tests.Action.MockStateDelta() - .SetState(GoldCurrencyState.Address, gold.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Fact] - public void Execute_With_TradableMaterial() - { - var previousStates = _initialState; - var apStoneRow = _tableSheets.MaterialItemSheet.OrderedList!.First(row => - row.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateTradableMaterial(apStoneRow); - var inventoryAddr = _avatarAddress.Derive(LegacyInventoryKey); - var inventory = new Inventory((List)previousStates.GetState(inventoryAddr)); - // Add 10 ap stones to inventory. - inventory.AddFungibleItem(apStone, 10); - previousStates = previousStates.SetState(inventoryAddr, inventory.Serialize()); - - // sell ap stones with count 1, 2, 3, 4. - var sellBlockIndex = 1L; - var random = new TestRandom(); - var orderIds = Enumerable.Range(0, 4).Select(_ => Guid.NewGuid()).ToArray(); - var sellActions = new[] - { - GetSell(apStone, 1, orderIds[0]), - GetSell(apStone, 2, orderIds[1]), - GetSell(apStone, 3, orderIds[2]), - GetSell(apStone, 4, orderIds[3]), - }; - var nextStates = previousStates; - foreach (var sellAction in sellActions) - { - nextStates = sellAction.Execute(new ActionContext - { - Signer = _agentAddress, - PreviousState = nextStates, - BlockIndex = sellBlockIndex, - Random = random, - Rehearsal = false, - }); - // TODO: Check state.. inventory, orders.. - } - - // Check inventory does not have ap stones. - var nextInventory = new Inventory((List)nextStates.GetState(inventoryAddr)); - Assert.False(nextInventory.RemoveFungibleItem( - apStone.FungibleId, - sellBlockIndex, - 1)); - - // Cancel sell orders. - var sellCancellationActions = new[] - { - GetSellCancellation(orderIds[0], apStone), - GetSellCancellation(orderIds[1], apStone), - GetSellCancellation(orderIds[2], apStone), - GetSellCancellation(orderIds[3], apStone), - }; - foreach (var sellCancellationAction in sellCancellationActions) - { - nextStates = sellCancellationAction.Execute(new ActionContext - { - Signer = _agentAddress, - PreviousState = nextStates, - BlockIndex = sellBlockIndex + 1L, - Random = random, - Rehearsal = false, - }); - // TODO: Check state.. inventory, orders.. - } - - // Check inventory has 10 ap stones. - nextInventory = new Inventory((List)nextStates.GetState(inventoryAddr)); - Assert.True(nextInventory.RemoveFungibleItem( - apStone.FungibleId, - sellBlockIndex + 1L, - 10)); - - // Sell 10 ap stones at once. - var newSellOrderId = Guid.NewGuid(); - var newSellAction = GetSell(apStone, 10, newSellOrderId); - nextStates = newSellAction.Execute(new ActionContext - { - Signer = _agentAddress, - PreviousState = nextStates, - BlockIndex = sellBlockIndex + 2L, - Random = random, - Rehearsal = false, - }); - - // Check inventory does not have ap stones. - nextInventory = new Inventory((List)nextStates.GetState(inventoryAddr)); - Assert.False(nextInventory.RemoveFungibleItem( - apStone.FungibleId, - sellBlockIndex + 2L, - 1)); - } - - private Sell GetSell(ITradableItem tradableItem, int count, Guid orderId) => - new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = count, - price = new FungibleAssetValue( -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - Currency.Legacy("NCG", 2, null), -#pragma warning restore CS0618 - 1, - 0), - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - - private SellCancellation GetSellCancellation(Guid orderId, ITradableItem tradableItem) => - new SellCancellation - { - orderId = orderId, - tradableId = tradableItem.TradableId, - sellerAvatarAddress = _avatarAddress, - itemSubType = tradableItem.ItemSubType, - }; - } -} diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs deleted file mode 100644 index 50e73eed00..0000000000 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs +++ /dev/null @@ -1,932 +0,0 @@ -namespace Lib9c.Tests.Action.Scenario -{ - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - using State = Lib9c.Tests.Action.MockStateDelta; - - public class StakeAndClaimStakeReward2ScenarioTest - { - private readonly IAccountStateDelta _initialState; - private readonly Currency _currency; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - private readonly Address _signerAddress; - private readonly Address _avatarAddress; - - public StakeAndClaimStakeReward2ScenarioTest(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new State(); - - var sheets = TableSheetsImporter.ImportSheets(); - var sheet = @"level,required_gold,item_id,rate -1,50,400000,10 -1,50,500000,800 -2,500,400000,8 -2,500,500000,800 -3,5000,400000,5 -3,5000,500000,800 -4,50000,400000,5 -4,50000,500000,800 -5,500000,400000,5 -5,500000,500000,800".Serialize(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), key == nameof(StakeRegularRewardSheet) ? sheet : value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(_currency); - - _signerAddress = new PrivateKey().ToAddress(); - var stakeStateAddress = StakeState.DeriveAddress(_signerAddress); - var agentState = new AgentState(_signerAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = _avatarAddress.Derive("ranking_map"); - agentState.avatarAddresses.Add(0, _avatarAddress); - var avatarState = new AvatarState( - _avatarAddress, - _signerAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - rankingMapAddress - ) - { - level = 100, - }; - _initialState = _initialState - .SetState(_signerAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState( - _avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - _avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()) - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()); - } - - public static IEnumerable StakeAndClaimStakeRewardTestCases() - { - // 일반적인 보상수령 확인 - // 1단계 수준(50~499NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 10 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50, - new[] - { - (400000, 5), - (500000, 1), - }, - 50400, - }; - yield return new object[] - { - 499, - new[] - { - (400000, 49), - (500000, 1), - }, - 50400, - }; - - // 2단계 수준(500~4,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 8 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500, - new[] - { - (400000, 62), - (500000, 2), - }, - 50400, - }; - yield return new object[] - { - 799, - new[] - { - (400000, 99), - (500000, 2), - }, - 50400, - }; - yield return new object[] - { - 4999, - new[] - { - (400000, 624), - (500000, 8), - }, - 50400, - }; - - // 3단계 수준(5,000~49,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 5000, - new[] - { - (400000, 1000), - (500000, 8), - }, - 50400, - }; - yield return new object[] - { - 49999, - new[] - { - (400000, 9999), - (500000, 64), - }, - 50400, - }; - - // 4단계 수준(50,000~499,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50000, - new[] - { - (400000, 10000), - (500000, 64), - }, - 50400, - }; - yield return new object[] - { - 499999, - new[] - { - (400000, 99999), - (500000, 626), - }, - 50400, - }; - - // 5단계 수준(500,000~100,000,000NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500000, - new[] - { - (400000, 100000), - (500000, 627), - }, - 50400, - }; - yield return new object[] - { - 99999999, - new[] - { - (400000, 19999999), - (500000, 125001), - }, - 50400, - }; - - // 지연된 보상수령 확인 - // 1단계 수준(50~499NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 50 NCG, 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50, - new[] - { - (400000, 45), - (500000, 9), - }, - 500800, - }; - yield return new object[] - { - 499, - new[] - { - (400000, 441), - (500000, 9), - }, - 500800, - }; - - // 2단계 수준(500~4,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 8 NCG, 2 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500, - new[] - { - (400000, 558), - (500000, 18), - }, - 500800, - }; - yield return new object[] - { - 799, - new[] - { - (400000, 891), - (500000, 18), - }, - 500800, - }; - yield return new object[] - { - 4999, - new[] - { - (400000, 5616), - (500000, 72), - }, - 500800, - }; - - // 3단계 수준(5,000~49,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 5 NCG, 2 ap portion / 180 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 5000, - new[] - { - (400000, 9000), - (500000, 72), - }, - 500800, - }; - yield return new object[] - { - 49999, - new[] - { - (400000, 89991), - (500000, 576), - }, - 500800, - }; - - // 4단계 수준(50,000~499,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 5 NCG, 2 ap portion / 180 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50000, - new[] - { - (400000, 90000), - (500000, 576), - }, - 500800, - }; - yield return new object[] - { - 499999, - new[] - { - (400000, 899991), - (500000, 5634), - }, - 500800, - }; - - // 5단계 수준(500,000~4,999,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 160 NCG 소수점 버림, 기존 deposit 유지 확인) - // 5단계 수준(500,000~500,000,000NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 5 NCG, 2 ap portion / 160 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500000, - new[] - { - (400000, 900000), - (500000, 5643), - }, - 500800, - }; - yield return new object[] - { - 4999999, - new[] - { - (400000, 8999991), - (500000, 56259), - }, - 500800, - }; - } - - public static IEnumerable StakeLessAfterLockupTestcases() - { - (long ClaimBlockIndex, (int ItemId, int Amount)[])[] BuildEvents( - int hourglassRate, - int apPotionRate, - int apPotionBonus, - long stakeAmount) - { - const int hourglassItemId = 400000, apPotionItemId = 500000; - return new[] - { - (StakeState.RewardInterval, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - (StakeState.RewardInterval * 2, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - (StakeState.RewardInterval * 3, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - (StakeState.RewardInterval * 4, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - }; - } - - // 1단계 수준(50~499NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 50 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 51, - 51, - 50, - BuildEvents(10, 800, 1, 50), - }; - yield return new object[] - { - 499, - 499, - 50, - BuildEvents(10, 800, 1, 499), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 50, - 50, - 0, - BuildEvents(10, 800, 1, 50), - }; - yield return new object[] - { - 499, - 499, - 0, - BuildEvents(10, 800, 1, 499), - }; - - // 2단계 수준(500~4,999NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 8 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 500, - 500, - 499, - BuildEvents(8, 800, 2, 500), - }; - yield return new object[] - { - 4999, - 4999, - 500, - BuildEvents(8, 800, 2, 4999), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 500, - 500, - 0, - BuildEvents(8, 800, 2, 500), - }; - yield return new object[] - { - 4999, - 4999, - 0, - BuildEvents(8, 800, 2, 4999), - }; - - // 3단계 수준(5,000~49,999NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 5000, - 5000, - 4999, - BuildEvents(5, 800, 2, 5000), - }; - yield return new object[] - { - 49999, - 49999, - 5000, - BuildEvents(5, 800, 2, 49999), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 5000, - 5000, - 0, - BuildEvents(5, 800, 2, 5000), - }; - yield return new object[] - { - 49999, - 49999, - 0, - BuildEvents(5, 800, 2, 49999), - }; - - // 4단계 수준(50,000~499,999NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 50000, - 50000, - 49999, - BuildEvents(5, 800, 2, 50000), - }; - yield return new object[] - { - 499999, - 499999, - 50000, - BuildEvents(5, 800, 2, 499999), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 50000, - 50000, - 0, - BuildEvents(5, 800, 2, 50000), - }; - yield return new object[] - { - 499999, - 499999, - 0, - BuildEvents(5, 800, 2, 499999), - }; - - // 5단계 수준(500,000~100,000,000NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 500000, - 500000, - 499999, - BuildEvents(5, 800, 2, 500000), - }; - yield return new object[] - { - 500000000, - 500000000, - 500000, - BuildEvents(5, 800, 2, 500000000), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 500000, - 500000, - 0, - BuildEvents(5, 800, 2, 500000), - }; - yield return new object[] - { - 500000000, - 500000000, - 0, - BuildEvents(5, 800, 2, 500000000), - }; - } - - [Theory] - [MemberData(nameof(StakeAndClaimStakeRewardTestCases))] - public void StakeAndClaimStakeReward(long stakeAmount, (int ItemId, int Amount)[] expectedItems, long receiveBlockIndex) - { - var context = new ActionContext(); - var states = _initialState.MintAsset(context, _signerAddress, _currency * stakeAmount); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = 0, - }); - - Assert.True(states.TryGetStakeState(_signerAddress, out StakeState stakeState)); - Assert.NotNull(stakeState); - - action = new ClaimStakeReward2(_avatarAddress); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = receiveBlockIndex, - }); - - // 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - var avatarState = states.GetAvatarStateV2(_avatarAddress); - foreach ((int itemId, int amount) in expectedItems) - { - Assert.True(avatarState.inventory.HasItem(itemId, amount)); - } - - // 기존 deposit 유지 확인 - Assert.Equal( - _currency * stakeAmount, - states.GetBalance(stakeState.address, _currency)); - } - - [Theory] - [InlineData(500, 50, 499, StakeState.LockupInterval - 1)] - [InlineData(500, 499, 500, StakeState.LockupInterval - 1)] - [InlineData(5000, 500, 4999, StakeState.LockupInterval - 1)] - [InlineData(5000, 4999, 5000, StakeState.LockupInterval - 1)] - [InlineData(50000, 5000, 49999, StakeState.LockupInterval - 1)] - [InlineData(50000, 49999, 50000, StakeState.LockupInterval - 1)] - [InlineData(500000, 50000, 499999, StakeState.LockupInterval - 1)] - [InlineData(500000, 499999, 500000, StakeState.LockupInterval - 1)] - [InlineData(500000000, 500000, 500000000, StakeState.LockupInterval - 1)] - public void StakeAndStakeMore(long initialBalance, long stakeAmount, long newStakeAmount, long newStakeBlockIndex) - { - // Validate testcases - Assert.True(newStakeBlockIndex < StakeState.LockupInterval); - Assert.True(stakeAmount < newStakeAmount); - - var context = new ActionContext(); - var states = _initialState.MintAsset(context, _signerAddress, _currency * initialBalance); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = 0, - }); - - Assert.True(states.TryGetStakeState(_signerAddress, out StakeState stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _currency * (initialBalance - stakeAmount), - states.GetBalance(_signerAddress, _currency)); - Assert.Equal( - _currency * stakeAmount, - states.GetBalance(stakeState.address, _currency)); - - action = new ClaimStakeReward2(_avatarAddress); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = newStakeBlockIndex, - }); - - action = new Stake(newStakeAmount); - // 스테이킹 추가는 가능 - // 락업기간 이전에 deposit을 추가해서 save 할 수 있는지 - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = newStakeBlockIndex, - }); - - Assert.True(states.TryGetStakeState(_signerAddress, out stakeState)); - Assert.NotNull(stakeState); - // 쌓여있던 보상 타이머가 정상적으로 리셋되는지 - Assert.Equal(newStakeBlockIndex, stakeState.StartedBlockIndex); - // 락업기간이 새롭게 201,600으로 갱신되는지 확인 - Assert.Equal( - newStakeBlockIndex + StakeState.LockupInterval, - stakeState.CancellableBlockIndex); - Assert.Equal( - _currency * (initialBalance - newStakeAmount), - states.GetBalance(_signerAddress, _currency)); - // 기존보다 초과해서 설정한 deposit 으로 묶인 상태 갱신된 것 확인 - Assert.Equal( - _currency * newStakeAmount, - states.GetBalance(stakeState.address, _currency)); - } - - [Theory] - [InlineData(500, 51, 50)] - [InlineData(500, 499, 50)] - [InlineData(5000, 500, 499)] - [InlineData(5000, 500, 50)] - [InlineData(5000, 4999, 500)] - [InlineData(50000, 5000, 4999)] - [InlineData(50000, 49999, 5000)] - [InlineData(500000, 50000, 49999)] - [InlineData(500000, 499999, 50000)] - [InlineData(500000000, 500000, 99999)] - [InlineData(500000000, 500000000, 0)] - [InlineData(500000000, 500000000, 99999999)] - public void StakeAndStakeLess(long initialBalance, long stakeAmount, long newStakeAmount) - { - // Validate testcases - Assert.True(initialBalance >= stakeAmount); - Assert.True(newStakeAmount < stakeAmount); - - var context = new ActionContext(); - var states = _initialState.MintAsset(context, _signerAddress, _currency * initialBalance); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = 0, - }); - - Assert.True(states.TryGetStakeState(_signerAddress, out StakeState stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _currency * (initialBalance - stakeAmount), - states.GetBalance(_signerAddress, _currency)); - Assert.Equal( - _currency * stakeAmount, - states.GetBalance(stakeState.address, _currency)); - - action = new ClaimStakeReward2(_avatarAddress); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = StakeState.LockupInterval - 1, - }); - - action = new Stake(newStakeAmount); - // 락업기간 이전에 deposit을 감소해서 save할때 락업되어 거부되는가 - Assert.Throws(() => states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = StakeState.LockupInterval - 1, - })); - - Assert.True(states.TryGetStakeState(_signerAddress, out stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _currency * (initialBalance - stakeAmount), - states.GetBalance(_signerAddress, _currency)); - Assert.Equal( - _currency * stakeAmount, - states.GetBalance(stakeState.address, _currency)); - } - - [Theory] - [MemberData(nameof(StakeLessAfterLockupTestcases))] - // 락업기간 종료 이후 deposit을 현재보다 낮게 설정했을때, 설정이 잘되서 새롭게 락업되는지 확인 - // 락업기간 종료 이후 보상 수령하고 락업해제되는지 확인 - public void StakeLessAfterLockup(long initialBalance, long stakeAmount, long newStakeAmount, (long ClaimBlockIndex, (int ItemId, int Amount)[] ExpectedItems)[] claimEvents) - { - StakeState stakeState; - - // Validate testcases - Assert.True(stakeAmount > newStakeAmount); - - var context = new ActionContext(); - var states = _initialState.MintAsset(context, _signerAddress, _currency * initialBalance); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = 0, - }); - - // 1~3회까지 모든 보상을 수령함 - // 201,600 블록 도달 이후 → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - foreach ((long claimBlockIndex, (int itemId, int amount)[] expectedItems) in claimEvents) - { - action = new ClaimStakeReward2(_avatarAddress); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = claimBlockIndex, - }); - - var avatarState = states.GetAvatarStateV2(_avatarAddress); - foreach ((int itemId, int amount) in expectedItems) - { - Assert.True(avatarState.inventory.HasItem(itemId, amount)); - } - - Assert.True(states.TryGetStakeState(_signerAddress, out stakeState)); - Assert.NotNull(stakeState); - // deposit 유지 확인 - Assert.Equal( - _currency * stakeAmount, - states.GetBalance(stakeState.address, _currency)); - } - - action = new Stake(newStakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = StakeState.LockupInterval, - }); - - // Setup staking again. - if (newStakeAmount > 0) - { - Assert.True(states.TryGetStakeState(_signerAddress, out stakeState)); - Assert.NotNull(stakeState); - // 쌓여있던 보상 타이머가 정상적으로 리셋되는지 - Assert.Equal(StakeState.LockupInterval, stakeState.StartedBlockIndex); - // 락업기간이 새롭게 201,600으로 갱신되는지 확인 - Assert.Equal( - StakeState.LockupInterval + StakeState.LockupInterval, - stakeState.CancellableBlockIndex); - // 기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인 - Assert.Equal( - _currency * (initialBalance - newStakeAmount), - states.GetBalance(_signerAddress, _currency)); - Assert.Equal( - _currency * newStakeAmount, - states.GetBalance(stakeState.address, _currency)); - - Assert.Throws(() => - { - // 현재 스테이킹된 NCG를 인출할 수 없다 - action = new ClaimStakeReward2(_avatarAddress); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = StakeState.LockupInterval + 1, - }); - }); - // 현재 deposit 묶인 상태 확인 - Assert.Equal( - _currency * newStakeAmount, - states.GetBalance(stakeState.address, _currency)); - } - else - { - Assert.Equal( - _currency * initialBalance, - states.GetBalance(_signerAddress, _currency)); - Assert.False(states.TryGetStakeState(_signerAddress, out stakeState)); - Assert.Null(stakeState); - } - } - - [Fact] - public void StakeAndClaimStakeRewardBeforeRewardInterval() - { - var context = new ActionContext(); - var states = _initialState.MintAsset(context, _signerAddress, _currency * 500); - IAction action = new Stake(500); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = 0, - }); - - action = new ClaimStakeReward2(_avatarAddress); - Assert.Throws(() => states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _signerAddress, - BlockIndex = StakeState.RewardInterval - 1, - })); - - var avatarState = states.GetAvatarStateV2(_avatarAddress); - Assert.Empty(avatarState.inventory.Items.Where(x => x.item.Id == 400000)); - Assert.Empty(avatarState.inventory.Items.Where(x => x.item.Id == 500000)); - } - } -} diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward3ScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward3ScenarioTest.cs deleted file mode 100644 index f7e5ee9377..0000000000 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward3ScenarioTest.cs +++ /dev/null @@ -1,909 +0,0 @@ -namespace Lib9c.Tests.Action.Scenario -{ - using System.Collections.Generic; - using System.Linq; - using Lib9c.Tests.Util; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class StakeAndClaimStakeReward3ScenarioTest - { - private readonly Address _agentAddr; - private readonly Address _avatarAddr; - private readonly IAccountStateDelta _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public StakeAndClaimStakeReward3ScenarioTest(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - ( - _, - _agentAddr, - _avatarAddr, - _, - _initialStatesWithAvatarStateV2) = InitializeUtil.InitializeStates(); - _ncg = _initialStatesWithAvatarStateV2.GetGoldCurrency(); - } - - public static IEnumerable StakeAndClaimStakeRewardTestCases() - { - // 일반적인 보상수령 확인 - // 1단계 수준(50~499NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 10 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50L, - new[] - { - (400_000, 5), - (500_000, 1), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - }; - yield return new object[] - { - 499L, - new[] - { - (400_000, 49), - (500_000, 1), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - }; - - // 2단계 수준(500~4,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 8 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500L, - new[] - { - (400_000, 62), - (500_000, 2), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - }; - yield return new object[] - { - 799L, - new[] - { - (400_000, 99), - (500_000, 2), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - }; - yield return new object[] - { - 4_999L, - new[] - { - (400_000, 624), - (500_000, 8), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - }; - - // 3단계 수준(5,000~49,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 5_000L, - new[] - { - (400_000, 1000), - (500_000, 8), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - }; - yield return new object[] - { - 49_999L, - new[] - { - (400_000, 9_999), - (500_000, 64), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 8, - }; - - // 4단계 수준(50,000~499,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50_000L, - new[] - { - (400_000, 10_000), - (500_000, 64), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 8, - }; - yield return new object[] - { - 499_999L, - new[] - { - (400_000, 99_999), - (500_000, 626), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 83, - }; - - // 5단계 수준(500,000~100,000,000NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500_000L, - new[] - { - (400_000, 100_000), - (500_000, 627), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 83, - }; - yield return new object[] - { - 99_999_999L, - new[] - { - (400_000, 19_999_999), - (500_000, 125_001), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 16_666, - }; - - // 지연된 보상수령 확인 - // 1단계 수준(50~499NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 50 NCG, 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50L, - new[] - { - (400_000, 45), - (500_000, 9), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - }; - yield return new object[] - { - 499L, - new[] - { - (400_000, 441), - (500_000, 9), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - }; - - // 2단계 수준(500~4,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 8 NCG, 2 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500L, - new[] - { - (400_000, 558), - (500_000, 18), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - }; - yield return new object[] - { - 799L, - new[] - { - (400_000, 891), - (500_000, 18), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - }; - yield return new object[] - { - 4_999L, - new[] - { - (400_000, 5_616), - (500_000, 72), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - }; - - // 3단계 수준(5,000~49,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 5 NCG, 2 ap portion / 180 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 5_000L, - new[] - { - (400_000, 9_000), - (500_000, 72), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - }; - yield return new object[] - { - 49_999L, - new[] - { - (400_000, 89_991), - (500_000, 576), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 72, - }; - - // 4단계 수준(50,000~499,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 5 NCG, 2 ap portion / 180 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 50_000L, - new[] - { - (400_000, 90_000), - (500_000, 576), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 72, - }; - yield return new object[] - { - 499_999L, - new[] - { - (400_000, 899_991), - (500_000, 5_634), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 747, - }; - - // 5단계 수준(500,000~4,999,999NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 160 NCG 소수점 버림, 기존 deposit 유지 확인) - // 5단계 수준(500,000~500,000,000NCG)의 deposit save 완료 - // → 최초 보상 시점 (50,400블록 이후) 도달 - // → 보상을 수령하지 않음 - // → 2번째 보상 시점 (100,800블록 이후) 도달 - // → 이하 보상의 수령이 가능해야 한다. - // (보상내용: 2 hourglass / 5 NCG, 2 ap portion / 160 NCG 소수점 버림, 기존 deposit 유지 확인) - yield return new object[] - { - 500_000L, - new[] - { - (400_000, 900_000), - (500_000, 5_643), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 747, - }; - yield return new object[] - { - 4_999_999L, - new[] - { - (400_000, 8_999_991), - (500_000, 56_259), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 7_497, - }; - } - - public static IEnumerable StakeLessAfterLockupTestcases() - { - (long ClaimBlockIndex, (int ItemId, int Amount)[])[] BuildEvents( - int hourglassRate, - int apPotionRate, - int apPotionBonus, - long stakeAmount) - { - const int hourglassItemId = 400_000, apPotionItemId = 500_000; - return new[] - { - (ClaimStakeReward2.ObsoletedIndex + StakeState.RewardInterval, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - (ClaimStakeReward2.ObsoletedIndex + StakeState.RewardInterval * 2, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - (ClaimStakeReward2.ObsoletedIndex + StakeState.RewardInterval * 3, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - (ClaimStakeReward2.ObsoletedIndex + StakeState.RewardInterval * 4, new[] - { - (hourglassItemId, (int)(stakeAmount / hourglassRate)), - (apPotionItemId, (int)(stakeAmount / apPotionRate + apPotionBonus)), - }), - }; - } - - // 1단계 수준(50~499NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 50 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 51L, - 51L, - 50L, - BuildEvents(10, 800, 1, 50), - }; - yield return new object[] - { - 499L, - 499L, - 50L, - BuildEvents(10, 800, 1, 499), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 50L, - 50L, - 0L, - BuildEvents(10, 800, 1, 50), - }; - yield return new object[] - { - 499L, - 499L, - 0L, - BuildEvents(10, 800, 1, 499), - }; - - // 2단계 수준(500~4,999NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 8 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 500L, - 500L, - 499L, - BuildEvents(8, 800, 2, 500), - }; - yield return new object[] - { - 4_999L, - 4_999L, - 500L, - BuildEvents(8, 800, 2, 4_999), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 500L, - 500L, - 0L, - BuildEvents(8, 800, 2, 500), - }; - yield return new object[] - { - 4_999L, - 4_999L, - 0L, - BuildEvents(8, 800, 2, 4_999), - }; - - // 3단계 수준(5,000~49,999NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 5_000L, - 5_000L, - 4_999L, - BuildEvents(5, 800, 2, 5_000), - }; - yield return new object[] - { - 49_999L, - 49_999L, - 5_000L, - BuildEvents(5, 800, 2, 49_999), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 5_000L, - 5_000L, - 0L, - BuildEvents(5, 800, 2, 5_000), - }; - yield return new object[] - { - 49_999L, - 49_999L, - 0L, - BuildEvents(5, 800, 2, 49_999), - }; - - // 4단계 수준(50,000~499,999NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 50_000L, - 50_000L, - 49_999L, - BuildEvents(5, 800, 2, 50_000), - }; - yield return new object[] - { - 499_999L, - 499_999L, - 50_000L, - BuildEvents(5, 800, 2, 499_999), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 50_000L, - 50_000L, - 0L, - BuildEvents(5, 800, 2, 50_000), - }; - yield return new object[] - { - 499_999L, - 499_999L, - 0L, - BuildEvents(5, 800, 2, 499_999), - }; - - // 5단계 수준(500,000~100,000,000NCG)의 deposit save 완료 - // → 1~3회까지 모든 보상을 수령함 - // → 201,600 블록 도달 이후 - // → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - // (보상내용: 1 hourglass / 5 NCG, 1 ap portion / 800 NCG 소수점 버림, 기존 deposit 유지 확인) - // → 기존 deposit보다 낮은 금액으로 edit save 한다. - // → 보상 타이머 리셋 확인 - // → (기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인) - // → 현재 스테이킹된 NCG를 인출할 수 없다 (스테이킹 추가는 가능) - // → (현재 deposit 묶인 상태 확인) - yield return new object[] - { - 500_000L, - 500_000L, - 499_999L, - BuildEvents(5, 800, 2, 500_000), - }; - yield return new object[] - { - 500_000_000L, - 500_000_000L, - 500_000L, - BuildEvents(5, 800, 2, 500_000_000), - }; - - // 현재의 스테이킹된 NCG의 전액 인출을 시도한다(deposit NCG 인출 상태 확인) - // → 스테이킹 완전 소멸 확인 - yield return new object[] - { - 500_000L, - 500_000L, - 0L, - BuildEvents(5, 800, 2, 500_000), - }; - yield return new object[] - { - 500_000_000L, - 500_000_000L, - 0L, - BuildEvents(5, 800, 2, 500_000_000), - }; - } - - [Theory] - [MemberData(nameof(StakeAndClaimStakeRewardTestCases))] - public void StakeAndClaimStakeReward( - long stakeAmount, - (int ItemId, int Amount)[] expectedItems, - long receiveBlockIndex, - int expectedRune) - { - var context = new ActionContext(); - var states = _initialStatesWithAvatarStateV2.MintAsset(context, _agentAddr, _ncg * stakeAmount); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out StakeState stakeState)); - Assert.NotNull(stakeState); - Assert.Equal(0 * RuneHelper.StakeRune, _initialStatesWithAvatarStateV2.GetBalance(_avatarAddr, RuneHelper.StakeRune)); - - action = new ClaimStakeReward3(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = receiveBlockIndex, - }); - - // 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - var avatarState = states.GetAvatarStateV2(_avatarAddr); - foreach ((int itemId, int amount) in expectedItems) - { - Assert.True(avatarState.inventory.HasItem(itemId, amount)); - } - - // 기존 deposit 유지 확인 - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - Assert.Equal(expectedRune * RuneHelper.StakeRune, states.GetBalance(_avatarAddr, RuneHelper.StakeRune)); - } - - [Theory] - [InlineData(500L, 50L, 499L)] - [InlineData(500L, 499L, 500L)] - [InlineData(5_000L, 500L, 4_999L)] - [InlineData(5_000L, 4_999L, 5_000L)] - [InlineData(50_000L, 5_000L, 49_999L)] - [InlineData(50_000L, 49_999L, 50_000L)] - [InlineData(500_000L, 50_000L, 499_999L)] - [InlineData(500_000L, 499_999L, 500_000L)] - [InlineData(500_000_000L, 500_000L, 500_000_000L)] - public void StakeAndStakeMore(long initialBalance, long stakeAmount, long newStakeAmount) - { - long newStakeBlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval - 1; - // Validate testcases - Assert.True(stakeAmount < newStakeAmount); - - var context = new ActionContext(); - var states = _initialStatesWithAvatarStateV2.MintAsset(context, _agentAddr, _ncg * initialBalance); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out StakeState stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _ncg * (initialBalance - stakeAmount), - states.GetBalance(_agentAddr, _ncg)); - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - - action = new ClaimStakeReward3(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = newStakeBlockIndex, - }); - - action = new Stake(newStakeAmount); - // 스테이킹 추가는 가능 - // 락업기간 이전에 deposit을 추가해서 save 할 수 있는지 - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = newStakeBlockIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out stakeState)); - Assert.NotNull(stakeState); - // 쌓여있던 보상 타이머가 정상적으로 리셋되는지 - Assert.Equal(newStakeBlockIndex, stakeState.StartedBlockIndex); - // 락업기간이 새롭게 201,600으로 갱신되는지 확인 - Assert.Equal( - newStakeBlockIndex + StakeState.LockupInterval, - stakeState.CancellableBlockIndex); - Assert.Equal( - _ncg * (initialBalance - newStakeAmount), - states.GetBalance(_agentAddr, _ncg)); - // 기존보다 초과해서 설정한 deposit 으로 묶인 상태 갱신된 것 확인 - Assert.Equal( - _ncg * newStakeAmount, - states.GetBalance(stakeState.address, _ncg)); - } - - [Theory] - [InlineData(500L, 51L, 50L)] - [InlineData(500L, 499L, 50L)] - [InlineData(5_000L, 500L, 499L)] - [InlineData(5_000L, 500L, 50L)] - [InlineData(5_000L, 4_999L, 500L)] - [InlineData(50_000L, 5_000L, 4_999L)] - [InlineData(50_000L, 49_999L, 5_000L)] - [InlineData(500_000L, 50_000L, 49_999L)] - [InlineData(500_000L, 499_999L, 50_000L)] - [InlineData(500_000_000L, 500_000L, 99_999L)] - [InlineData(500_000_000L, 500_000_000L, 0L)] - [InlineData(500_000_000L, 500_000_000L, 99_999_999L)] - public void StakeAndStakeLess(long initialBalance, long stakeAmount, long newStakeAmount) - { - // Validate testcases - Assert.True(initialBalance >= stakeAmount); - Assert.True(newStakeAmount < stakeAmount); - - var context = new ActionContext(); - var states = _initialStatesWithAvatarStateV2.MintAsset(context, _agentAddr, _ncg * initialBalance); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out StakeState stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _ncg * (initialBalance - stakeAmount), - states.GetBalance(_agentAddr, _ncg)); - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - - action = new ClaimStakeReward3(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval - 1, - }); - - action = new Stake(newStakeAmount); - // 락업기간 이전에 deposit을 감소해서 save할때 락업되어 거부되는가 - Assert.Throws(() => states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval - 1, - })); - - Assert.True(states.TryGetStakeState(_agentAddr, out stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _ncg * (initialBalance - stakeAmount), - states.GetBalance(_agentAddr, _ncg)); - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - } - - [Theory] - [MemberData(nameof(StakeLessAfterLockupTestcases))] - // 락업기간 종료 이후 deposit을 현재보다 낮게 설정했을때, 설정이 잘되서 새롭게 락업되는지 확인 - // 락업기간 종료 이후 보상 수령하고 락업해제되는지 확인 - public void StakeLessAfterLockup( - long initialBalance, - long stakeAmount, - long newStakeAmount, - (long ClaimBlockIndex, (int ItemId, int Amount)[] ExpectedItems)[] claimEvents) - { - StakeState stakeState; - - // Validate testcases - Assert.True(stakeAmount > newStakeAmount); - - var context = new ActionContext(); - var states = _initialStatesWithAvatarStateV2.MintAsset(context, _agentAddr, _ncg * initialBalance); - - IAction action = new Stake(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - // 1~3회까지 모든 보상을 수령함 - // 201,600 블록 도달 이후 → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - foreach ((long claimBlockIndex, (int itemId, int amount)[] expectedItems) in claimEvents) - { - action = new ClaimStakeReward3(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = claimBlockIndex, - }); - - var avatarState = states.GetAvatarStateV2(_avatarAddr); - foreach ((int itemId, int amount) in expectedItems) - { - Assert.True(avatarState.inventory.HasItem(itemId, amount)); - } - - Assert.True(states.TryGetStakeState(_agentAddr, out stakeState)); - Assert.NotNull(stakeState); - // deposit 유지 확인 - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - } - - action = new Stake(newStakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, - }); - - // Setup staking again. - if (newStakeAmount > 0) - { - Assert.True(states.TryGetStakeState(_agentAddr, out stakeState)); - Assert.NotNull(stakeState); - // 쌓여있던 보상 타이머가 정상적으로 리셋되는지 - Assert.Equal(ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval, stakeState.StartedBlockIndex); - // 락업기간이 새롭게 201,600으로 갱신되는지 확인 - Assert.Equal( - ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval + StakeState.LockupInterval, - stakeState.CancellableBlockIndex); - // 기존 deposit - 현재 deposit 만큼의 ncg 인출 상태 확인 - Assert.Equal( - _ncg * (initialBalance - newStakeAmount), - states.GetBalance(_agentAddr, _ncg)); - Assert.Equal( - _ncg * newStakeAmount, - states.GetBalance(stakeState.address, _ncg)); - - Assert.Throws(() => - { - // 현재 스테이킹된 NCG를 인출할 수 없다 - action = new ClaimStakeReward3(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval + 1, - }); - }); - // 현재 deposit 묶인 상태 확인 - Assert.Equal( - _ncg * newStakeAmount, - states.GetBalance(stakeState.address, _ncg)); - } - else - { - Assert.Equal( - _ncg * initialBalance, - states.GetBalance(_agentAddr, _ncg)); - Assert.False(states.TryGetStakeState(_agentAddr, out stakeState)); - Assert.Null(stakeState); - } - } - - [Fact] - public void StakeAndClaimStakeRewardBeforeRewardInterval() - { - var context = new ActionContext(); - var states = _initialStatesWithAvatarStateV2.MintAsset(context, _agentAddr, _ncg * 500); - IAction action = new Stake(500); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - action = new ClaimStakeReward3(_avatarAddr); - Assert.Throws(() => states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.RewardInterval - 1, - })); - - var avatarState = states.GetAvatarStateV2(_avatarAddr); - Assert.Empty(avatarState.inventory.Items.Where(x => x.item.Id == 400000)); - Assert.Empty(avatarState.inventory.Items.Where(x => x.item.Id == 500000)); - } - } -} diff --git a/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs index 31d7c0a996..5cad29227f 100644 --- a/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs @@ -90,16 +90,14 @@ public void UnlockWorldByHackAndSlashAfterPatchTableWithAddRow( avatarState.inventory.AddItem(doomfist); var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - var hackAndSlash = new HackAndSlash3 + var hackAndSlash = new HackAndSlash { - worldId = worldIdToClear, - stageId = stageIdToClear, - avatarAddress = _avatarAddress, - costumes = new List(), - equipments = new List { doomfist.NonFungibleId }, - foods = new List(), - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, + WorldId = worldIdToClear, + StageId = stageIdToClear, + AvatarAddress = _avatarAddress, + Costumes = new List(), + Equipments = new List { doomfist.NonFungibleId }, + Foods = new List(), }; nextState = hackAndSlash.Execute(new ActionContext { @@ -108,7 +106,6 @@ public void UnlockWorldByHackAndSlashAfterPatchTableWithAddRow( Random = new TestRandom(), Rehearsal = false, }); - Assert.True(hackAndSlash.Result.IsClear); avatarState = nextState.GetAvatarState(_avatarAddress); Assert.True(avatarState.worldInformation.IsStageCleared(stageIdToClear)); @@ -151,121 +148,9 @@ public void UnlockWorldByHackAndSlashAfterPatchTableWithAddRow( Random = new TestRandom(), Rehearsal = false, }); - Assert.True(hackAndSlash.Result.IsClear); avatarState = nextState.GetAvatarState(_avatarAddress); Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldIdToUnlock)); } - - [Theory] - [InlineData(400, 10000001, "4,2,100,10001", "4,1,1,10001")] - [InlineData(400, 10000001, "4,2,100,10001", "4,2,99,10001")] - public void UnlockWorldByMimisbrunnrBattleAfterPatchTableWithChangeRow( - int avatarLevel, - int stageIdToUnlock, - string targetRowStringBefore, - string targetRowStringAfter) - { - var elements = targetRowStringAfter.Split(","); - Assert.True(int.TryParse(elements[1], out var worldIdToClear)); - Assert.True(int.TryParse(elements[2], out var stageIdToClear)); - Assert.True(int.TryParse(elements[3], out var worldIdToUnlock)); - - Assert.True(_tableSheets.CharacterLevelSheet.ContainsKey(avatarLevel)); - Assert.True(_tableSheets.WorldSheet.ContainsKey(worldIdToClear)); - Assert.True(_tableSheets.StageSheet.ContainsKey(stageIdToClear)); - Assert.True(_tableSheets.WorldSheet.ContainsKey(worldIdToUnlock)); - Assert.False(_tableSheets.WorldUnlockSheet.TryGetUnlockedInformation( - worldIdToClear, - stageIdToClear, - out _)); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.level = avatarLevel; - avatarState.worldInformation = new WorldInformation(0, _tableSheets.WorldSheet, stageIdToClear); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldIdToClear)); - Assert.False(avatarState.worldInformation.IsWorldUnlocked(worldIdToUnlock)); - - var equipments = new List(); - - var equipmentRow = - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10151001); - var equipment = ItemFactory.CreateItemUsable(equipmentRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(equipment); - - var mailEquipmentRow = _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10251001); - var mailEquipment = ItemFactory.CreateItemUsable(mailEquipmentRow, Guid.NewGuid(), 0); - avatarState.inventory.AddItem(mailEquipment); - - var beltEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10351000), Guid.NewGuid(), 0); - avatarState.inventory.AddItem(beltEquipment); - - var necklaceEquipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.Values.Last(x => x.Id == 10451000), Guid.NewGuid(), 0); - avatarState.inventory.AddItem(necklaceEquipment); - equipments.Add(equipment.ItemId); - equipments.Add(mailEquipment.ItemId); - equipments.Add(beltEquipment.ItemId); - equipments.Add(necklaceEquipment.ItemId); - - var nextState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - var mimisbrunnrBattle = new MimisbrunnrBattle3() - { - worldId = worldIdToUnlock, - stageId = stageIdToUnlock, - avatarAddress = _avatarAddress, - costumes = new List(), - equipments = equipments, - foods = new List(), - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, - }; - Assert.Throws(() => - { - nextState = mimisbrunnrBattle.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - }); - - avatarState = nextState.GetAvatarState(_avatarAddress); - Assert.False(avatarState.worldInformation.IsWorldUnlocked(worldIdToUnlock)); - - var tableCsv = nextState.GetSheetCsv(); - var newTable = new StringBuilder(tableCsv).Replace(targetRowStringBefore, targetRowStringAfter).ToString(); - var patchTableSheet = new PatchTableSheet - { - TableName = nameof(WorldUnlockSheet), - TableCsv = newTable, - }; - - nextState = patchTableSheet.Execute(new ActionContext - { - PreviousState = nextState, - Signer = AdminState.Address, - Random = new TestRandom(), - Rehearsal = false, - }); - - var nextTableCsv = nextState.GetSheetCsv(); - Assert.Equal(nextTableCsv, newTable); - - nextState = mimisbrunnrBattle.Execute(new ActionContext - { - PreviousState = nextState, - Signer = _agentAddress, - Random = new TestRandom(), - Rehearsal = false, - }); - - avatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(mimisbrunnrBattle.Result.IsClear); - Assert.True(avatarState.worldInformation.IsStageCleared(stageIdToClear)); - Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldIdToUnlock)); - } } } diff --git a/.Lib9c.Tests/Action/Sell0Test.cs b/.Lib9c.Tests/Action/Sell0Test.cs deleted file mode 100644 index 0ddab86943..0000000000 --- a/.Lib9c.Tests/Action/Sell0Test.cs +++ /dev/null @@ -1,236 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Sell0Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell0Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - Assert.Empty(shopState.Products); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - Assert.Single(avatarState.inventory.Equipments); - - var equipment = avatarState.inventory.Equipments.FirstOrDefault(); - Assert.NotNull(equipment); - - var currencyState = _initialState.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, 100, 0); - var sellAction = new Sell0 - { - itemId = equipment.ItemId, - price = price, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Empty(nextAvatarState.inventory.Equipments); - - var nextShopState = nextState.GetShopState(); - Assert.Single(nextShopState.Products); - - var (_, shopItem) = nextShopState.Products.FirstOrDefault(); - Assert.NotNull(shopItem); - Assert.Equal(equipment.ItemId, shopItem.ItemUsable.ItemId); - Assert.Equal(price, shopItem.Price); - Assert.Equal(_agentAddress, shopItem.SellerAgentAddress); - Assert.Equal(_avatarAddress, shopItem.SellerAvatarAddress); - } - - [Fact] - public void ExecuteThrowInvalidPriceException() - { - var action = new Sell0 - { - itemId = default, - price = -1 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new Sell0 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell0 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new Sell0 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowRequiredBlockIndexException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell0 - { - itemId = equipmentId, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell10Test.cs b/.Lib9c.Tests/Action/Sell10Test.cs deleted file mode 100644 index 286a4fbd0b..0000000000 --- a/.Lib9c.Tests/Action/Sell10Test.cs +++ /dev/null @@ -1,458 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Sell10Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, 1, true)] - [InlineData(ItemType.Costume, 1, false)] - [InlineData(ItemType.Equipment, 1, true)] - [InlineData(ItemType.Material, 1, false)] - public void Execute( - ItemType itemType, - int itemCount, - bool backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - if (backward) - { - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousStates = previousStates - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var orderId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var orderAddress = Order.DeriveAddress(orderId); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - tradableItem.ItemSubType, - orderId); - long blockIndex = 1; - Assert.Null(previousStates.GetState(shardedShopAddress)); - - var sellAction = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - long expiredBlockIndex = Order.ExpirationInterval + blockIndex; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out var inventoryItem)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, blockIndex, itemCount, out _)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, expiredBlockIndex, itemCount, out _)); - ITradableItem nextTradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState); - Assert.Single(nextShardedShopState.OrderDigestList); - var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId)); - Assert.Equal(price, orderDigest.Price); - Assert.Equal(blockIndex, orderDigest.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex); - Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId); - Assert.Equal(tradableItem.TradableId, orderDigest.TradableId); - - var serializedOrder = nextState.GetState(orderAddress); - Assert.NotNull(serializedOrder); - var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId)); - Assert.NotNull(serializedItem); - - var order = OrderFactory.Deserialize((Dictionary)serializedOrder); - ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem); - - Assert.Equal(price, order.Price); - Assert.Equal(orderId, order.OrderId); - Assert.Equal(tradableItem.TradableId, order.TradableId); - Assert.Equal(blockIndex, order.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex); - Assert.Equal(_agentAddress, order.SellerAgentAddress); - Assert.Equal(_avatarAddress, order.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex); - - var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress)); - Assert.NotNull(receiptDict); - var orderDigestList = new OrderDigestListState((Dictionary)receiptDict); - Assert.Single(orderDigestList.OrderDigestList); - OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First(); - Assert.Equal(orderDigest, orderDigest2); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = -1 * _currency, - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_ItemDoesNotExistException(bool isLock) - { - var tradableId = Guid.NewGuid(); - if (isLock) - { - var tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - tradableId, - 0); - var orderLock = new OrderLock(Guid.NewGuid()); - _avatarState.inventory.AddItem(tradableItem, 1, orderLock); - Assert.True(_avatarState.inventory.TryGetLockedItem(orderLock, out _)); - _initialState = _initialState.SetState( - _avatarAddress.Derive(LegacyInventoryKey), - _avatarState.inventory.Serialize() - ); - } - - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_DuplicateOrderIdException() - { - ITradableItem tradableItem = (ITradableItem)ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Weapon), - new TestRandom()); - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.inventory.AddItem((ItemBase)tradableItem); - - Guid tradableId = tradableItem.TradableId; - Guid orderId = Guid.NewGuid(); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - ItemSubType.Weapon, - orderId); - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - _currency * 0, - tradableId, - 0, - ItemSubType.Weapon, - 1 - ); - shardedShopState.Add(order.Digest2(avatarState, _tableSheets.CostumeStatSheet), 1); - Assert.Single(shardedShopState.OrderDigestList); - - IAccountStateDelta previousStates = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(shardedShopAddress, shardedShopState.Serialize()); - - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - Guid tradableId = Guid.NewGuid(); - Guid orderId = Guid.NewGuid(); - var action = new Sell10 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = _currency * ProductPrice, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell11Test.cs b/.Lib9c.Tests/Action/Sell11Test.cs deleted file mode 100644 index 6c307e5fd3..0000000000 --- a/.Lib9c.Tests/Action/Sell11Test.cs +++ /dev/null @@ -1,506 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Sell11Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, 1, true)] - [InlineData(ItemType.Costume, 1, false)] - [InlineData(ItemType.Equipment, 1, true)] - [InlineData(ItemType.Material, 1, false)] - public void Execute( - ItemType itemType, - int itemCount, - bool backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - if (backward) - { - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousStates = previousStates - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var orderId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var orderAddress = Order.DeriveAddress(orderId); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - tradableItem.ItemSubType, - orderId); - long blockIndex = 1; - Assert.Null(previousStates.GetState(shardedShopAddress)); - - var sellAction = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - long expiredBlockIndex = Order.ExpirationInterval + blockIndex; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out var inventoryItem)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, blockIndex, itemCount, out _)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, expiredBlockIndex, itemCount, out _)); - ITradableItem nextTradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState); - Assert.Single(nextShardedShopState.OrderDigestList); - var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId)); - Assert.Equal(price, orderDigest.Price); - Assert.Equal(blockIndex, orderDigest.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex); - Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId); - Assert.Equal(tradableItem.TradableId, orderDigest.TradableId); - - var serializedOrder = nextState.GetState(orderAddress); - Assert.NotNull(serializedOrder); - var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId)); - Assert.NotNull(serializedItem); - - var order = OrderFactory.Deserialize((Dictionary)serializedOrder); - ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem); - - Assert.Equal(price, order.Price); - Assert.Equal(orderId, order.OrderId); - Assert.Equal(tradableItem.TradableId, order.TradableId); - Assert.Equal(blockIndex, order.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex); - Assert.Equal(_agentAddress, order.SellerAgentAddress); - Assert.Equal(_avatarAddress, order.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex); - - var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress)); - Assert.NotNull(receiptDict); - var orderDigestList = new OrderDigestListState((Dictionary)receiptDict); - Assert.Single(orderDigestList.OrderDigestList); - OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First(); - Assert.Equal(orderDigest, orderDigest2); - } - - [Fact] - public void Execute_Throw_InvalidPriceException_DueTo_InvalidCurrencyPrice() - { - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = new FungibleAssetValue( -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - Currency.Legacy("KRW", 0, null), -#pragma warning restore CS0618 - 1, - 0), - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException_DueTo_NonZeroMinorUnitPrice() - { - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = new FungibleAssetValue(_currency, 1, 1), - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException_DueTo_NegativePrice() - { - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = new FungibleAssetValue(_currency, -1, 0), - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidOperationException_DueTo_EmptyState() - { - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_ItemDoesNotExistException(bool isLock) - { - var tradableId = Guid.NewGuid(); - if (isLock) - { - var tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - tradableId, - 0); - var orderLock = new OrderLock(Guid.NewGuid()); - _avatarState.inventory.AddItem(tradableItem, 1, orderLock); - Assert.True(_avatarState.inventory.TryGetLockedItem(orderLock, out _)); - _initialState = _initialState.SetState( - _avatarAddress.Derive(LegacyInventoryKey), - _avatarState.inventory.Serialize() - ); - } - - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_DuplicateOrderIdException() - { - ITradableItem tradableItem = (ITradableItem)ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Weapon), - new TestRandom()); - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.inventory.AddItem((ItemBase)tradableItem); - - Guid tradableId = tradableItem.TradableId; - Guid orderId = Guid.NewGuid(); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - ItemSubType.Weapon, - orderId); - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - _currency * 0, - tradableId, - 0, - ItemSubType.Weapon, - 1 - ); - shardedShopState.Add(order.Digest2(avatarState, _tableSheets.CostumeStatSheet), 1); - Assert.Single(shardedShopState.OrderDigestList); - - IAccountStateDelta previousStates = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(shardedShopAddress, shardedShopState.Serialize()); - - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - Guid tradableId = Guid.NewGuid(); - Guid orderId = Guid.NewGuid(); - var action = new Sell11 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = _currency * ProductPrice, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell2Test.cs b/.Lib9c.Tests/Action/Sell2Test.cs deleted file mode 100644 index 777e5b8d8f..0000000000 --- a/.Lib9c.Tests/Action/Sell2Test.cs +++ /dev/null @@ -1,275 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Sell2Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - _avatarState.inventory.AddItem2(equipment); - - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - _avatarState.inventory.AddItem2(costume); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - Assert.Empty(shopState.Products); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - Assert.Single(avatarState.inventory.Equipments); - - var equipment = avatarState.inventory.Equipments.FirstOrDefault(); - Assert.NotNull(equipment); - - var costume = avatarState.inventory.Costumes.FirstOrDefault(); - Assert.NotNull(costume); - - var items = new INonFungibleItem[] { equipment, costume }; - - var previousStates = _initialState; - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, 100, 0); - - var productCount = 0; - var random = new TestRandom(); - foreach (var nonFungibleItem in items) - { - var sellAction = new Sell2 - { - itemId = nonFungibleItem.NonFungibleId, - price = price, - sellerAvatarAddress = _avatarAddress, - }; - - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = random, - }); - - productCount++; - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Empty(nextAvatarState.inventory.Equipments); - - var nextShopState = nextState.GetShopState(); - - Assert.Equal(productCount, nextShopState.Products.Count); - - var products = nextShopState.Products.Values; - Assert.NotNull(products); - - var shopItem = nonFungibleItem is Costume ? - products.First(x => x.Costume != null) : - products.First(x => x.ItemUsable != null); - - if (shopItem.ItemUsable != null) - { - Assert.Equal(nonFungibleItem.NonFungibleId, shopItem.ItemUsable.ItemId); - } - - if (shopItem.Costume != null) - { - Assert.Equal(nonFungibleItem.NonFungibleId, shopItem.Costume.ItemId); - } - - Assert.Equal(price, shopItem.Price); - Assert.Equal(_agentAddress, shopItem.SellerAgentAddress); - Assert.Equal(_avatarAddress, shopItem.SellerAvatarAddress); - - previousStates = nextState; - } - } - - [Fact] - public void ExecuteThrowInvalidPriceException() - { - var action = new Sell2 - { - itemId = default, - price = -1 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new Sell2 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell2 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new Sell2 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void ExecuteThrowRequiredBlockIndexException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell2 - { - itemId = equipmentId, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell3Test.cs b/.Lib9c.Tests/Action/Sell3Test.cs deleted file mode 100644 index 783c28936e..0000000000 --- a/.Lib9c.Tests/Action/Sell3Test.cs +++ /dev/null @@ -1,276 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Sell3Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - _avatarState.inventory.AddItem2(equipment); - - var consumable = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - _avatarState.inventory.AddItem2(consumable); - - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - _avatarState.inventory.AddItem2(costume); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - Assert.Empty(shopState.Products); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - Assert.Single(avatarState.inventory.Equipments); - - var equipment = avatarState.inventory.Equipments.FirstOrDefault(); - Assert.NotNull(equipment); - - var consumable = avatarState.inventory.Consumables.FirstOrDefault(); - Assert.NotNull(equipment); - - var costume = avatarState.inventory.Costumes.FirstOrDefault(); - Assert.NotNull(costume); - - var items = new INonFungibleItem[] { equipment, consumable, costume }; - - var previousStates = _initialState; - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - - var productCount = 0; - var random = new TestRandom(); - foreach (var nonFungibleItem in items) - { - var sellAction = new Sell3 - { - itemId = nonFungibleItem.NonFungibleId, - price = price, - sellerAvatarAddress = _avatarAddress, - }; - - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = random, - }); - - productCount++; - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Empty(nextAvatarState.inventory.Equipments); - - var nextShopState = nextState.GetShopState(); - - Assert.Equal(productCount, nextShopState.Products.Count); - - var products = nextShopState.Products.Values; - Assert.NotNull(products); - - var shopItem = nonFungibleItem is Costume ? - products.First(x => x.Costume != null) : - products.First(x => x.ItemUsable != null); - - Assert.Equal(price, shopItem.Price); - Assert.Equal(_agentAddress, shopItem.SellerAgentAddress); - Assert.Equal(_avatarAddress, shopItem.SellerAvatarAddress); - - previousStates = nextState; - } - } - - [Fact] - public void ExecuteThrowInvalidPriceException() - { - var action = new Sell3 - { - itemId = default, - price = -1 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowFailedLoadStateException() - { - var action = new Sell3 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowNotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell3 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void ExecuteThrowItemDoesNotExistException() - { - var action = new Sell3 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void ExecuteThrowRequiredBlockIndexException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell3 - { - itemId = equipmentId, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell4Test.cs b/.Lib9c.Tests/Action/Sell4Test.cs deleted file mode 100644 index edaa1f8c6f..0000000000 --- a/.Lib9c.Tests/Action/Sell4Test.cs +++ /dev/null @@ -1,339 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Sell4Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - _avatarState.inventory.AddItem2(equipment); - - var consumable = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - _avatarState.inventory.AddItem2(consumable); - - var costume = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - _avatarState.inventory.AddItem2(costume); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, true, 2)] - [InlineData(ItemType.Costume, true, 2)] - [InlineData(ItemType.Equipment, true, 2)] - [InlineData(ItemType.Consumable, false, 0)] - [InlineData(ItemType.Costume, false, 0)] - [InlineData(ItemType.Equipment, false, 0)] - public void Execute(ItemType itemType, bool shopItemExist, int blockIndex) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - List inventoryItem = avatarState.inventory.Items.Where(i => i.item.ItemType == itemType).ToList(); - Assert.Single(inventoryItem); - var previousStates = _initialState; - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - INonFungibleItem nonFungibleItem = (INonFungibleItem)inventoryItem.First().item; - nonFungibleItem.RequiredBlockIndex = blockIndex; - Assert.Equal(blockIndex, nonFungibleItem.RequiredBlockIndex); - ItemSubType itemSubType = ItemSubType.Food; - Guid productId = new Guid("6f460c1a-755d-48e4-ad67-65d5f519dbc8"); - if (nonFungibleItem is ItemUsable itemUsable) - { - itemSubType = itemUsable.ItemSubType; - } - else if (nonFungibleItem is Costume costume) - { - itemSubType = costume.ItemSubType; - } - - Address shopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - - if (shopItemExist) - { - var si = new ShopItem( - _agentAddress, - _avatarAddress, - productId, - new FungibleAssetValue(currencyState, 100, 0), - blockIndex, - nonFungibleItem); - ShardedShopState shardedShopState = - new ShardedShopState(shopAddress); - shardedShopState.Register(si); - Assert.Single(shardedShopState.Products); - previousStates = previousStates.SetState(shopAddress, shardedShopState.Serialize()); - } - else - { - Assert.Null(previousStates.GetState(shopAddress)); - } - - var sellAction = new Sell4 - { - itemId = nonFungibleItem.NonFungibleId, - price = price, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - }; - - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - const long expiredBlockIndex = Sell6.ExpiredBlockIndex + 1; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem(nonFungibleItem.NonFungibleId, out var nextItem)); - INonFungibleItem nextNonFungibleItem = (INonFungibleItem)nextItem.item; - Assert.Equal(expiredBlockIndex, nextNonFungibleItem.RequiredBlockIndex); - - var nextShopState = new ShardedShopState((Dictionary)nextState.GetState(shopAddress)); - - Assert.Single(nextShopState.Products); - - var products = nextShopState.Products.Values; - - var shopItem = products.First(); - INonFungibleItem item = itemType == ItemType.Costume ? (INonFungibleItem)shopItem.Costume : shopItem.ItemUsable; - - Assert.Equal(price, shopItem.Price); - Assert.Equal(expiredBlockIndex, shopItem.ExpiredBlockIndex); - Assert.Equal(expiredBlockIndex, item.RequiredBlockIndex); - Assert.Equal(_agentAddress, shopItem.SellerAgentAddress); - Assert.Equal(_avatarAddress, shopItem.SellerAvatarAddress); - - var mailList = nextAvatarState.mailBox.Where(m => m is SellCancelMail).ToList(); - Assert.Single(mailList); - - Assert.Equal(expiredBlockIndex, mailList.First().requiredBlockIndex); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell4 - { - itemId = default, - price = -1 * _currency, - sellerAvatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new Sell4 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell4 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_ItemDoesNotExistException() - { - var action = new Sell4 - { - itemId = default, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell4 - { - itemId = equipmentId, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_RequiredBlockIndexException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell4 - { - itemId = equipmentId, - price = 0 * _currency, - sellerAvatarAddress = _avatarAddress, - itemSubType = equipment.ItemSubType, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell5Test.cs b/.Lib9c.Tests/Action/Sell5Test.cs deleted file mode 100644 index 26bf1ea944..0000000000 --- a/.Lib9c.Tests/Action/Sell5Test.cs +++ /dev/null @@ -1,389 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Sell5Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, true, 2, 1, 1, 1)] - [InlineData(ItemType.Costume, true, 2, 1, 1, 1)] - [InlineData(ItemType.Equipment, true, 2, 1, 1, 1)] - [InlineData(ItemType.Consumable, false, 0, 1, 1, 1)] - [InlineData(ItemType.Costume, false, 0, 1, 1, 1)] - [InlineData(ItemType.Equipment, false, 0, 1, 1, 1)] - [InlineData(ItemType.Material, true, 1, 2, 1, 1)] - [InlineData(ItemType.Material, true, 1, 1, 2, 1)] - [InlineData(ItemType.Material, true, 2, 1, 2, 2)] - [InlineData(ItemType.Material, true, 3, 2, 2, 2)] - [InlineData(ItemType.Material, false, 1, 1, 1, 1)] - public void Execute( - ItemType itemType, - bool shopItemExist, - long blockIndex, - int itemCount, - int prevCount, - int expectedProductsCount - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var productId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var shardedShopAddress = ShardedShopState.DeriveAddress( - tradableItem.ItemSubType, - productId); - if (shopItemExist) - { - tradableItem.RequiredBlockIndex = blockIndex; - Assert.Equal(blockIndex, tradableItem.RequiredBlockIndex); - var shopItem = new ShopItem( - _agentAddress, - _avatarAddress, - expectedProductsCount == 2 ? Guid.NewGuid() : productId, - price, - blockIndex, - tradableItem, - prevCount - ); - - var shardedShopState = new ShardedShopState(shardedShopAddress); - shardedShopState.Register(shopItem); - Assert.Single(shardedShopState.Products); - previousStates = previousStates.SetState( - shardedShopAddress, - shardedShopState.Serialize()); - } - else - { - Assert.Null(previousStates.GetState(shardedShopAddress)); - } - - var sellAction = new Sell5 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - const long expiredBlockIndex = Sell6.ExpiredBlockIndex + 1; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetTradableItems( - tradableItem.TradableId, - expiredBlockIndex, - 1, - out var inventoryItems)); - Assert.Single(inventoryItems); - ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState and ShopItem - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopState((Dictionary)nextSerializedShardedShopState); - Assert.Equal(expectedProductsCount, nextShardedShopState.Products.Count); - - var nextShopItem = nextShardedShopState.Products.Values.First(s => s.ExpiredBlockIndex == expiredBlockIndex); - ITradableItem nextTradableItemInShopItem; - switch (itemType) - { - case ItemType.Consumable: - case ItemType.Equipment: - nextTradableItemInShopItem = nextShopItem.ItemUsable; - break; - case ItemType.Costume: - nextTradableItemInShopItem = nextShopItem.Costume; - break; - case ItemType.Material: - nextTradableItemInShopItem = nextShopItem.TradableFungibleItem; - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(price, nextShopItem.Price); - Assert.Equal(expiredBlockIndex, nextShopItem.ExpiredBlockIndex); - Assert.Equal(_agentAddress, nextShopItem.SellerAgentAddress); - Assert.Equal(_avatarAddress, nextShopItem.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, nextTradableItemInShopItem.RequiredBlockIndex); - - var mailList = nextAvatarState.mailBox.Where(m => m is SellCancelMail).ToList(); - Assert.Single(mailList); - var mail = mailList.First() as SellCancelMail; - Assert.NotNull(mail); - Assert.Equal(expiredBlockIndex, mail.requiredBlockIndex); - - ITradableItem attachmentItem; - int attachmentCount = 0; - switch (itemType) - { - case ItemType.Consumable: - case ItemType.Equipment: - Assert.NotNull(mail.attachment.itemUsable); - attachmentItem = mail.attachment.itemUsable; - Assert.Equal(tradableItem, mail.attachment.itemUsable); - break; - case ItemType.Costume: - Assert.NotNull(mail.attachment.costume); - attachmentItem = mail.attachment.costume; - Assert.Equal(tradableItem, mail.attachment.costume); - break; - case ItemType.Material: - Assert.NotNull(mail.attachment.tradableFungibleItem); - attachmentItem = mail.attachment.tradableFungibleItem; - attachmentCount = mail.attachment.tradableFungibleItemCount; - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(attachmentCount, nextShopItem.TradableFungibleItemCount); - Assert.Equal(nextTradableItem, attachmentItem); - Assert.Equal(nextTradableItemInShopItem, attachmentItem); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell5 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = -1 * _currency, - itemSubType = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new Sell5 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell5 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_ItemDoesNotExistException() - { - var action = new Sell5 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell5 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell7Test.cs b/.Lib9c.Tests/Action/Sell7Test.cs deleted file mode 100644 index c8e670e9c2..0000000000 --- a/.Lib9c.Tests/Action/Sell7Test.cs +++ /dev/null @@ -1,481 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Sell7Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell7Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, true, 1, true)] - [InlineData(ItemType.Equipment, true, 1, false)] - [InlineData(ItemType.Consumable, false, 1, true)] - [InlineData(ItemType.Costume, false, 1, false)] - [InlineData(ItemType.Equipment, false, 1, true)] - [InlineData(ItemType.Material, true, 2, false)] - [InlineData(ItemType.Material, true, 1, true)] - [InlineData(ItemType.Material, false, 1, false)] - public void Execute( - ItemType itemType, - bool orderExist, - int itemCount, - bool backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - if (backward) - { - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousStates = previousStates - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var orderId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var existOrderId = new Guid("229e5f8c-fabe-4c04-bab9-45325cfa69a4"); - var orderAddress = Order.DeriveAddress(orderId); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - tradableItem.ItemSubType, - orderId); - long blockIndex = 1; - if (orderExist) - { - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var existOrder = OrderFactory.Create( - _agentAddress, - _avatarAddress, - existOrderId, - price, - tradableItem.TradableId, - 0, - tradableItem.ItemSubType, - 1 - ); - existOrder.Sell2(avatarState); - blockIndex = existOrder.ExpiredBlockIndex; - shardedShopState.Add(existOrder.Digest2(avatarState, _tableSheets.CostumeStatSheet), blockIndex); - Assert.Single(shardedShopState.OrderDigestList); - previousStates = previousStates.SetState( - shardedShopAddress, - shardedShopState.Serialize()); - } - else - { - Assert.Null(previousStates.GetState(shardedShopAddress)); - } - - var sellAction = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - long expiredBlockIndex = Order.ExpirationInterval + blockIndex; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetTradableItems( - tradableItem.TradableId, - expiredBlockIndex, - 1, - out var inventoryItems)); - Assert.Single(inventoryItems); - ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState); - var expectedCount = orderExist ? 2 : 1; - Assert.Equal(expectedCount, nextShardedShopState.OrderDigestList.Count); - var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId)); - Assert.Equal(price, orderDigest.Price); - Assert.Equal(blockIndex, orderDigest.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex); - Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId); - Assert.Equal(tradableItem.TradableId, orderDigest.TradableId); - - var serializedOrder = nextState.GetState(orderAddress); - Assert.NotNull(serializedOrder); - var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId)); - Assert.NotNull(serializedItem); - - var order = OrderFactory.Deserialize((Dictionary)serializedOrder); - ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem); - - Assert.Equal(price, order.Price); - Assert.Equal(orderId, order.OrderId); - Assert.Equal(tradableItem.TradableId, order.TradableId); - Assert.Equal(blockIndex, order.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex); - Assert.Equal(_agentAddress, order.SellerAgentAddress); - Assert.Equal(_avatarAddress, order.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex); - - var mailList = nextAvatarState.mailBox.OfType().ToList(); - Assert.Single(mailList); - var mail = mailList.First(); - Assert.NotNull(mail); - Assert.Equal(expiredBlockIndex, mail.requiredBlockIndex); - Assert.Equal(orderId, mail.OrderId); - - var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress)); - Assert.NotNull(receiptDict); - var orderDigestList = new OrderDigestListState((Dictionary)receiptDict); - Assert.Single(orderDigestList.OrderDigestList); - OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First(); - Assert.Equal(orderDigest, orderDigest2); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = -1 * _currency, - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_ItemDoesNotExistException() - { - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_DuplicateOrderIdException() - { - ITradableItem tradableItem = (ITradableItem)ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Weapon), - new TestRandom()); - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.inventory.AddItem2((ItemBase)tradableItem); - - Guid tradableId = tradableItem.TradableId; - Guid orderId = Guid.NewGuid(); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - ItemSubType.Weapon, - orderId); - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - _currency * 0, - tradableId, - 0, - ItemSubType.Weapon, - 1 - ); - shardedShopState.Add(order.Digest2(avatarState, _tableSheets.CostumeStatSheet), 1); - Assert.Single(shardedShopState.OrderDigestList); - - IAccountStateDelta previousStates = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(shardedShopAddress, shardedShopState.Serialize()); - - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - Guid tradableId = Guid.NewGuid(); - Guid orderId = Guid.NewGuid(); - var action = new Sell7 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = _currency * ProductPrice, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell8Test.cs b/.Lib9c.Tests/Action/Sell8Test.cs deleted file mode 100644 index 3bbb6e772f..0000000000 --- a/.Lib9c.Tests/Action/Sell8Test.cs +++ /dev/null @@ -1,474 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Sell8Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, true, 1, true)] - [InlineData(ItemType.Equipment, true, 1, false)] - [InlineData(ItemType.Consumable, false, 1, true)] - [InlineData(ItemType.Costume, false, 1, false)] - [InlineData(ItemType.Equipment, false, 1, true)] - [InlineData(ItemType.Material, true, 2, false)] - [InlineData(ItemType.Material, true, 1, true)] - [InlineData(ItemType.Material, false, 1, false)] - public void Execute( - ItemType itemType, - bool orderExist, - int itemCount, - bool backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - if (backward) - { - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousStates = previousStates - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var orderId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var existOrderId = new Guid("229e5f8c-fabe-4c04-bab9-45325cfa69a4"); - var orderAddress = Order.DeriveAddress(orderId); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - tradableItem.ItemSubType, - orderId); - long blockIndex = 1; - if (orderExist) - { - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var existOrder = OrderFactory.Create( - _agentAddress, - _avatarAddress, - existOrderId, - price, - tradableItem.TradableId, - 0, - tradableItem.ItemSubType, - 1 - ); - existOrder.Sell2(avatarState); - blockIndex = existOrder.ExpiredBlockIndex; - shardedShopState.Add(existOrder.Digest2(avatarState, _tableSheets.CostumeStatSheet), blockIndex); - Assert.Single(shardedShopState.OrderDigestList); - previousStates = previousStates.SetState( - shardedShopAddress, - shardedShopState.Serialize()); - } - else - { - Assert.Null(previousStates.GetState(shardedShopAddress)); - } - - var sellAction = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - long expiredBlockIndex = Order.ExpirationInterval + blockIndex; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetTradableItems( - tradableItem.TradableId, - expiredBlockIndex, - 1, - out var inventoryItems)); - Assert.Single(inventoryItems); - ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState); - var expectedCount = orderExist ? 2 : 1; - Assert.Equal(expectedCount, nextShardedShopState.OrderDigestList.Count); - var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId)); - Assert.Equal(price, orderDigest.Price); - Assert.Equal(blockIndex, orderDigest.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex); - Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId); - Assert.Equal(tradableItem.TradableId, orderDigest.TradableId); - - var serializedOrder = nextState.GetState(orderAddress); - Assert.NotNull(serializedOrder); - var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId)); - Assert.NotNull(serializedItem); - - var order = OrderFactory.Deserialize((Dictionary)serializedOrder); - ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem); - - Assert.Equal(price, order.Price); - Assert.Equal(orderId, order.OrderId); - Assert.Equal(tradableItem.TradableId, order.TradableId); - Assert.Equal(blockIndex, order.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex); - Assert.Equal(_agentAddress, order.SellerAgentAddress); - Assert.Equal(_avatarAddress, order.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex); - - var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress)); - Assert.NotNull(receiptDict); - var orderDigestList = new OrderDigestListState((Dictionary)receiptDict); - Assert.Single(orderDigestList.OrderDigestList); - OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First(); - Assert.Equal(orderDigest, orderDigest2); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = -1 * _currency, - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_ItemDoesNotExistException() - { - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_DuplicateOrderIdException() - { - ITradableItem tradableItem = (ITradableItem)ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Weapon), - new TestRandom()); - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.inventory.AddItem2((ItemBase)tradableItem); - - Guid tradableId = tradableItem.TradableId; - Guid orderId = Guid.NewGuid(); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - ItemSubType.Weapon, - orderId); - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - _currency * 0, - tradableId, - 0, - ItemSubType.Weapon, - 1 - ); - shardedShopState.Add(order.Digest2(avatarState, _tableSheets.CostumeStatSheet), 1); - Assert.Single(shardedShopState.OrderDigestList); - - IAccountStateDelta previousStates = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(shardedShopAddress, shardedShopState.Serialize()); - - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - Guid tradableId = Guid.NewGuid(); - Guid orderId = Guid.NewGuid(); - var action = new Sell8 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = _currency * ProductPrice, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell9Test.cs b/.Lib9c.Tests/Action/Sell9Test.cs deleted file mode 100644 index f4a7122de8..0000000000 --- a/.Lib9c.Tests/Action/Sell9Test.cs +++ /dev/null @@ -1,458 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class Sell9Test - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public Sell9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, 1, true)] - [InlineData(ItemType.Costume, 1, false)] - [InlineData(ItemType.Equipment, 1, true)] - [InlineData(ItemType.Material, 1, false)] - public void Execute( - ItemType itemType, - int itemCount, - bool backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - if (backward) - { - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousStates = previousStates - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var orderId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var orderAddress = Order.DeriveAddress(orderId); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - tradableItem.ItemSubType, - orderId); - long blockIndex = 1; - Assert.Null(previousStates.GetState(shardedShopAddress)); - - var sellAction = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - long expiredBlockIndex = Order.ExpirationInterval + blockIndex; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out var inventoryItem)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, blockIndex, itemCount, out _)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, expiredBlockIndex, itemCount, out _)); - ITradableItem nextTradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState); - Assert.Single(nextShardedShopState.OrderDigestList); - var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId)); - Assert.Equal(price, orderDigest.Price); - Assert.Equal(blockIndex, orderDigest.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex); - Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId); - Assert.Equal(tradableItem.TradableId, orderDigest.TradableId); - - var serializedOrder = nextState.GetState(orderAddress); - Assert.NotNull(serializedOrder); - var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId)); - Assert.NotNull(serializedItem); - - var order = OrderFactory.Deserialize((Dictionary)serializedOrder); - ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem); - - Assert.Equal(price, order.Price); - Assert.Equal(orderId, order.OrderId); - Assert.Equal(tradableItem.TradableId, order.TradableId); - Assert.Equal(blockIndex, order.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex); - Assert.Equal(_agentAddress, order.SellerAgentAddress); - Assert.Equal(_avatarAddress, order.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex); - - var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress)); - Assert.NotNull(receiptDict); - var orderDigestList = new OrderDigestListState((Dictionary)receiptDict); - Assert.Single(orderDigestList.OrderDigestList); - OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First(); - Assert.Equal(orderDigest, orderDigest2); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = -1 * _currency, - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_ItemDoesNotExistException(bool isLock) - { - var tradableId = Guid.NewGuid(); - if (isLock) - { - var tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - tradableId, - 0); - var orderLock = new OrderLock(Guid.NewGuid()); - _avatarState.inventory.AddItem2(tradableItem, 1, orderLock); - Assert.True(_avatarState.inventory.TryGetLockedItem(orderLock, out _)); - _initialState = _initialState.SetState( - _avatarAddress.Derive(LegacyInventoryKey), - _avatarState.inventory.Serialize() - ); - } - - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem2(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_DuplicateOrderIdException() - { - ITradableItem tradableItem = (ITradableItem)ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Weapon), - new TestRandom()); - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.inventory.AddItem2((ItemBase)tradableItem); - - Guid tradableId = tradableItem.TradableId; - Guid orderId = Guid.NewGuid(); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - ItemSubType.Weapon, - orderId); - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - _currency * 0, - tradableId, - 0, - ItemSubType.Weapon, - 1 - ); - shardedShopState.Add(order.Digest2(avatarState, _tableSheets.CostumeStatSheet), 1); - Assert.Single(shardedShopState.OrderDigestList); - - IAccountStateDelta previousStates = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(shardedShopAddress, shardedShopState.Serialize()); - - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - Guid tradableId = Guid.NewGuid(); - Guid orderId = Guid.NewGuid(); - var action = new Sell9 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = _currency * ProductPrice, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation0Test.cs b/.Lib9c.Tests/Action/SellCancellation0Test.cs deleted file mode 100644 index ad5d0f6b23..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation0Test.cs +++ /dev/null @@ -1,121 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class SellCancellation0Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public SellCancellation0Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - equipment)); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - Assert.Single(shopState.Products); - - var (_, shopItem) = shopState.Products.FirstOrDefault(); - Assert.NotNull(shopItem); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - Assert.False(avatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, - out ItemUsable _)); - - var sellCancellationAction = new SellCancellation0 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var nextShopState = nextState.GetShopState(); - Assert.Empty(nextShopState.Products); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, - out ItemUsable _)); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation2Test.cs b/.Lib9c.Tests/Action/SellCancellation2Test.cs deleted file mode 100644 index b49430c8d3..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation2Test.cs +++ /dev/null @@ -1,139 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class SellCancellation2Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public SellCancellation2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - equipment)); - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - for (var i = 0; i < 100; i++) - { - var mail = new CombinationMail(result, i, default, 0); - avatarState.Update2(mail); - } - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - Assert.Single(shopState.Products); - - var (_, shopItem) = shopState.Products.FirstOrDefault(); - Assert.NotNull(shopItem); - - var avatarState = _initialState.GetAvatarState(_avatarAddress); - Assert.False(avatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, - out ItemUsable _)); - - var sellCancellationAction = new SellCancellation2 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var nextShopState = nextState.GetShopState(); - Assert.Empty(nextShopState.Products); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, - out ItemUsable _)); - Assert.Equal(30, nextAvatarState.mailBox.Count); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation3Test.cs b/.Lib9c.Tests/Action/SellCancellation3Test.cs deleted file mode 100644 index e9948fffac..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation3Test.cs +++ /dev/null @@ -1,182 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class SellCancellation3Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public SellCancellation3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - - var consumable = ItemFactory.CreateItemUsable( - tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - - var costume = ItemFactory.CreateCostume( - tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - equipment)); - - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - consumable)); - - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - costume)); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - var productsCount = shopState.Products.Count; - var shopItems = shopState.Products.Values.ToList(); - Assert.NotNull(shopItems); - var previousStates = _initialState; - var avatarState = previousStates.GetAvatarState(_avatarAddress); - - foreach (var shopItem in shopItems) - { - if (shopItem.ItemUsable != null) - { - Assert.False(avatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, out _)); - - var sellCancellationAction = new SellCancellation3 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - productsCount--; - - var nextShopState = nextState.GetShopState(); - Assert.Equal(productsCount, nextShopState.Products.Count); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, out _)); - - previousStates = nextState; - } - - if (shopItem.Costume != null) - { - Assert.False(avatarState.inventory.TryGetNonFungibleItem( - shopItem.Costume.ItemId, out var _)); - - var sellCancellationAction = new SellCancellation3 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - productsCount--; - - var nextShopState = nextState.GetShopState(); - Assert.Equal(productsCount, nextShopState.Products.Count); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem( - shopItem.Costume.ItemId, out _)); - - previousStates = nextState; - } - } - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation4Test.cs b/.Lib9c.Tests/Action/SellCancellation4Test.cs deleted file mode 100644 index 36701f9141..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation4Test.cs +++ /dev/null @@ -1,182 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Linq; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class SellCancellation4Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public SellCancellation4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(currency); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - var equipment = ItemFactory.CreateItemUsable( - tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - - var consumable = ItemFactory.CreateItemUsable( - tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - - var costume = ItemFactory.CreateCostume( - tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - - var shopState = new ShopState(); - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - equipment)); - - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - consumable)); - - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - costume)); - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Fact] - public void Execute() - { - var shopState = _initialState.GetShopState(); - var productsCount = shopState.Products.Count; - var shopItems = shopState.Products.Values.ToList(); - Assert.NotNull(shopItems); - var previousStates = _initialState; - var avatarState = previousStates.GetAvatarState(_avatarAddress); - - foreach (var shopItem in shopItems) - { - if (shopItem.ItemUsable != null) - { - Assert.False(avatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, out _)); - - var sellCancellationAction = new SellCancellation4 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - productsCount--; - - var nextShopState = nextState.GetShopState(); - Assert.Equal(productsCount, nextShopState.Products.Count); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem( - shopItem.ItemUsable.ItemId, out _)); - - previousStates = nextState; - } - - if (shopItem.Costume != null) - { - Assert.False(avatarState.inventory.TryGetNonFungibleItem( - shopItem.Costume.ItemId, out var _)); - - var sellCancellationAction = new SellCancellation4 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - productsCount--; - - var nextShopState = nextState.GetShopState(); - Assert.Equal(productsCount, nextShopState.Products.Count); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem( - shopItem.Costume.ItemId, out _)); - - previousStates = nextState; - } - } - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation5Test.cs b/.Lib9c.Tests/Action/SellCancellation5Test.cs deleted file mode 100644 index e3efc82ca2..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation5Test.cs +++ /dev/null @@ -1,345 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class SellCancellation5Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - - public SellCancellation5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", true)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", true)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", false)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", false)] - public void Execute(ItemType itemType, string guid, bool contain) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - INonFungibleItem nonFungibleItem; - Guid itemId = new Guid(guid); - Guid productId = itemId; - ItemSubType itemSubType; - const long requiredBlockIndex = 0; - ShopState legacyShopState = _initialState.GetShopState(); - if (itemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - nonFungibleItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - nonFungibleItem = costume; - itemSubType = costume.ItemSubType; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - avatarState.Update2(mail); - } - - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - var shopItem = new ShopItem( - _agentAddress, - _avatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - requiredBlockIndex, - nonFungibleItem); - - if (contain) - { - shopState.Register(shopItem); - avatarState.inventory.AddItem2((ItemBase)nonFungibleItem); - Assert.Empty(legacyShopState.Products); - Assert.Single(shopState.Products); - } - else - { - legacyShopState.Register(shopItem); - Assert.Single(legacyShopState.Products); - Assert.Empty(shopState.Products); - } - - Assert.Equal(requiredBlockIndex, nonFungibleItem.RequiredBlockIndex); - Assert.Equal(contain, avatarState.inventory.TryGetNonFungibleItem(itemId, out _)); - - IAccountStateDelta prevState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(Addresses.Shop, legacyShopState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var sellCancellationAction = new SellCancellation5 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = prevState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - ShardedShopState nextShopState = new ShardedShopState((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.Empty(nextShopState.Products); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.True(nextAvatarState.inventory.TryGetNonFungibleItem(itemId, out INonFungibleItem nextNonFungibleItem)); - Assert.Equal(1, nextNonFungibleItem.RequiredBlockIndex); - Assert.Equal(30, nextAvatarState.mailBox.Count); - ShopState nextLegacyShopState = nextState.GetShopState(); - Assert.Empty(nextLegacyShopState.Products); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new SellCancellation5 - { - productId = default, - sellerAvatarAddress = default, - itemSubType = ItemSubType.Weapon, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = default, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_initialState.GetAvatarState(_avatarAddress)) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - IAccountStateDelta prevState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new SellCancellation5 - { - productId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Weapon, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = prevState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_ItemDoesNotExistException() - { - Guid productId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemUsable.ItemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - - var shopItem = new ShopItem( - _agentAddress, - _avatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - itemUsable); - - IAccountStateDelta prevState = _initialState - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation5 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - Random = new TestRandom(), - Signer = _agentAddress, - }) - ); - } - - [Fact] - public void Execute_Throw_InvalidAddressException_From_Agent() - { - Guid productId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemUsable.ItemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - - var shopItem = new ShopItem( - default, - _avatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - itemUsable); - shopState.Register(shopItem); - - IAccountStateDelta prevState = _initialState - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation5 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - Random = new TestRandom(), - Signer = _agentAddress, - }) - ); - } - - [Fact] - public void Execute_Throw_InvalidAddressException_From_Avatar() - { - Guid productId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemUsable.ItemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - - var shopItem = new ShopItem( - _agentAddress, - default, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - itemUsable); - shopState.Register(shopItem); - - IAccountStateDelta prevState = _initialState - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation5 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - Random = new TestRandom(), - Signer = _agentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation6Test.cs b/.Lib9c.Tests/Action/SellCancellation6Test.cs deleted file mode 100644 index 0d7a88eb80..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation6Test.cs +++ /dev/null @@ -1,431 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class SellCancellation6Test - { - private readonly IAccountStateDelta _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - - public SellCancellation6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(currency); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(Addresses.Shop, new ShopState().Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", true, 1, 1, 1, 1)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", true, 1, 1, 1, 1)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", true, 1, 1, 1, 1)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", true, 2, 1, 2, 2)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", true, 2, 2, 3, 3)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", false, 1, 1, 0, 1)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", false, 1, 1, 0, 1)] - public void Execute(ItemType itemType, string guid, bool contain, int itemCount, int inventoryCount, int prevCount, int expectedCount) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - Guid itemId = new Guid(guid); - Guid productId = itemId; - ItemSubType itemSubType; - const long requiredBlockIndex = Sell6.ExpiredBlockIndex; - ShopState legacyShopState = _initialState.GetShopState(); - if (itemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else if (itemType == ItemType.Costume) - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - tradableItem = costume; - itemSubType = costume.ItemSubType; - } - else - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - itemSubType = material.ItemSubType; - material.RequiredBlockIndex = requiredBlockIndex; - tradableItem = material; - } - - var result = new DailyReward2.DailyRewardResult() - { - id = default, - materials = new Dictionary(), - }; - - for (var i = 0; i < 100; i++) - { - var mail = new DailyRewardMail(result, i, default, 0); - avatarState.Update2(mail); - } - - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - var shopItem = new ShopItem( - _agentAddress, - _avatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - requiredBlockIndex, - tradableItem, - itemCount); - - if (contain) - { - shopState.Register(shopItem); - if (inventoryCount > 1) - { - for (int i = 0; i < inventoryCount; i++) - { - // Different RequiredBlockIndex for divide inventory slot. - var tradable = new TradableMaterial((Dictionary)tradableItem.Serialize()) - { - RequiredBlockIndex = tradableItem.RequiredBlockIndex - i, - }; - avatarState.inventory.AddItem2(tradable, 2 - i); - } - } - else - { - avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); - } - - Assert.Empty(legacyShopState.Products); - Assert.Single(shopState.Products); - Assert.Equal(inventoryCount, avatarState.inventory.Items.Count); - Assert.Equal(prevCount, avatarState.inventory.Items.Sum(i => i.count)); - } - else - { - legacyShopState.Register(shopItem); - Assert.Single(legacyShopState.Products); - Assert.Empty(shopState.Products); - } - - Assert.Equal(requiredBlockIndex, tradableItem.RequiredBlockIndex); - Assert.Equal( - contain, - avatarState.inventory.TryGetTradableItems(itemId, requiredBlockIndex, itemCount, out _) - ); - - IAccountStateDelta prevState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(Addresses.Shop, legacyShopState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var sellCancellationAction = new SellCancellation6 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = prevState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - ShardedShopState nextShopState = new ShardedShopState((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.Empty(nextShopState.Products); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(expectedCount, nextAvatarState.inventory.Items.Sum(i => i.count)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems( - itemId, - 0, - itemCount, - out List _ - )); - Assert.True(nextAvatarState.inventory.TryGetTradableItems( - itemId, - requiredBlockIndex, - itemCount, - out List inventoryItems - )); - Assert.Empty(inventoryItems.Select(i => (ITradableItem)i.item).Where(item => item.RequiredBlockIndex == requiredBlockIndex)); - Assert.Equal(inventoryCount, inventoryItems.Count); - Inventory.Item inventoryItem = inventoryItems.First(); - Assert.Equal(itemCount, inventoryItem.count); - Assert.Equal(inventoryCount, nextAvatarState.inventory.Items.Count); - ITradableItem nextTradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(1, nextTradableItem.RequiredBlockIndex); - Assert.Equal(30, nextAvatarState.mailBox.Count); - ShopState nextLegacyShopState = nextState.GetShopState(); - Assert.Empty(nextLegacyShopState.Products); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new SellCancellation6 - { - productId = default, - sellerAvatarAddress = default, - itemSubType = ItemSubType.Weapon, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - Random = new TestRandom(), - Signer = default, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_initialState.GetAvatarState(_avatarAddress)) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - IAccountStateDelta prevState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new SellCancellation6 - { - productId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Weapon, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = prevState, - Signer = _agentAddress, - })); - } - - [Theory] - [InlineData(ItemType.Equipment)] - [InlineData(ItemType.Consumable)] - [InlineData(ItemType.Costume)] - [InlineData(ItemType.Material)] - public void Execute_Throw_ItemDoesNotExistException(ItemType itemType) - { - Guid productId = Guid.NewGuid(); - ITradableItem tradableItem; - ItemSheet.Row row; - switch (itemType) - { - case ItemType.Consumable: - row = _tableSheets.ConsumableItemSheet.First; - break; - case ItemType.Costume: - row = _tableSheets.CostumeItemSheet.First; - break; - case ItemType.Equipment: - row = _tableSheets.EquipmentItemSheet.First; - break; - case ItemType.Material: - row = _tableSheets.MaterialItemSheet.OrderedList - .First(r => r.ItemSubType == ItemSubType.Hourglass); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - if (itemType == ItemType.Material) - { - tradableItem = ItemFactory.CreateTradableMaterial((MaterialItemSheet.Row)row); - } - else - { - tradableItem = (ITradableItem)ItemFactory.CreateItem(row, new TestRandom()); - } - - tradableItem.RequiredBlockIndex = Sell6.ExpiredBlockIndex; - - Address shardedShopAddress = ShardedShopState.DeriveAddress(tradableItem.ItemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - - var shopItem = new ShopItem( - _agentAddress, - _avatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - tradableItem); - - IAccountStateDelta prevState = _initialState - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation6 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = tradableItem.ItemSubType, - }; - - ItemDoesNotExistException exc = Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - Random = new TestRandom(), - Signer = _agentAddress, - }) - ); - Assert.Equal(itemType == ItemType.Material, !exc.Message.Contains("legacy")); - } - - [Fact] - public void Execute_Throw_InvalidAddressException_From_Agent() - { - Guid productId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemUsable.ItemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - - var shopItem = new ShopItem( - default, - _avatarAddress, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - itemUsable); - shopState.Register(shopItem); - - IAccountStateDelta prevState = _initialState - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation6 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - Random = new TestRandom(), - Signer = _agentAddress, - }) - ); - } - - [Fact] - public void Execute_Throw_InvalidAddressException_From_Avatar() - { - Guid productId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemUsable.ItemSubType, productId); - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - - var shopItem = new ShopItem( - _agentAddress, - default, - productId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - Sell6.ExpiredBlockIndex, - itemUsable); - shopState.Register(shopItem); - - IAccountStateDelta prevState = _initialState - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation6 - { - productId = shopItem.ProductId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - Random = new TestRandom(), - Signer = _agentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/SellTest.cs b/.Lib9c.Tests/Action/SellTest.cs deleted file mode 100644 index d9a511780a..0000000000 --- a/.Lib9c.Tests/Action/SellTest.cs +++ /dev/null @@ -1,505 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class SellTest - { - private const long ProductPrice = 100; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private IAccountStateDelta _initialState; - - public SellTest(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Consumable, 1, true)] - [InlineData(ItemType.Costume, 1, false)] - [InlineData(ItemType.Equipment, 1, true)] - [InlineData(ItemType.Material, 1, false)] - public void Execute( - ItemType itemType, - int itemCount, - bool backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - - ITradableItem tradableItem; - switch (itemType) - { - case ItemType.Consumable: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.ConsumableItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Costume: - tradableItem = ItemFactory.CreateCostume( - _tableSheets.CostumeItemSheet.First, - Guid.NewGuid()); - break; - case ItemType.Equipment: - tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - 0); - break; - case ItemType.Material: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemType), itemType, null); - } - - Assert.Equal(0, tradableItem.RequiredBlockIndex); - avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount); - - var previousStates = _initialState; - if (backward) - { - previousStates = previousStates.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - previousStates = previousStates - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - var currencyState = previousStates.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var orderId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var orderAddress = Order.DeriveAddress(orderId); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - tradableItem.ItemSubType, - orderId); - long blockIndex = 1; - Assert.Null(previousStates.GetState(shardedShopAddress)); - - var sellAction = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - orderId = orderId, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Rehearsal = false, - Signer = _agentAddress, - Random = new TestRandom(), - }); - - long expiredBlockIndex = Order.ExpirationInterval + blockIndex; - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out var inventoryItem)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, blockIndex, itemCount, out _)); - Assert.False(nextAvatarState.inventory.TryGetTradableItems(tradableItem.TradableId, expiredBlockIndex, itemCount, out _)); - ITradableItem nextTradableItem = (ITradableItem)inventoryItem.item; - Assert.Equal(expiredBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopStateV2((Dictionary)nextSerializedShardedShopState); - Assert.Single(nextShardedShopState.OrderDigestList); - var orderDigest = nextShardedShopState.OrderDigestList.First(o => o.OrderId.Equals(orderId)); - Assert.Equal(price, orderDigest.Price); - Assert.Equal(blockIndex, orderDigest.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, orderDigest.ExpiredBlockIndex); - Assert.Equal(((ItemBase)tradableItem).Id, orderDigest.ItemId); - Assert.Equal(tradableItem.TradableId, orderDigest.TradableId); - - var serializedOrder = nextState.GetState(orderAddress); - Assert.NotNull(serializedOrder); - var serializedItem = nextState.GetState(Addresses.GetItemAddress(tradableItem.TradableId)); - Assert.NotNull(serializedItem); - - var order = OrderFactory.Deserialize((Dictionary)serializedOrder); - ITradableItem orderItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)serializedItem); - - Assert.Equal(price, order.Price); - Assert.Equal(orderId, order.OrderId); - Assert.Equal(tradableItem.TradableId, order.TradableId); - Assert.Equal(blockIndex, order.StartedBlockIndex); - Assert.Equal(expiredBlockIndex, order.ExpiredBlockIndex); - Assert.Equal(_agentAddress, order.SellerAgentAddress); - Assert.Equal(_avatarAddress, order.SellerAvatarAddress); - Assert.Equal(expiredBlockIndex, orderItem.RequiredBlockIndex); - - var receiptDict = nextState.GetState(OrderDigestListState.DeriveAddress(_avatarAddress)); - Assert.NotNull(receiptDict); - var orderDigestList = new OrderDigestListState((Dictionary)receiptDict); - Assert.Single(orderDigestList.OrderDigestList); - OrderDigest orderDigest2 = orderDigestList.OrderDigestList.First(); - Assert.Equal(orderDigest, orderDigest2); - } - - [Fact] - public void Execute_Throw_InvalidPriceException_DueTo_InvalidCurrencyPrice() - { - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = new FungibleAssetValue( -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - Currency.Legacy("KRW", 0, null), -#pragma warning restore CS0618 - 1, - 0), - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException_DueTo_NonZeroMinorUnitPrice() - { - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = new FungibleAssetValue(_currency, 1, 1), - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException_DueTo_NegativePrice() - { - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = new FungibleAssetValue(_currency, -1, 0), - itemSubType = default, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidOperationException_DueTo_EmptyState() - { - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void Execute_Throw_ItemDoesNotExistException(bool isLock) - { - var tradableId = Guid.NewGuid(); - if (isLock) - { - var tradableItem = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - tradableId, - 0); - var orderLock = new OrderLock(Guid.NewGuid()); - _avatarState.inventory.AddItem(tradableItem, 1, orderLock); - Assert.True(_avatarState.inventory.TryGetLockedItem(orderLock, out _)); - _initialState = _initialState.SetState( - _avatarAddress.Derive(LegacyInventoryKey), - _avatarState.inventory.Serialize() - ); - } - - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_InvalidItemTypeException() - { - var equipmentId = Guid.NewGuid(); - var equipment = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - equipmentId, - 10); - _avatarState.inventory.AddItem(equipment); - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - orderId = default, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Execute_Throw_DuplicateOrderIdException() - { - ITradableItem tradableItem = (ITradableItem)ItemFactory.CreateItem( - _tableSheets.EquipmentItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Weapon), - new TestRandom()); - AvatarState avatarState = _initialState.GetAvatarState(_avatarAddress); - avatarState.inventory.AddItem((ItemBase)tradableItem); - - Guid tradableId = tradableItem.TradableId; - Guid orderId = Guid.NewGuid(); - var shardedShopAddress = ShardedShopStateV2.DeriveAddress( - ItemSubType.Weapon, - orderId); - var shardedShopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - _currency * 0, - tradableId, - 0, - ItemSubType.Weapon, - 1 - ); - shardedShopState.Add(order.Digest2(avatarState, _tableSheets.CostumeStatSheet), 1); - Assert.Single(shardedShopState.OrderDigestList); - - IAccountStateDelta previousStates = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(shardedShopAddress, shardedShopState.Serialize()); - - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - Random = new TestRandom(), - })); - } - - [Fact] - public void Rehearsal() - { - Guid tradableId = Guid.NewGuid(); - Guid orderId = Guid.NewGuid(); - var action = new Sell - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableId, - count = 1, - price = _currency * ProductPrice, - itemSubType = ItemSubType.Weapon, - orderId = orderId, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/Snapshot/TransferAsset0SnapshotTest.cs b/.Lib9c.Tests/Action/Snapshot/TransferAsset0SnapshotTest.cs deleted file mode 100644 index 81d7a606f6..0000000000 --- a/.Lib9c.Tests/Action/Snapshot/TransferAsset0SnapshotTest.cs +++ /dev/null @@ -1,118 +0,0 @@ -namespace Lib9c.Tests.Action.Snapshot -{ - using System.Collections.Immutable; - using System.Numerics; - using System.Threading.Tasks; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Common; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume.Action; - using Nekoyume.Helper; - using VerifyTests; - using VerifyXunit; - using Xunit; - using static ActionUtils; - - [UsesVerify] - public class TransferAsset0SnapshotTest - { - public TransferAsset0SnapshotTest() - { - VerifierSettings.SortPropertiesAlphabetically(); - } - - [Fact] - public Task PlainValue() - { - var action = new TransferAsset0( - default(Address), - default(Address), - Currency.Legacy("NNN", 2, null) * 100); - - return Verifier - .Verify(action.PlainValue) - .UseTypeName((Text)GetActionTypeId()); - } - - [Fact] - public Task TransferCrystal() - { - var senderPrivateKey = - new PrivateKey(ByteUtil.ParseHex( - "810234bc093e2b66406b06dd0c2d2d3320bc5f19caef7acd3f800424bd46cb60")); - var recipientPrivateKey = - new PrivateKey(ByteUtil.ParseHex( - "f8960846e9ae4ad1c23686f74c8e5f80f22336b6f2175be21db82afa8823c92d")); - var senderAddress = senderPrivateKey.ToAddress(); - var recipientAddress = recipientPrivateKey.ToAddress(); - var crystal = CrystalCalculator.CRYSTAL; - var context = new ActionContext(); - IAccountStateDelta state = new MockStateDelta().MintAsset(context, senderAddress, crystal * 100); - var actionContext = new ActionContext - { - Signer = senderAddress, - PreviousState = state, - }; - var action = new TransferAsset0( - senderAddress, - recipientAddress, - crystal * 100); - var outputState = action.Execute(actionContext); - - // Verifier does not handle tuples well when nested. - var summary = Verifier - .Verify(outputState) - .IgnoreMembersWithType>() - .IgnoreMembersWithType>() - .UseTypeName((Text)GetActionTypeId()) - .UseMethodName($"{nameof(TransferCrystal)}.summary"); - var fungibles = Verifier - .Verify(outputState.Delta.Fungibles) - .UseTypeName((Text)GetActionTypeId()) - .UseMethodName($"{nameof(TransferCrystal)}.fungibles"); - return Task.WhenAll(summary, fungibles); - } - - [Fact] - public Task TransferWithMemo() - { - var senderPrivateKey = - new PrivateKey(ByteUtil.ParseHex( - "810234bc093e2b66406b06dd0c2d2d3320bc5f19caef7acd3f800424bd46cb60")); - var recipientPrivateKey = - new PrivateKey(ByteUtil.ParseHex( - "f8960846e9ae4ad1c23686f74c8e5f80f22336b6f2175be21db82afa8823c92d")); - var senderAddress = senderPrivateKey.ToAddress(); - var recipientAddress = recipientPrivateKey.ToAddress(); - var crystal = CrystalCalculator.CRYSTAL; - var context = new ActionContext(); - var state = new MockStateDelta().MintAsset(context, senderAddress, crystal * 100); - var actionContext = new ActionContext - { - Signer = senderAddress, - PreviousState = state, - }; - var action = new TransferAsset0( - senderAddress, - recipientAddress, - crystal * 100, - "MEMO"); - var outputState = action.Execute(actionContext); - - // Verifier does not handle tuples well when nested. - var summary = Verifier - .Verify(outputState) - .IgnoreMembersWithType>() - .IgnoreMembersWithType>() - .UseTypeName((Text)GetActionTypeId()) - .UseMethodName($"{nameof(TransferWithMemo)}.summary"); - var fungibles = Verifier - .Verify(outputState.Delta.Fungibles) - .UseTypeName((Text)GetActionTypeId()) - .UseMethodName($"{nameof(TransferWithMemo)}.fungibles"); - return Task.WhenAll(summary, fungibles); - } - } -} diff --git a/.Lib9c.Tests/Action/Snapshot/transfer_asset.PlainValue.verified.txt b/.Lib9c.Tests/Action/Snapshot/transfer_asset.PlainValue.verified.txt deleted file mode 100644 index 3694f6213a..0000000000 --- a/.Lib9c.Tests/Action/Snapshot/transfer_asset.PlainValue.verified.txt +++ /dev/null @@ -1,113 +0,0 @@ -{ - Bencodex.Types.Text "type_id": { - EncodingLength: 18, - Fingerprint: { - Digest: [ - 116, - 114, - 97, - 110, - 115, - 102, - 101, - 114, - 95, - 97, - 115, - 115, - 101, - 116 - ], - EncodingLength: 18, - Kind: Text - }, - Kind: Text, - Value: transfer_asset - }, - Bencodex.Types.Text "values": { - Bencodex.Types.Text "amount": [ - { - Bencodex.Types.Text "decimalPlaces": [ - 2 - ], - Bencodex.Types.Text "minters": { - EncodingLength: 1, - Fingerprint: { - EncodingLength: 1 - } - }, - Bencodex.Types.Text "ticker": { - EncodingLength: 6, - Fingerprint: { - Digest: [ - 78, - 78, - 78 - ], - EncodingLength: 6, - Kind: Text - }, - Kind: Text, - Value: NNN - } - }, - { - EncodingLength: 7, - Fingerprint: { - Digest: [ - 16, - 39 - ], - EncodingLength: 7, - Kind: Integer - }, - Kind: Integer, - Value: 10000 - } - ], - Bencodex.Types.Text "recipient": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - Bencodex.Types.Text "sender": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ] - } -} \ No newline at end of file diff --git a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferCrystal.fungibles.verified.txt b/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferCrystal.fungibles.verified.txt deleted file mode 100644 index abc9592fba..0000000000 --- a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferCrystal.fungibles.verified.txt +++ /dev/null @@ -1,4 +0,0 @@ -{ - (0x99A06c2Bd71f0EAa8196fE45BE5ca424eE809733, CRYSTAL (deedf7b43769c065ebc9ec7abe66a883df02407b)): 100000000000000000000, - (0xC61C376693E6B26717C9ee9aCA5A2453028f6ffA, CRYSTAL (deedf7b43769c065ebc9ec7abe66a883df02407b)): 0 -} \ No newline at end of file diff --git a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferCrystal.summary.verified.txt b/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferCrystal.summary.verified.txt deleted file mode 100644 index 1a74723cb7..0000000000 --- a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferCrystal.summary.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -{ - Delta: { - FungibleUpdatedAddresses: [ - 99A06c2Bd71f0EAa8196fE45BE5ca424eE809733, - C61C376693E6B26717C9ee9aCA5A2453028f6ffA - ], - UpdatedAddresses: [ - 99A06c2Bd71f0EAa8196fE45BE5ca424eE809733, - C61C376693E6B26717C9ee9aCA5A2453028f6ffA - ] - } -} \ No newline at end of file diff --git a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferWithMemo.fungibles.verified.txt b/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferWithMemo.fungibles.verified.txt deleted file mode 100644 index abc9592fba..0000000000 --- a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferWithMemo.fungibles.verified.txt +++ /dev/null @@ -1,4 +0,0 @@ -{ - (0x99A06c2Bd71f0EAa8196fE45BE5ca424eE809733, CRYSTAL (deedf7b43769c065ebc9ec7abe66a883df02407b)): 100000000000000000000, - (0xC61C376693E6B26717C9ee9aCA5A2453028f6ffA, CRYSTAL (deedf7b43769c065ebc9ec7abe66a883df02407b)): 0 -} \ No newline at end of file diff --git a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferWithMemo.summary.verified.txt b/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferWithMemo.summary.verified.txt deleted file mode 100644 index 1a74723cb7..0000000000 --- a/.Lib9c.Tests/Action/Snapshot/transfer_asset.TransferWithMemo.summary.verified.txt +++ /dev/null @@ -1,12 +0,0 @@ -{ - Delta: { - FungibleUpdatedAddresses: [ - 99A06c2Bd71f0EAa8196fE45BE5ca424eE809733, - C61C376693E6B26717C9ee9aCA5A2453028f6ffA - ], - UpdatedAddresses: [ - 99A06c2Bd71f0EAa8196fE45BE5ca424eE809733, - C61C376693E6B26717C9ee9aCA5A2453028f6ffA - ] - } -} \ No newline at end of file diff --git a/.Lib9c.Tests/Action/TransferAsset2Test.cs b/.Lib9c.Tests/Action/TransferAsset2Test.cs deleted file mode 100644 index 0887201a52..0000000000 --- a/.Lib9c.Tests/Action/TransferAsset2Test.cs +++ /dev/null @@ -1,386 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Xunit; - - public class TransferAsset2Test - { - private static readonly Address _sender = new Address( - new byte[] - { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - - private static readonly Address _recipient = new Address(new byte[] - { - 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - private static readonly Currency _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - - [Fact] - public void Constructor_ThrowsMemoLengthOverflowException() - { - Assert.Throws(() => - new TransferAsset2(_sender, _recipient, _currency * 100, new string(' ', 100))); - } - - [Fact] - public void Execute() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - - Assert.Equal(_currency * 900, nextState.GetBalance(_sender, _currency)); - Assert.Equal(_currency * 110, nextState.GetBalance(_recipient, _currency)); - } - - [Fact] - public void ExecuteWithInvalidSigner() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - // 송금자가 직접 사인하지 않으면 실패해야 합니다. - Signer = _recipient, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000)); - // Should not allow TransferAsset2 with same sender and recipient. - var action = new TransferAsset2( - sender: _sender, - recipient: _sender, - amount: _currency * 100 - ); - - // No exception should be thrown when its index is less then 380000. - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 380001, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: _currency * 100000 - ); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ExecuteWithMinterAsSender() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyBySender = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, currencyBySender * 1000) - .SetBalance(_recipient, currencyBySender * 10)); - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: currencyBySender * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithMinterAsRecipient() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyByRecipient = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, currencyByRecipient * 1000) - .SetBalance(_sender, currencyByRecipient * 10)); - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: currencyByRecipient * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithUnactivatedRecipient() - { - var activatedAddress = new ActivatedAccountsState().AddAccount(new PrivateKey().ToAddress()); - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_sender.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetState(Addresses.ActivatedAccount, activatedAddress.Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void Rehearsal() - { - var action = new TransferAsset2( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = default, - Rehearsal = true, - BlockIndex = 1, - }); - - Assert.Equal( - ImmutableHashSet.Create( - _sender, - _recipient - ), - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item1).ToImmutableHashSet() - ); - Assert.Equal( - new[] { _currency }, - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item2).ToImmutableHashSet()); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void PlainValue(string memo) - { - var action = new TransferAsset2(_sender, _recipient, _currency * 100, memo); - Dictionary plainValue = (Dictionary)action.PlainValue; - Dictionary values = (Dictionary)plainValue["values"]; - - Assert.Equal((Text)"transfer_asset2", plainValue["type_id"]); - Assert.Equal(_sender, values["sender"].ToAddress()); - Assert.Equal(_recipient, values["recipient"].ToAddress()); - Assert.Equal(_currency * 100, values["amount"].ToFungibleAssetValue()); - if (!(memo is null)) - { - Assert.Equal(memo, values["memo"].ToDotnetString()); - } - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void LoadPlainValue(string memo) - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipient", _recipient.Serialize()), - new KeyValuePair((Text)"amount", (_currency * 100).Serialize()), - }; - if (!(memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text)"memo", memo.Serialize())); - } - - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset2") - .Add("values", new Dictionary(pairs)); - var action = new TransferAsset2(); - action.LoadPlainValue(plainValue); - - Assert.Equal(_sender, action.Sender); - Assert.Equal(_recipient, action.Recipient); - Assert.Equal(_currency * 100, action.Amount); - Assert.Equal(memo, action.Memo); - } - - [Fact] - public void LoadPlainValue_ThrowsMemoLengthOverflowException() - { - var action = new TransferAsset2(); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset2") - .Add("values", new Dictionary(new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipient", _recipient.Serialize()), - new KeyValuePair((Text)"amount", (_currency * 100).Serialize()), - new KeyValuePair((Text)"memo", new string(' ', 81).Serialize()), - })); - - Assert.Throws(() => action.LoadPlainValue(plainValue)); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void SerializeWithDotnetAPI(string memo) - { - var formatter = new BinaryFormatter(); - var action = new TransferAsset2(_sender, _recipient, _currency * 100, memo); - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (TransferAsset2)formatter.Deserialize(ms); - - Assert.Equal(_sender, deserialized.Sender); - Assert.Equal(_recipient, deserialized.Recipient); - Assert.Equal(_currency * 100, deserialized.Amount); - Assert.Equal(memo, deserialized.Memo); - } - - [Fact] - public void CheckObsolete() - { - var action = new TransferAsset2(_sender, _recipient, _currency * 1); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _sender, - Rehearsal = false, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAsset3Test.cs b/.Lib9c.Tests/Action/TransferAsset3Test.cs deleted file mode 100644 index bb8eecc583..0000000000 --- a/.Lib9c.Tests/Action/TransferAsset3Test.cs +++ /dev/null @@ -1,416 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Numerics; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Xunit; - - public class TransferAsset3Test - { - private static readonly Address _sender = new Address( - new byte[] - { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - - private static readonly Address _recipient = new Address(new byte[] - { - 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - private static readonly Currency _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - - [Fact] - public void Constructor_ThrowsMemoLengthOverflowException() - { - Assert.Throws(() => - new TransferAsset3(_sender, _recipient, _currency * 100, new string(' ', 100))); - } - - [Theory] - // activation by derive address. - [InlineData(true, false, false)] - // activation by ActivatedAccountsState. - [InlineData(false, true, false)] - // state exist. - [InlineData(false, false, true)] - public void Execute(bool activate, bool legacyActivate, bool stateExist) - { - var mockState = MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10); - - if (activate) - { - mockState = mockState.SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()); - } - - if (legacyActivate) - { - var activatedAccountState = new ActivatedAccountsState(); - activatedAccountState = activatedAccountState.AddAccount(_recipient); - mockState = mockState.SetState(activatedAccountState.address, activatedAccountState.Serialize()); - } - - if (stateExist) - { - mockState = mockState.SetState(_recipient, new AgentState(_recipient).Serialize()); - } - - var prevState = new MockStateDelta(mockState); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - - Assert.Equal(_currency * 900, nextState.GetBalance(_sender, _currency)); - Assert.Equal(_currency * 110, nextState.GetBalance(_recipient, _currency)); - } - - [Fact] - public void ExecuteWithInvalidSigner() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - // 송금자가 직접 사인하지 않으면 실패해야 합니다. - Signer = _recipient, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var balance = ImmutableDictionary<(Address, Currency), FungibleAssetValue>.Empty - .Add((_sender, _currency), _currency * 1000); - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000)); - - // Should not allow TransferAsset with same sender and recipient. - var action = new TransferAsset3( - sender: _sender, - recipient: _sender, - amount: _currency * 100 - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)) - .SetState(_recipient, new AgentState(_recipient).Serialize()); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: _currency * 100000 - ); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ExecuteWithMinterAsSender() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyBySender = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, currencyBySender * 1000) - .SetBalance(_recipient, currencyBySender * 10)); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: currencyBySender * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithMinterAsRecipient() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyByRecipient = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, currencyByRecipient * 1000) - .SetBalance(_recipient, currencyByRecipient * 10) - .SetState(_recipient, new AgentState(_recipient).Serialize())); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: currencyByRecipient * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithUnactivatedRecipient() - { - var activatedAddress = new ActivatedAccountsState().AddAccount(new PrivateKey().ToAddress()); - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_sender.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetState(Addresses.ActivatedAccount, activatedAddress.Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void Rehearsal() - { - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = default, - Rehearsal = true, - BlockIndex = 1, - }); - - Assert.Equal( - ImmutableHashSet.Create( - _sender, - _recipient - ), - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item1).ToImmutableHashSet() - ); - Assert.Equal( - new[] { _currency }, - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item2).ToImmutableHashSet()); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void PlainValue(string memo) - { - var action = new TransferAsset3(_sender, _recipient, _currency * 100, memo); - Dictionary plainValue = (Dictionary)action.PlainValue; - Dictionary values = (Dictionary)plainValue["values"]; - - Assert.Equal("transfer_asset3", (Text)plainValue["type_id"]); - Assert.Equal(_sender, values["sender"].ToAddress()); - Assert.Equal(_recipient, values["recipient"].ToAddress()); - Assert.Equal(_currency * 100, values["amount"].ToFungibleAssetValue()); - if (!(memo is null)) - { - Assert.Equal(memo, values["memo"].ToDotnetString()); - } - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void LoadPlainValue(string memo) - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipient", _recipient.Serialize()), - new KeyValuePair((Text)"amount", (_currency * 100).Serialize()), - }; - if (!(memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text)"memo", memo.Serialize())); - } - - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset3") - .Add("values", new Dictionary(pairs)); - var action = new TransferAsset3(); - action.LoadPlainValue(plainValue); - - Assert.Equal(_sender, action.Sender); - Assert.Equal(_recipient, action.Recipient); - Assert.Equal(_currency * 100, action.Amount); - Assert.Equal(memo, action.Memo); - } - - [Fact] - public void Execute_Throw_InvalidTransferCurrencyException() - { - var crystal = CrystalCalculator.CRYSTAL; - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetBalance(_sender, crystal * 1000)); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: 1000 * crystal - ); - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - })); - } - - [Fact] - public void LoadPlainValue_ThrowsMemoLengthOverflowException() - { - var action = new TransferAsset3(); - var values = new Dictionary(new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipient", _recipient.Serialize()), - new KeyValuePair((Text)"amount", (_currency * 100).Serialize()), - new KeyValuePair((Text)"memo", new string(' ', 81).Serialize()), - }); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset3") - .Add("values", values); - - Assert.Throws(() => action.LoadPlainValue(plainValue)); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void SerializeWithDotnetAPI(string memo) - { - var formatter = new BinaryFormatter(); - var action = new TransferAsset3(_sender, _recipient, _currency * 100, memo); - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (TransferAsset3)formatter.Deserialize(ms); - - Assert.Equal(_sender, deserialized.Sender); - Assert.Equal(_recipient, deserialized.Recipient); - Assert.Equal(_currency * 100, deserialized.Amount); - Assert.Equal(memo, deserialized.Memo); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAssetTest.cs b/.Lib9c.Tests/Action/TransferAssetTest.cs index 08499d420e..215ca1735b 100644 --- a/.Lib9c.Tests/Action/TransferAssetTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetTest.cs @@ -293,7 +293,7 @@ public void Execute_Throw_InvalidTransferCurrencyException() PreviousState = prevState, Signer = _sender, Rehearsal = false, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, + BlockIndex = TransferAsset.CrystalTransferringRestrictionStartIndex, })); } diff --git a/.Lib9c.Tests/Action/TransferAssetTest0.cs b/.Lib9c.Tests/Action/TransferAssetTest0.cs deleted file mode 100644 index 050aea1691..0000000000 --- a/.Lib9c.Tests/Action/TransferAssetTest0.cs +++ /dev/null @@ -1,338 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model.State; - using Xunit; - - public class TransferAssetTest0 - { - private static readonly Address _sender = new Address( - new byte[] - { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - - private static readonly Address _recipient = new Address(new byte[] - { - 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - private static readonly Currency _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - - [Fact] - public void Constructor_ThrowsMemoLengthOverflowException() - { - Assert.Throws(() => - new TransferAsset(_sender, _recipient, _currency * 100, new string(' ', 100))); - } - - [Fact] - public void Execute() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - - Assert.Equal(_currency * 900, nextState.GetBalance(_sender, _currency)); - Assert.Equal(_currency * 110, nextState.GetBalance(_recipient, _currency)); - } - - [Fact] - public void ExecuteWithInvalidSigner() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - // 송금자가 직접 사인하지 않으면 실패해야 합니다. - Signer = _recipient, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000)); - // Should not allow TransferAsset with same sender and recipient. - var action = new TransferAsset0( - sender: _sender, - recipient: _sender, - amount: _currency * 100 - ); - - // No exception should be thrown when its index is less then 380000. - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 380001, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: _currency * 100000 - ); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ExecuteWithMinterAsSender() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyBySender = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, currencyBySender * 1000) - .SetBalance(_recipient, currencyBySender * 10)); - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: currencyBySender * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithMinterAsRecipient() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyByRecipient = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, currencyByRecipient * 1000) - .SetBalance(_recipient, currencyByRecipient * 10)); - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: currencyByRecipient * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void Rehearsal() - { - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = default, - Rehearsal = true, - BlockIndex = 1, - }); - - Assert.Equal( - ImmutableHashSet.Create( - _sender, - _recipient - ), - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item1).ToImmutableHashSet() - ); - Assert.Equal( - new[] { _currency }, - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item2).ToImmutableHashSet()); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void PlainValue(string memo) - { - var action = new TransferAsset0(_sender, _recipient, _currency * 100, memo); - Dictionary plainValue = (Dictionary)action.PlainValue; - Dictionary values = (Dictionary)plainValue["values"]; - - Assert.Equal(_sender, values["sender"].ToAddress()); - Assert.Equal(_recipient, values["recipient"].ToAddress()); - Assert.Equal(_currency * 100, values["amount"].ToFungibleAssetValue()); - if (!(memo is null)) - { - Assert.Equal(memo, values["memo"].ToDotnetString()); - } - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void LoadPlainValue(string memo) - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipient", _recipient.Serialize()), - new KeyValuePair((Text)"amount", (_currency * 100).Serialize()), - }; - if (!(memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text)"memo", memo.Serialize())); - } - - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset") - .Add("values", new Dictionary(pairs)); - var action = new TransferAsset0(); - action.LoadPlainValue(plainValue); - - Assert.Equal(_sender, action.Sender); - Assert.Equal(_recipient, action.Recipient); - Assert.Equal(_currency * 100, action.Amount); - Assert.Equal(memo, action.Memo); - } - - [Fact] - public void LoadPlainValue_ThrowsMemoLengthOverflowException() - { - var action = new TransferAsset0(); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset") - .Add("values", new Dictionary(new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipient", _recipient.Serialize()), - new KeyValuePair((Text)"amount", (_currency * 100).Serialize()), - new KeyValuePair((Text)"memo", new string(' ', 81).Serialize()), - })); - - Assert.Throws(() => action.LoadPlainValue(plainValue)); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void SerializeWithDotnetAPI(string memo) - { - var formatter = new BinaryFormatter(); - var action = new TransferAsset0(_sender, _recipient, _currency * 100, memo); - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (TransferAsset0)formatter.Deserialize(ms); - - Assert.Equal(_sender, deserialized.Sender); - Assert.Equal(_recipient, deserialized.Recipient); - Assert.Equal(_currency * 100, deserialized.Amount); - Assert.Equal(memo, deserialized.Memo); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAssets0Test.cs b/.Lib9c.Tests/Action/TransferAssets0Test.cs deleted file mode 100644 index 8057fce511..0000000000 --- a/.Lib9c.Tests/Action/TransferAssets0Test.cs +++ /dev/null @@ -1,492 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.IO; - using System.Linq; - using System.Numerics; - using System.Runtime.Serialization.Formatters.Binary; - using Bencodex.Types; - using Libplanet.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Xunit; - - public class TransferAssets0Test - { - private static readonly Address _sender = new Address( - new byte[] - { - 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - - private static readonly Address _recipient = new Address(new byte[] - { - 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - - private static readonly Address _recipient2 = new Address(new byte[] - { - 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - } - ); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - private static readonly Currency _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - - [Fact] - public void Constructor_ThrowsMemoLengthOverflowException() - { - Assert.Throws(() => - new TransferAssets0( - _sender, - new List<(Address, FungibleAssetValue)>() - { - (_recipient, _currency * 100), - }, - new string(' ', 100) - ) - ); - } - - [Theory] - // activation by derive address. - [InlineData(true, false, false)] - // activation by ActivatedAccountsState. - [InlineData(false, true, false)] - // state exist. - [InlineData(false, false, true)] - public void Execute(bool activate, bool legacyActivate, bool stateExist) - { - var mockState = MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10); - if (activate) - { - mockState = mockState - .SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetState(_recipient2.Derive(ActivationKey.DeriveKey), true.Serialize()); - } - - if (legacyActivate) - { - var activatedAccountState = new ActivatedAccountsState(); - activatedAccountState = activatedAccountState - .AddAccount(_recipient) - .AddAccount(_recipient2); - mockState = mockState.SetState(activatedAccountState.address, activatedAccountState.Serialize()); - } - - if (stateExist) - { - mockState = mockState - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetState(_recipient2, new AgentState(_recipient2).Serialize()); - } - - var prevState = new MockStateDelta(mockState); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - (_recipient2, _currency * 100), - } - ); - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - - Assert.Equal(_currency * 800, nextState.GetBalance(_sender, _currency)); - Assert.Equal(_currency * 110, nextState.GetBalance(_recipient, _currency)); - Assert.Equal(_currency * 100, nextState.GetBalance(_recipient2, _currency)); - } - - [Fact] - public void ExecuteWithInvalidSigner() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - } - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - // 송금자가 직접 사인하지 않으면 실패해야 합니다. - Signer = _recipient, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetBalance(_sender, _currency * 1000)); - // Should not allow TransferAsset with same sender and recipient. - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_sender, _currency * 100), - } - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100000), - } - ); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ExecuteWithMinterAsSender() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyBySender = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, currencyBySender * 1000) - .SetBalance(_recipient, currencyBySender * 10)); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, currencyBySender * 100), - } - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithMinterAsRecipient() - { -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyByRecipient = Currency.Legacy("NCG", 2, _sender); -#pragma warning restore CS0618 - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, currencyByRecipient * 1000) - .SetBalance(_recipient, currencyByRecipient * 10)); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, currencyByRecipient * 100), - } - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void ExecuteWithUnactivatedRecipient() - { - var activatedAddress = new ActivatedAccountsState().AddAccount(new PrivateKey().ToAddress()); - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_sender.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetState(Addresses.ActivatedAccount, activatedAddress.Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - } - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Fact] - public void Rehearsal() - { - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - } - ); - - IAccountStateDelta nextState = action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = default, - Rehearsal = true, - BlockIndex = 1, - }); - - Assert.Equal( - ImmutableHashSet.Create( - _sender, - _recipient - ), - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item1).ToImmutableHashSet() - ); - Assert.Equal( - new[] { _currency }, - nextState.Delta.UpdatedFungibleAssets.Select(pair => pair.Item2).ToImmutableHashSet()); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void PlainValue(string memo) - { - var action = new TransferAssets0( - _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - }, - memo - ); - - Dictionary plainValue = (Dictionary)action.PlainValue; - var values = (Dictionary)plainValue["values"]; - var recipients = (List)values["recipients"]; - var info = (List)recipients[0]; - Assert.Equal(_sender, values["sender"].ToAddress()); - Assert.Equal(_recipient, info[0].ToAddress()); - Assert.Equal(_currency * 100, info[1].ToFungibleAssetValue()); - if (!(memo is null)) - { - Assert.Equal(memo, values["memo"].ToDotnetString()); - } - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void LoadPlainValue(string memo) - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipients", List.Empty.Add(List.Empty.Add(_recipient.Serialize()).Add((_currency * 100).Serialize()))), - }; - if (!(memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text)"memo", memo.Serialize())); - } - - var values = new Dictionary(pairs); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_assets") - .Add("values", values); - var action = new TransferAssets0(); - action.LoadPlainValue(plainValue); - - Assert.Equal(_sender, action.Sender); - Assert.Equal(_recipient, action.Recipients.Single().recipient); - Assert.Equal(_currency * 100, action.Recipients.Single().amount); - Assert.Equal(memo, action.Memo); - } - - [Fact] - public void LoadPlainValue_ThrowsMemoLengthOverflowException() - { - var action = new TransferAssets0(); - var values = new Dictionary(new[] - { - new KeyValuePair((Text)"sender", _sender.Serialize()), - new KeyValuePair((Text)"recipients", List.Empty.Add(List.Empty.Add(_recipient.Serialize()).Add((_currency * 100).Serialize()))), - new KeyValuePair((Text)"memo", new string(' ', 81).Serialize()), - }); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_assets") - .Add("values", values); - - Assert.Throws(() => action.LoadPlainValue(plainValue)); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void SerializeWithDotnetAPI(string memo) - { - var formatter = new BinaryFormatter(); - var action = new TransferAssets0( - _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - }, - memo - ); - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (TransferAssets0)formatter.Deserialize(ms); - - Assert.Equal(_sender, deserialized.Sender); - Assert.Equal(_recipient, deserialized.Recipients.Single().recipient); - Assert.Equal(_currency * 100, deserialized.Recipients.Single().amount); - Assert.Equal(memo, deserialized.Memo); - } - - [Fact] - public void Execute_Throw_ArgumentOutOfRangeException() - { - var recipients = new List<(Address, FungibleAssetValue)>(); - - for (int i = 0; i < TransferAssets0.RecipientsCapacity + 1; i++) - { - recipients.Add((_recipient, _currency * 100)); - } - - var action = new TransferAssets0(_sender, recipients); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new MockStateDelta(), - Signer = _sender, - Rehearsal = false, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void Execute_Throw_InvalidTransferCurrencyException() - { - var crystal = CrystalCalculator.CRYSTAL; - var prevState = new MockStateDelta( - MockState.Empty - .SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetBalance(_sender, crystal * 1000)); - var action = new TransferAssets0( - sender: _sender, - recipients: new List<(Address, FungibleAssetValue)> - { - (_recipient, 1000 * crystal), - (_recipient, 100 * _currency), - } - ); - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - Rehearsal = false, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAssetsTest.cs b/.Lib9c.Tests/Action/TransferAssetsTest.cs index 8db9787ffa..3b4d7ba978 100644 --- a/.Lib9c.Tests/Action/TransferAssetsTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetsTest.cs @@ -395,7 +395,7 @@ public void Execute_Throw_InvalidTransferCurrencyException() PreviousState = prevState, Signer = _sender, Rehearsal = false, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, + BlockIndex = TransferAsset.CrystalTransferringRestrictionStartIndex, })); } } diff --git a/.Lib9c.Tests/Action/UpdateSell0Test.cs b/.Lib9c.Tests/Action/UpdateSell0Test.cs deleted file mode 100644 index 6155376447..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell0Test.cs +++ /dev/null @@ -1,382 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class UpdateSell0Test - { - private const long ProductPrice = 100; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public UpdateSell0Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, true, true)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, true, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, true, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, true, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, true, true)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, false, true)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, false, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, false, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, false, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, false, true)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, true, false)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, true, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, true, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, true, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, true, false)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, false, false)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, false, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, false, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, false, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, false, false)] - public void Execute( - ItemType itemType, - string guid, - int itemCount, - int inventoryCount, - int expectedCount, - bool fromPreviousAction, - bool legacy - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - var itemId = new Guid(guid); - var orderId = Guid.NewGuid(); - var updateSellOrderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - switch (itemType) - { - case ItemType.Equipment: - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - break; - } - - case ItemType.Costume: - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - tradableItem = costume; - itemSubType = costume.ItemSubType; - break; - } - - default: - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - itemSubType = material.ItemSubType; - material.RequiredBlockIndex = requiredBlockIndex; - tradableItem = material; - break; - } - } - - var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - var prevState = _initialState; - - if (inventoryCount > 1) - { - for (int i = 0; i < inventoryCount; i++) - { - // Different RequiredBlockIndex for divide inventory slot. - if (tradableItem is ITradableFungibleItem tradableFungibleItem) - { - var tradable = (TradableMaterial)tradableFungibleItem.Clone(); - tradable.RequiredBlockIndex = tradableItem.RequiredBlockIndex - i; - avatarState.inventory.AddItem2(tradable, 2 - i); - } - } - } - else - { - avatarState.inventory.AddItem2((ItemBase)tradableItem, itemCount); - } - - var sellItem = legacy ? order.Sell2(avatarState) : order.Sell3(avatarState); - var orderDigest = legacy ? order.Digest2(avatarState, _tableSheets.CostumeStatSheet) - : order.Digest(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - if (legacy) - { - Assert.True(avatarState.inventory.TryGetTradableItems(itemId, requiredBlockIndex * 2, itemCount, out _)); - } - else - { - Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - } - - Assert.Equal(inventoryCount, avatarState.inventory.Items.Count); - Assert.Equal(expectedCount, avatarState.inventory.Items.Sum(i => i.count)); - - Assert.Single(shopState.OrderDigestList); - Assert.Single(orderDigestList.OrderDigestList); - - Assert.Equal(requiredBlockIndex * 2, sellItem.RequiredBlockIndex); - - if (fromPreviousAction) - { - prevState = prevState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - prevState = prevState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - prevState = prevState - .SetState(Addresses.GetItemAddress(itemId), sellItem.Serialize()) - .SetState(Order.DeriveAddress(order.OrderId), order.Serialize()) - .SetState(orderDigestList.Address, orderDigestList.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var currencyState = prevState.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var action = new UpdateSell0 - { - orderId = orderId, - updateSellOrderId = updateSellOrderId, - tradableId = itemId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - price = price, - count = itemCount, - }; - var nextState = action.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(updateSellShopAddress)); - Assert.Equal(1, nextShopState.OrderDigestList.Count); - Assert.NotEqual(orderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(updateSellOrderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(itemId, nextShopState.OrderDigestList.First().TradableId); - Assert.Equal(requiredBlockIndex + 101, nextShopState.OrderDigestList.First().ExpiredBlockIndex); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new UpdateSell0 - { - orderId = default, - updateSellOrderId = default, - tradableId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - price = 0 * _currency, - count = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new UpdateSell0 - { - orderId = default, - updateSellOrderId = default, - tradableId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = default, - price = -1 * _currency, - count = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new UpdateSell0 - { - updateSellOrderId = default, - orderId = default, - tradableId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - price = 0 * _currency, - count = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Rehearsal() - { - var tradableId = Guid.NewGuid(); - var orderId = Guid.NewGuid(); - var updateSellOrderId = Guid.NewGuid(); - var action = new UpdateSell0 - { - orderId = orderId, - updateSellOrderId = updateSellOrderId, - tradableId = tradableId, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Weapon, - price = _currency * ProductPrice, - count = 1, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(updateSellOrderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, updateSellOrderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/UpdateSell2Test.cs b/.Lib9c.Tests/Action/UpdateSell2Test.cs deleted file mode 100644 index 782e2fa650..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell2Test.cs +++ /dev/null @@ -1,363 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static SerializeKeys; - - public class UpdateSell2Test - { - private const long ProductPrice = 100; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public UpdateSell2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, true)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, true)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, false)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, false)] - public void Execute( - ItemType itemType, - string guid, - int itemCount, - int inventoryCount, - int expectedCount, - bool fromPreviousAction - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - var itemId = new Guid(guid); - var orderId = Guid.NewGuid(); - var updateSellOrderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - switch (itemType) - { - case ItemType.Equipment: - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - break; - } - - case ItemType.Costume: - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - tradableItem = costume; - itemSubType = costume.ItemSubType; - break; - } - - default: - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - itemSubType = material.ItemSubType; - material.RequiredBlockIndex = requiredBlockIndex; - tradableItem = material; - break; - } - } - - var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - var prevState = _initialState; - - if (inventoryCount > 1) - { - for (int i = 0; i < inventoryCount; i++) - { - // Different RequiredBlockIndex for divide inventory slot. - if (tradableItem is ITradableFungibleItem tradableFungibleItem) - { - var tradable = (TradableMaterial)tradableFungibleItem.Clone(); - tradable.RequiredBlockIndex = tradableItem.RequiredBlockIndex - i; - avatarState.inventory.AddItem(tradable, 2 - i); - } - } - } - else - { - avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount); - } - - var sellItem = order.Sell(avatarState); - var orderDigest = order.Digest(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - - Assert.Equal(inventoryCount, avatarState.inventory.Items.Count); - Assert.Equal(expectedCount, avatarState.inventory.Items.Sum(i => i.count)); - - Assert.Single(shopState.OrderDigestList); - Assert.Single(orderDigestList.OrderDigestList); - - Assert.Equal(requiredBlockIndex * 2, sellItem.RequiredBlockIndex); - - if (fromPreviousAction) - { - prevState = prevState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - prevState = prevState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - prevState = prevState - .SetState(Addresses.GetItemAddress(itemId), sellItem.Serialize()) - .SetState(Order.DeriveAddress(order.OrderId), order.Serialize()) - .SetState(orderDigestList.Address, orderDigestList.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var currencyState = prevState.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - var action = new UpdateSell2 - { - orderId = orderId, - updateSellOrderId = updateSellOrderId, - tradableId = itemId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - price = price, - count = itemCount, - }; - var nextState = action.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(updateSellShopAddress)); - Assert.Equal(1, nextShopState.OrderDigestList.Count); - Assert.NotEqual(orderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(updateSellOrderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(itemId, nextShopState.OrderDigestList.First().TradableId); - Assert.Equal(requiredBlockIndex + 101, nextShopState.OrderDigestList.First().ExpiredBlockIndex); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new UpdateSell2 - { - orderId = default, - updateSellOrderId = default, - tradableId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - price = 0 * _currency, - count = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new UpdateSell2 - { - orderId = default, - updateSellOrderId = default, - tradableId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = default, - price = -1 * _currency, - count = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new UpdateSell2 - { - updateSellOrderId = default, - orderId = default, - tradableId = default, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Food, - price = 0 * _currency, - count = 1, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Rehearsal() - { - var tradableId = Guid.NewGuid(); - var orderId = Guid.NewGuid(); - var updateSellOrderId = Guid.NewGuid(); - var action = new UpdateSell2 - { - orderId = orderId, - updateSellOrderId = updateSellOrderId, - tradableId = tradableId, - sellerAvatarAddress = _avatarAddress, - itemSubType = ItemSubType.Weapon, - price = _currency * ProductPrice, - count = 1, - }; - - var updatedAddresses = new List
() - { - _agentAddress, - _avatarAddress, - _avatarAddress.Derive(LegacyInventoryKey), - _avatarAddress.Derive(LegacyWorldInformationKey), - _avatarAddress.Derive(LegacyQuestListKey), - Addresses.GetItemAddress(tradableId), - Order.DeriveAddress(updateSellOrderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, orderId), - ShardedShopStateV2.DeriveAddress(ItemSubType.Weapon, updateSellOrderId), - OrderDigestListState.DeriveAddress(_avatarAddress), - }; - - var state = new MockStateDelta(); - - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - Rehearsal = true, - }); - - Assert.Equal(updatedAddresses.ToImmutableHashSet(), nextState.Delta.UpdatedAddresses); - } - } -} diff --git a/.Lib9c.Tests/Action/UpdateSell3Test.cs b/.Lib9c.Tests/Action/UpdateSell3Test.cs deleted file mode 100644 index 27db783417..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell3Test.cs +++ /dev/null @@ -1,390 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class UpdateSell3Test - { - private const long ProductPrice = 100; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public UpdateSell3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, true)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, true)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, false)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, false)] - public void Execute( - ItemType itemType, - string guid, - int itemCount, - int inventoryCount, - int expectedCount, - bool fromPreviousAction - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - var itemId = new Guid(guid); - var orderId = Guid.NewGuid(); - var updateSellOrderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - switch (itemType) - { - case ItemType.Equipment: - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - break; - } - - case ItemType.Costume: - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - tradableItem = costume; - itemSubType = costume.ItemSubType; - break; - } - - default: - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - itemSubType = material.ItemSubType; - material.RequiredBlockIndex = requiredBlockIndex; - tradableItem = material; - break; - } - } - - var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - var prevState = _initialState; - - if (inventoryCount > 1) - { - for (int i = 0; i < inventoryCount; i++) - { - // Different RequiredBlockIndex for divide inventory slot. - if (tradableItem is ITradableFungibleItem tradableFungibleItem) - { - var tradable = (TradableMaterial)tradableFungibleItem.Clone(); - tradable.RequiredBlockIndex = tradableItem.RequiredBlockIndex - i; - avatarState.inventory.AddItem(tradable, 2 - i); - } - } - } - else - { - avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount); - } - - var sellItem = order.Sell(avatarState); - var orderDigest = order.Digest(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - - Assert.Equal(inventoryCount, avatarState.inventory.Items.Count); - Assert.Equal(expectedCount, avatarState.inventory.Items.Sum(i => i.count)); - - Assert.Single(shopState.OrderDigestList); - Assert.Single(orderDigestList.OrderDigestList); - - Assert.Equal(requiredBlockIndex * 2, sellItem.RequiredBlockIndex); - - if (fromPreviousAction) - { - prevState = prevState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - prevState = prevState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - prevState = prevState - .SetState(Addresses.GetItemAddress(itemId), sellItem.Serialize()) - .SetState(Order.DeriveAddress(order.OrderId), order.Serialize()) - .SetState(orderDigestList.Address, orderDigestList.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var currencyState = prevState.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - - var updateSellInfo = new UpdateSellInfo( - orderId, - updateSellOrderId, - itemId, - itemSubType, - price, - itemCount - ); - - var action = new UpdateSell3 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - var nextState = action.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(updateSellShopAddress)); - Assert.Equal(1, nextShopState.OrderDigestList.Count); - Assert.NotEqual(orderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(updateSellOrderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(itemId, nextShopState.OrderDigestList.First().TradableId); - Assert.Equal(requiredBlockIndex + 101, nextShopState.OrderDigestList.First().ExpiredBlockIndex); - } - - [Fact] - public void Execute_Throw_ListEmptyException() - { - var action = new UpdateSell3 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - ItemSubType.Food, - 0 * _currency, - 1); - - var action = new UpdateSell3 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - ItemSubType.Food, - 0 * _currency, - 1); - - var action = new UpdateSell3 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop - ), - }; - var digestListAddress = OrderDigestListState.DeriveAddress(_avatarAddress); - var digestList = new OrderDigestListState(digestListAddress); - _initialState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(digestListAddress, digestList.Serialize()); - - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - default, - -1 * _currency, - 1); - - var action = new UpdateSell3 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - default, - -1 * _currency, - 1); - - var action = new UpdateSell3 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = ActionObsoleteConfig.V100320ObsoleteIndex + 1, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/UpdateSell4Test.cs b/.Lib9c.Tests/Action/UpdateSell4Test.cs deleted file mode 100644 index 453a015f1e..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell4Test.cs +++ /dev/null @@ -1,409 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Bencodex.Types; - using Lib9c.Model.Order; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Battle; - using Nekoyume.Model; - using Nekoyume.Model.Item; - using Nekoyume.Model.State; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class UpdateSell4Test - { - private const long ProductPrice = 100; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly Currency _currency; - private readonly AvatarState _avatarState; - private readonly TableSheets _tableSheets; - private readonly GoldCurrencyState _goldCurrencyState; - private IAccountStateDelta _initialState; - - public UpdateSell4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new MockStateDelta(); - var sheets = TableSheetsImporter.ImportSheets(); - foreach (var (key, value) in sheets) - { - _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - _goldCurrencyState = new GoldCurrencyState(_currency); - - var shopState = new ShopState(); - - _agentAddress = new PrivateKey().ToAddress(); - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().ToAddress(); - var rankingMapAddress = new PrivateKey().ToAddress(); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop), - }; - agentState.avatarAddresses[0] = _avatarAddress; - - _initialState = _initialState - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(Addresses.Shop, shopState.Serialize()) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, _avatarState.Serialize()); - } - - [Theory] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, true)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, true)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, true)] - [InlineData(ItemType.Equipment, "F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4", 1, 1, 1, false)] - [InlineData(ItemType.Costume, "936DA01F-9ABD-4d9d-80C7-02AF85C822A8", 1, 1, 1, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 1, 1, 1, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 1, 2, false)] - [InlineData(ItemType.Material, "15396359-04db-68d5-f24a-d89c18665900", 2, 2, 3, false)] - public void Execute( - ItemType itemType, - string guid, - int itemCount, - int inventoryCount, - int expectedCount, - bool fromPreviousAction - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - var itemId = new Guid(guid); - var orderId = Guid.NewGuid(); - var updateSellOrderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - switch (itemType) - { - case ItemType.Equipment: - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - break; - } - - case ItemType.Costume: - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, itemId); - costume.Update(requiredBlockIndex); - tradableItem = costume; - itemSubType = costume.ItemSubType; - break; - } - - default: - { - var material = ItemFactory.CreateTradableMaterial( - _tableSheets.MaterialItemSheet.OrderedList.First(r => r.ItemSubType == ItemSubType.Hourglass)); - itemSubType = material.ItemSubType; - material.RequiredBlockIndex = requiredBlockIndex; - tradableItem = material; - break; - } - } - - var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - var order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - var prevState = _initialState; - - if (inventoryCount > 1) - { - for (int i = 0; i < inventoryCount; i++) - { - // Different RequiredBlockIndex for divide inventory slot. - if (tradableItem is ITradableFungibleItem tradableFungibleItem) - { - var tradable = (TradableMaterial)tradableFungibleItem.Clone(); - tradable.RequiredBlockIndex = tradableItem.RequiredBlockIndex - i; - avatarState.inventory.AddItem(tradable, 2 - i); - } - } - } - else - { - avatarState.inventory.AddItem((ItemBase)tradableItem, itemCount); - } - - var sellItem = order.Sell(avatarState); - var orderDigest = order.Digest(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - - Assert.Equal(inventoryCount, avatarState.inventory.Items.Count); - Assert.Equal(expectedCount, avatarState.inventory.Items.Sum(i => i.count)); - - Assert.Single(shopState.OrderDigestList); - Assert.Single(orderDigestList.OrderDigestList); - - Assert.Equal(requiredBlockIndex * 2, sellItem.RequiredBlockIndex); - - if (fromPreviousAction) - { - prevState = prevState.SetState(_avatarAddress, avatarState.Serialize()); - } - else - { - prevState = prevState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()) - .SetState(_avatarAddress, avatarState.SerializeV2()); - } - - prevState = prevState - .SetState(Addresses.GetItemAddress(itemId), sellItem.Serialize()) - .SetState(Order.DeriveAddress(order.OrderId), order.Serialize()) - .SetState(orderDigestList.Address, orderDigestList.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var currencyState = prevState.GetGoldCurrency(); - var price = new FungibleAssetValue(currencyState, ProductPrice, 0); - - var updateSellInfo = new UpdateSellInfo( - orderId, - updateSellOrderId, - itemId, - itemSubType, - price, - itemCount - ); - - var action = new UpdateSell4 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - var nextState = action.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - Random = new TestRandom(), - Rehearsal = false, - Signer = _agentAddress, - }); - - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); - var nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(updateSellShopAddress)); - Assert.Equal(1, nextShopState.OrderDigestList.Count); - Assert.NotEqual(orderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(updateSellOrderId, nextShopState.OrderDigestList.First().OrderId); - Assert.Equal(itemId, nextShopState.OrderDigestList.First().TradableId); - Assert.Equal(requiredBlockIndex + 101, nextShopState.OrderDigestList.First().ExpiredBlockIndex); - } - - [Fact] - public void Execute_Throw_ListEmptyException() - { - var action = new UpdateSell4 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - ItemSubType.Food, - 0 * _currency, - 1); - - var action = new UpdateSell4 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new MockStateDelta(), - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - _initialState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - ItemSubType.Food, - 0 * _currency, - 1); - - var action = new UpdateSell4 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var avatarState = new AvatarState(_avatarState) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInShop - ), - }; - var digestListAddress = OrderDigestListState.DeriveAddress(_avatarAddress); - var digestList = new OrderDigestListState(digestListAddress); - _initialState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(digestListAddress, digestList.Serialize()); - - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - default, - -1 * _currency, - 1); - - var action = new UpdateSell4 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = new[] { updateSellInfo }, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - - [Theory] - [InlineData(100, false)] - [InlineData(1, false)] - [InlineData(101, true)] - public void PurchaseInfos_Capacity(int count, bool exc) - { - var updateSellInfo = new UpdateSellInfo( - default, - default, - default, - default, - -1 * _currency, - 1); - var updateSellInfos = new List(); - for (int i = 0; i < count; i++) - { - updateSellInfos.Add(updateSellInfo); - } - - var action = new UpdateSell4 - { - sellerAvatarAddress = _avatarAddress, - updateSellInfos = updateSellInfos, - }; - if (exc) - { - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - else - { - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - })); - } - } - } -} diff --git a/Lib9c/Action/BattleArena1.cs b/Lib9c/Action/BattleArena1.cs deleted file mode 100644 index 3e725de7b4..0000000000 --- a/Lib9c/Action/BattleArena1.cs +++ /dev/null @@ -1,322 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1156 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena")] - public class BattleArena1 : GameAction, IBattleArenaV1 - { - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100290ObsoleteIndex, context); - - var addressesHex = - GetSignerAndOtherAddressesHex(context, myAvatarAddress, enemyAvatarAddress); - - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, myAvatarAddress, - out var avatarState, out var _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets =states.GetSheetsV100291( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena1)}] ChampionshipId({arenaRow.ChampionshipId}) - round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena1)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena1)}] ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena1)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena1)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena1)}] my avatar address : {myAvatarAddress}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState(enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena1)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = - ArenaScore.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena1)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = - ArenaScore.DeriveAddress(enemyAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena1)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena1)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV1(ArenaHelper.ScoreLimitsV1, roundData.ArenaType, - myArenaScore.Score, enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena1)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = diff({scoreDiff})"); - } - - var gameConfigState = states.GetGameConfigState(); - var interval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, interval); - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - } - - arenaInformation.UseTicket(ticket); - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest(avatarState, myArenaAvatarState); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest(enemyAvatarState, enemyArenaAvatarState); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets_v100291(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(context.Random); - var log = simulator.SimulateV1(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal(roundData.ChampionshipId, roundData.Round, materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - var inventoryAddress = myAvatarAddress.Derive(LegacyInventoryKey); - var questListAddress = myAvatarAddress.Derive(LegacyQuestListKey); - - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(myAvatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/BattleArena10.cs b/Lib9c/Action/BattleArena10.cs deleted file mode 100644 index 6cc31aa7b6..0000000000 --- a/Lib9c/Action/BattleArena10.cs +++ /dev/null @@ -1,469 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// Hard forked at https://github.com/planetarium/lib9c/pull/1649 - /// Updated at https://github.com/planetarium/lib9c/pull/1679 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena10")] - public class BattleArena10 : GameAction, IBattleArenaV1 - { - public const string PurchasedCountKey = "purchased_count_during_interval"; - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - public List runeInfos; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - IEnumerable IBattleArenaV1.RuneSlotInfos => runeInfos - .Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - [RuneInfos] = runeInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - runeInfos = plainValue[RuneInfos].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(runeInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Arena); - itemSlotState.UpdateEquipment(equipments); - itemSlotState.UpdateCostumes(costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}"); - } - - var gameConfigState = states.GetGameConfigState(); - var battleArenaInterval = roundData.ArenaType == ArenaType.OffSeason - ? 1 - : gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifference( - ArenaHelper.ScoreLimits, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var dailyArenaInterval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, dailyArenaInterval); - var purchasedCountAddr = arenaInformation.Address.Derive(PurchasedCountKey); - if (!states.TryGetState(purchasedCountAddr, out Integer purchasedCountDuringInterval)) - { - purchasedCountDuringInterval = 0; - } - - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - purchasedCountDuringInterval = 0; - states = states.SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else if (ticket > 1) - { - throw new TicketPurchaseLimitExceedException( - $"[{nameof(ArenaInformation)}] tickets to buy : {ticket}"); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - arenaInformation.BuyTicket(roundData.MaxPurchaseCount); - if (purchasedCountDuringInterval >= roundData.MaxPurchaseCountWithInterval) - { - throw new ExceedTicketPurchaseLimitDuringIntervalException( - $"[{nameof(ArenaInformation)}] PurchasedTicketCount({purchasedCountDuringInterval}) >= MAX({{max}})"); - } - - purchasedCountDuringInterval++; - states = states - .TransferAsset(context, context.Signer, arenaAdr, ticketBalance) - .SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - var runeStates = new List(); - foreach (var address in runeInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // get enemy equipped items - var enemyItemSlotStateAddress = ItemSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyItemSlotState = states.TryGetState(enemyItemSlotStateAddress, out List rawEnemyItemSlotState) - ? new ItemSlotState(rawEnemyItemSlotState) - : new ItemSlotState(BattleType.Arena); - var enemyRuneSlotStateAddress = RuneSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyRuneSlotState = states.TryGetState(enemyRuneSlotStateAddress, out List enemyRawRuneSlotState) - ? new RuneSlotState(enemyRawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - - var enemyRuneStates = new List(); - var enemyRuneSlotInfos = enemyRuneSlotState.GetEquippedRuneSlotInfos(); - foreach (var address in enemyRuneSlotInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - enemyRuneStates.Add(new RuneState(rawRuneState)); - } - } - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - var myArenaPlayerDigest = new ArenaPlayerDigest( - avatarState, - equipments, - costumes, - runeStates); - var enemyArenaPlayerDigest = new ArenaPlayerDigest( - enemyAvatarState, - enemyItemSlotState.Equipments, - enemyItemSlotState.Costumes, - enemyRuneStates); - var previousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV3(context.Random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - myArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(previousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScores(previousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleArena11.cs b/Lib9c/Action/BattleArena11.cs deleted file mode 100644 index 229c296794..0000000000 --- a/Lib9c/Action/BattleArena11.cs +++ /dev/null @@ -1,467 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1930 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena11")] - public class BattleArena11 : GameAction, IBattleArenaV1 - { - public const string PurchasedCountKey = "purchased_count_during_interval"; - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - public List runeInfos; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - IEnumerable IBattleArenaV1.RuneSlotInfos => runeInfos - .Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - [RuneInfos] = runeInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - runeInfos = plainValue[RuneInfos].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(runeInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Arena); - itemSlotState.UpdateEquipment(equipments); - itemSlotState.UpdateCostumes(costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}"); - } - - var gameConfigState = states.GetGameConfigState(); - var battleArenaInterval = roundData.ArenaType == ArenaType.OffSeason - ? 1 - : gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifference( - ArenaHelper.ScoreLimits, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var dailyArenaInterval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, dailyArenaInterval); - var purchasedCountAddr = arenaInformation.Address.Derive(PurchasedCountKey); - if (!states.TryGetState(purchasedCountAddr, out Integer purchasedCountDuringInterval)) - { - purchasedCountDuringInterval = 0; - } - - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - purchasedCountDuringInterval = 0; - states = states.SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else if (ticket > 1) - { - throw new TicketPurchaseLimitExceedException( - $"[{nameof(ArenaInformation)}] tickets to buy : {ticket}"); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - arenaInformation.BuyTicket(roundData.MaxPurchaseCount); - if (purchasedCountDuringInterval >= roundData.MaxPurchaseCountWithInterval) - { - throw new ExceedTicketPurchaseLimitDuringIntervalException( - $"[{nameof(ArenaInformation)}] PurchasedTicketCount({purchasedCountDuringInterval}) >= MAX({{max}})"); - } - - purchasedCountDuringInterval++; - states = states - .TransferAsset(context, context.Signer, arenaAdr, ticketBalance) - .SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - var runeStates = new List(); - foreach (var address in runeInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // get enemy equipped items - var enemyItemSlotStateAddress = ItemSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyItemSlotState = states.TryGetState(enemyItemSlotStateAddress, out List rawEnemyItemSlotState) - ? new ItemSlotState(rawEnemyItemSlotState) - : new ItemSlotState(BattleType.Arena); - var enemyRuneSlotStateAddress = RuneSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyRuneSlotState = states.TryGetState(enemyRuneSlotStateAddress, out List enemyRawRuneSlotState) - ? new RuneSlotState(enemyRawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - - var enemyRuneStates = new List(); - var enemyRuneSlotInfos = enemyRuneSlotState.GetEquippedRuneSlotInfos(); - foreach (var address in enemyRuneSlotInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - enemyRuneStates.Add(new RuneState(rawRuneState)); - } - } - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - var myArenaPlayerDigest = new ArenaPlayerDigest( - avatarState, - equipments, - costumes, - runeStates); - var enemyArenaPlayerDigest = new ArenaPlayerDigest( - enemyAvatarState, - enemyItemSlotState.Equipments, - enemyItemSlotState.Costumes, - enemyRuneStates); - var previousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV4(context.Random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - myArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(previousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScores(previousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleArena2.cs b/Lib9c/Action/BattleArena2.cs deleted file mode 100644 index 3e50f46b8c..0000000000 --- a/Lib9c/Action/BattleArena2.cs +++ /dev/null @@ -1,340 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1190 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena2")] - public class BattleArena2 : GameAction, IBattleArenaV1 - { - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100290ObsoleteIndex, context); - - var addressesHex = - GetSignerAndOtherAddressesHex(context, myAvatarAddress, enemyAvatarAddress); - - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, myAvatarAddress, - out var avatarState, out var _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheetsV100291( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({arenaRow.ChampionshipId}) - round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena2)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena2)}] ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena2)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena2)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena2)}] my avatar address : {myAvatarAddress}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState(enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena2)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = - ArenaScore.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena2)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = - ArenaScore.DeriveAddress(enemyAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena2)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena2)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV1(ArenaHelper.ScoreLimitsV1, roundData.ArenaType, - myArenaScore.Score, enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena2)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = diff({scoreDiff})"); - } - - var gameConfigState = states.GetGameConfigState(); - var interval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, interval); - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena2)}] " + - $"ticket : {ticket} / arenaType : {roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else - { - var arenaAdr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - for (var i = 0; i < ticket; i++) - { - var ticketBalance = ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - states = states.TransferAsset(context, context.Signer, arenaAdr, ticketBalance); - arenaInformation.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest(avatarState, myArenaAvatarState); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest(enemyAvatarState, enemyArenaAvatarState); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets_v100291(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(context.Random); - var log = simulator.Simulate(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal(roundData.ChampionshipId, roundData.Round, materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - var inventoryAddress = myAvatarAddress.Derive(LegacyInventoryKey); - var questListAddress = myAvatarAddress.Derive(LegacyQuestListKey); - - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(myAvatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/BattleArena3.cs b/Lib9c/Action/BattleArena3.cs deleted file mode 100644 index 92bec05c85..0000000000 --- a/Lib9c/Action/BattleArena3.cs +++ /dev/null @@ -1,341 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1190 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena3")] - public class BattleArena3 : GameAction, IBattleArenaV1 - { - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100290ObsoleteIndex, context); - - var addressesHex = - GetSignerAndOtherAddressesHex(context, myAvatarAddress, enemyAvatarAddress); - - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, myAvatarAddress, - out var avatarState, out var _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - bool useV100291Sheets = UseV100291Sheets(context.BlockIndex); - var sheets = states.GetSheetsV100291( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({arenaRow.ChampionshipId}) - round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena3)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena3)}] ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena3)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena3)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena3)}] my avatar address : {myAvatarAddress}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState(enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena3)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = - ArenaScore.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena3)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = - ArenaScore.DeriveAddress(enemyAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena3)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena3)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV2(ArenaHelper.ScoreLimitsV2, roundData.ArenaType, - myArenaScore.Score, enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena3)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = diff({scoreDiff})"); - } - - var gameConfigState = states.GetGameConfigState(); - var interval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, interval); - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena3)}] " + - $"ticket : {ticket} / arenaType : {roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else - { - var arenaAdr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - for (var i = 0; i < ticket; i++) - { - var ticketBalance = ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - states = states.TransferAsset(context, context.Signer, arenaAdr, ticketBalance); - arenaInformation.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest(avatarState, myArenaAvatarState); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest(enemyAvatarState, enemyArenaAvatarState); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets_v100291(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(context.Random); - var log = simulator.Simulate(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal(roundData.ChampionshipId, roundData.Round, materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - var inventoryAddress = myAvatarAddress.Derive(LegacyInventoryKey); - var questListAddress = myAvatarAddress.Derive(LegacyQuestListKey); - - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(myAvatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/BattleArena4.cs b/Lib9c/Action/BattleArena4.cs deleted file mode 100644 index 2fdf87bfd7..0000000000 --- a/Lib9c/Action/BattleArena4.cs +++ /dev/null @@ -1,363 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1330 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena4")] - public class BattleArena4 : GameAction, IBattleArenaV1 - { - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100320ObsoleteIndex, context); - - var addressesHex = - GetSignerAndOtherAddressesHex(context, myAvatarAddress, enemyAvatarAddress); - - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, myAvatarAddress, - out var avatarState, out var _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - bool useV100291Sheets = UseV100291Sheets(context.BlockIndex); - var sheets = useV100291Sheets - ? states.GetSheetsV100291( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }) - : states.GetSheetsV1( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({arenaRow.ChampionshipId}) - round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena4)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena4)}] ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena4)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena4)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena4)}] my avatar address : {myAvatarAddress}"); - } - - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < 2) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena4)}] LastBattleBlockIndex : {myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState(enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena4)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = - ArenaScore.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena4)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = - ArenaScore.DeriveAddress(enemyAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena4)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = - ArenaInformation.DeriveAddress(myAvatarAddress, roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena4)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV2(ArenaHelper.ScoreLimitsV2, roundData.ArenaType, - myArenaScore.Score, enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena4)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = diff({scoreDiff})"); - } - - var gameConfigState = states.GetGameConfigState(); - var interval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, interval); - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena4)}] " + - $"ticket : {ticket} / arenaType : {roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else - { - var arenaAdr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - for (var i = 0; i < ticket; i++) - { - var ticketBalance = ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - states = states.TransferAsset(context, context.Signer, arenaAdr, ticketBalance); - arenaInformation.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest(avatarState, myArenaAvatarState); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest(enemyAvatarState, enemyArenaAvatarState); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = useV100291Sheets - ? sheets.GetArenaSimulatorSheets_v100291() - : sheets.GetArenaSimulatorSheetsV1(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(context.Random); - var log = simulator.Simulate(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal(roundData.ChampionshipId, roundData.Round, materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - var inventoryAddress = myAvatarAddress.Derive(LegacyInventoryKey); - var questListAddress = myAvatarAddress.Derive(LegacyQuestListKey); - - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(myAvatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/BattleArena5.cs b/Lib9c/Action/BattleArena5.cs deleted file mode 100644 index 937a672e24..0000000000 --- a/Lib9c/Action/BattleArena5.cs +++ /dev/null @@ -1,395 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1370 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena5")] - public class BattleArena5 : GameAction, IBattleArenaV1 - { - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100320ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena5)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena5)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena5)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena5)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena5)}] my avatar address : {myAvatarAddress}"); - } - - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < 2) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena5)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena5)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena5)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena5)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena5)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV2( - ArenaHelper.ScoreLimitsV2, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena5)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var gameConfigState = states.GetGameConfigState(); - var interval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, interval); - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena5)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - for (var i = 0; i < ticket; i++) - { - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - states = states.TransferAsset( - context, - context.Signer, - arenaAdr, - ticketBalance); - arenaInformation.BuyTicket(ArenaHelper.GetMaxPurchasedTicketCount(roundData)); - } - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest(avatarState, myArenaAvatarState); - ExtraEnemyArenaPlayerDigest = - new ArenaPlayerDigest(enemyAvatarState, enemyArenaAvatarState); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheetsV1(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(context.Random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleArena6.cs b/Lib9c/Action/BattleArena6.cs deleted file mode 100644 index 3e60ce4f4b..0000000000 --- a/Lib9c/Action/BattleArena6.cs +++ /dev/null @@ -1,414 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1464 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena6")] - public class BattleArena6 : GameAction, IBattleArenaV1 - { - public const string PurchasedCountKey = "purchased_count_during_interval"; - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - if (championshipId > 2) - { - throw new ActionObsoletedException(); - } - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheetsV1( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena6)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena6)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena6)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena6)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena6)}] my avatar address : {myAvatarAddress}"); - } - - var gameConfigState = states.GetGameConfigState(); - var battleArenaInterval = gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena6)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena6)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena6)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena6)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena6)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV2( - ArenaHelper.ScoreLimitsV2, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena6)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var dailyArenaInterval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, dailyArenaInterval); - var purchasedCountAddr = arenaInformation.Address.Derive(PurchasedCountKey); - if (!states.TryGetState(purchasedCountAddr, out Integer purchasedCountDuringInterval)) - { - purchasedCountDuringInterval = 0; - } - - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - purchasedCountDuringInterval = 0; - states = states.SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena6)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - for (var i = 0; i < ticket; i++) - { - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - arenaInformation.BuyTicket(roundData.MaxPurchaseCount); - if (purchasedCountDuringInterval >= roundData.MaxPurchaseCountWithInterval) - { - throw new ExceedTicketPurchaseLimitDuringIntervalException( - $"[{nameof(ArenaInformation)}] PurchasedTicketCount({purchasedCountDuringInterval}) >= MAX({{max}})"); - } - - states = states - .TransferAsset(context, context.Signer, arenaAdr, ticketBalance) - .SetState(purchasedCountAddr, ++purchasedCountDuringInterval); - } - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest(avatarState, myArenaAvatarState); - ExtraEnemyArenaPlayerDigest = - new ArenaPlayerDigest(enemyAvatarState, enemyArenaAvatarState); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheetsV1(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(context.Random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleArena7.cs b/Lib9c/Action/BattleArena7.cs deleted file mode 100644 index 00340eacaf..0000000000 --- a/Lib9c/Action/BattleArena7.cs +++ /dev/null @@ -1,469 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1495 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena7")] - public class BattleArena7 : GameAction, IBattleArenaV1 - { - public const string PurchasedCountKey = "purchased_count_during_interval"; - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - public List runeInfos; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - IEnumerable IBattleArenaV1.RuneSlotInfos => runeInfos - .Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - [RuneInfos] = runeInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - runeInfos = plainValue[RuneInfos].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena7 exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlotV2(runeInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Arena); - itemSlotState.UpdateEquipment(equipments); - itemSlotState.UpdateCostumes(costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena7)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena7)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena7)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena7)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena7)}] my avatar address : {myAvatarAddress}"); - } - - var gameConfigState = states.GetGameConfigState(); - var battleArenaInterval = gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena7)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena7)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena7)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena7)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena7)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifferenceV2( - ArenaHelper.ScoreLimitsV2, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena7)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var dailyArenaInterval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, dailyArenaInterval); - var purchasedCountAddr = arenaInformation.Address.Derive(PurchasedCountKey); - if (!states.TryGetState(purchasedCountAddr, out Integer purchasedCountDuringInterval)) - { - purchasedCountDuringInterval = 0; - } - - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - purchasedCountDuringInterval = 0; - states = states.SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena7)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - for (var i = 0; i < ticket; i++) - { - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - arenaInformation.BuyTicket(roundData.MaxPurchaseCount); - if (purchasedCountDuringInterval >= roundData.MaxPurchaseCountWithInterval) - { - throw new ExceedTicketPurchaseLimitDuringIntervalException( - $"[{nameof(ArenaInformation)}] PurchasedTicketCount({purchasedCountDuringInterval}) >= MAX({{max}})"); - } - - states = states - .TransferAsset(context, context.Signer, arenaAdr, ticketBalance) - .SetState(purchasedCountAddr, ++purchasedCountDuringInterval); - } - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - var runeStates = new List(); - foreach (var address in runeInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // get enemy equipped items - var enemyItemSlotStateAddress = ItemSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyItemSlotState = states.TryGetState(enemyItemSlotStateAddress, out List rawEnemyItemSlotState) - ? new ItemSlotState(rawEnemyItemSlotState) - : new ItemSlotState(BattleType.Arena); - var enemyRuneSlotStateAddress = RuneSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyRuneSlotState = states.TryGetState(enemyRuneSlotStateAddress, out List enemyRawRuneSlotState) - ? new RuneSlotState(enemyRawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - - var enemyRuneStates = new List(); - var enemyRuneSlotInfos = enemyRuneSlotState.GetEquippedRuneSlotInfos(); - foreach (var address in enemyRuneSlotInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - enemyRuneStates.Add(new RuneState(rawRuneState)); - } - } - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest( - avatarState, - equipments, - costumes, - runeStates); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest( - enemyAvatarState, - enemyItemSlotState.Equipments, - enemyItemSlotState.Costumes, - enemyRuneStates); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV2(context.Random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena7 Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleArena8.cs b/Lib9c/Action/BattleArena8.cs deleted file mode 100644 index e3f81bc6fb..0000000000 --- a/Lib9c/Action/BattleArena8.cs +++ /dev/null @@ -1,475 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// Hard forked at https://github.com/planetarium/lib9c/pull/1649 - /// Updated at https://github.com/planetarium/lib9c/pull/1679 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("battle_arena8")] - public class BattleArena8 : GameAction, IBattleArenaV1 - { - public const string PurchasedCountKey = "purchased_count_during_interval"; - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - public List runeInfos; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - IEnumerable IBattleArenaV1.RuneSlotInfos => runeInfos - .Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - [RuneInfos] = runeInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - runeInfos = plainValue[RuneInfos].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(runeInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Arena); - itemSlotState.UpdateEquipment(equipments); - itemSlotState.UpdateCostumes(costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena8)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena8)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena8)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena8)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena8)}] my avatar address : {myAvatarAddress}"); - } - - var gameConfigState = states.GetGameConfigState(); - var battleArenaInterval = roundData.ArenaType == ArenaType.OffSeason - ? 0 - : gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena8)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena8)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena8)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena8)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena8)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifference( - ArenaHelper.ScoreLimitsV3, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena8)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var dailyArenaInterval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, dailyArenaInterval); - var purchasedCountAddr = arenaInformation.Address.Derive(PurchasedCountKey); - if (!states.TryGetState(purchasedCountAddr, out Integer purchasedCountDuringInterval)) - { - purchasedCountDuringInterval = 0; - } - - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - purchasedCountDuringInterval = 0; - states = states.SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena8)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else if (ticket > 1) - { - throw new TicketPurchaseLimitExceedException( - $"[{nameof(ArenaInformation)}] tickets to buy : {ticket}"); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - arenaInformation.BuyTicket(roundData.MaxPurchaseCount); - if (purchasedCountDuringInterval >= roundData.MaxPurchaseCountWithInterval) - { - throw new ExceedTicketPurchaseLimitDuringIntervalException( - $"[{nameof(ArenaInformation)}] PurchasedTicketCount({purchasedCountDuringInterval}) >= MAX({{max}})"); - } - - purchasedCountDuringInterval++; - states = states - .TransferAsset(context, context.Signer, arenaAdr, ticketBalance) - .SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - var runeStates = new List(); - foreach (var address in runeInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // get enemy equipped items - var enemyItemSlotStateAddress = ItemSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyItemSlotState = states.TryGetState(enemyItemSlotStateAddress, out List rawEnemyItemSlotState) - ? new ItemSlotState(rawEnemyItemSlotState) - : new ItemSlotState(BattleType.Arena); - var enemyRuneSlotStateAddress = RuneSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyRuneSlotState = states.TryGetState(enemyRuneSlotStateAddress, out List enemyRawRuneSlotState) - ? new RuneSlotState(enemyRawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - - var enemyRuneStates = new List(); - var enemyRuneSlotInfos = enemyRuneSlotState.GetEquippedRuneSlotInfos(); - foreach (var address in enemyRuneSlotInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - enemyRuneStates.Add(new RuneState(rawRuneState)); - } - } - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest( - avatarState, - equipments, - costumes, - runeStates); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest( - enemyAvatarState, - enemyItemSlotState.Equipments, - enemyItemSlotState.Costumes, - enemyRuneStates); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV3(context.Random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScoresV1(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleArena9.cs b/Lib9c/Action/BattleArena9.cs deleted file mode 100644 index 3c3138d854..0000000000 --- a/Lib9c/Action/BattleArena9.cs +++ /dev/null @@ -1,473 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Arena; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// Hard forked at https://github.com/planetarium/lib9c/pull/1649 - /// Updated at https://github.com/planetarium/lib9c/pull/1679 - /// - [Serializable] - [ActionType("battle_arena9")] - public class BattleArena9 : GameAction, IBattleArenaV1 - { - public const string PurchasedCountKey = "purchased_count_during_interval"; - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int championshipId; - public int round; - public int ticket; - - public List costumes; - public List equipments; - public List runeInfos; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - public int ExtraPreviousMyScore; - - Address IBattleArenaV1.MyAvatarAddress => myAvatarAddress; - - Address IBattleArenaV1.EnemyAvatarAddress => enemyAvatarAddress; - - int IBattleArenaV1.ChampionshipId => championshipId; - - int IBattleArenaV1.Round => round; - - int IBattleArenaV1.Ticket => ticket; - - IEnumerable IBattleArenaV1.Costumes => costumes; - - IEnumerable IBattleArenaV1.Equipments => equipments; - - IEnumerable IBattleArenaV1.RuneSlotInfos => runeInfos - .Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [ChampionshipIdKey] = championshipId.Serialize(), - [RoundKey] = round.Serialize(), - [TicketKey] = ticket.Serialize(), - [CostumesKey] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - [EquipmentsKey] = new List(equipments - .OrderBy(element => element).Select(e => e.Serialize())), - [RuneInfos] = runeInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - myAvatarAddress = plainValue[MyAvatarAddressKey].ToAddress(); - enemyAvatarAddress = plainValue[EnemyAvatarAddressKey].ToAddress(); - championshipId = plainValue[ChampionshipIdKey].ToInteger(); - round = plainValue[RoundKey].ToInteger(); - ticket = plainValue[TicketKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - runeInfos = plainValue[RuneInfos].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena exec started", addressesHex); - if (myAvatarAddress.Equals(enemyAvatarAddress)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - myAvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - { - throw new NotEnoughClearedStageLevelException( - $"{addressesHex}Aborted as NotEnoughClearedStageLevelException"); - } - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(runeInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(myAvatarAddress, BattleType.Arena); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Arena); - itemSlotState.UpdateEquipment(equipments); - itemSlotState.UpdateCostumes(costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var arenaSheet = sheets.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var arenaRow)) - { - throw new SheetRowNotFoundException(nameof(ArenaSheet), - $"championship Id : {championshipId}"); - } - - if (!arenaRow.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena9)} : block index({context.BlockIndex}) - " + - $"championshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaParticipantsAdr = - ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - if (!states.TryGetArenaParticipants(arenaParticipantsAdr, out var arenaParticipants)) - { - throw new ArenaParticipantsNotFoundException( - $"[{nameof(BattleArena9)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena9)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena9)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena9)}] my avatar address : {myAvatarAddress}"); - } - - var gameConfigState = states.GetGameConfigState(); - var battleArenaInterval = roundData.ArenaType == ArenaType.OffSeason - ? 0 - : gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena9)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena9)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena9)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var enemyArenaScoreAdr = ArenaScore.DeriveAddress( - enemyAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(enemyArenaScoreAdr, out var enemyArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena9)}] enemy avatar address : {enemyAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - var arenaInformationAdr = ArenaInformation.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaInformation(arenaInformationAdr, out var arenaInformation)) - { - throw new ArenaInformationNotFoundException( - $"[{nameof(BattleArena9)}] my avatar address : {myAvatarAddress}" + - $" - ChampionshipId({roundData.ChampionshipId}) - round({roundData.Round})"); - } - - if (!ArenaHelper.ValidateScoreDifference( - ArenaHelper.ScoreLimits, - roundData.ArenaType, - myArenaScore.Score, - enemyArenaScore.Score)) - { - var scoreDiff = enemyArenaScore.Score - myArenaScore.Score; - throw new ValidateScoreDifferenceException( - $"[{nameof(BattleArena9)}] Arena Type({roundData.ArenaType}) : " + - $"enemyScore({enemyArenaScore.Score}) - myScore({myArenaScore.Score}) = " + - $"diff({scoreDiff})"); - } - - var dailyArenaInterval = gameConfigState.DailyArenaInterval; - var currentTicketResetCount = ArenaHelper.GetCurrentTicketResetCount( - context.BlockIndex, roundData.StartBlockIndex, dailyArenaInterval); - var purchasedCountAddr = arenaInformation.Address.Derive(PurchasedCountKey); - if (!states.TryGetState(purchasedCountAddr, out Integer purchasedCountDuringInterval)) - { - purchasedCountDuringInterval = 0; - } - - if (arenaInformation.TicketResetCount < currentTicketResetCount) - { - arenaInformation.ResetTicket(currentTicketResetCount); - purchasedCountDuringInterval = 0; - states = states.SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - if (roundData.ArenaType != ArenaType.OffSeason && ticket > 1) - { - throw new ExceedPlayCountException($"[{nameof(BattleArena9)}] " + - $"ticket : {ticket} / arenaType : " + - $"{roundData.ArenaType}"); - } - - if (arenaInformation.Ticket > 0) - { - arenaInformation.UseTicket(ticket); - } - else if (ticket > 1) - { - throw new TicketPurchaseLimitExceedException( - $"[{nameof(ArenaInformation)}] tickets to buy : {ticket}"); - } - else - { - var arenaAdr = - ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - var goldCurrency = states.GetGoldCurrency(); - var ticketBalance = - ArenaHelper.GetTicketPrice(roundData, arenaInformation, goldCurrency); - arenaInformation.BuyTicket(roundData.MaxPurchaseCount); - if (purchasedCountDuringInterval >= roundData.MaxPurchaseCountWithInterval) - { - throw new ExceedTicketPurchaseLimitDuringIntervalException( - $"[{nameof(ArenaInformation)}] PurchasedTicketCount({purchasedCountDuringInterval}) >= MAX({{max}})"); - } - - purchasedCountDuringInterval++; - states = states - .TransferAsset(context, context.Signer, arenaAdr, ticketBalance) - .SetState(purchasedCountAddr, purchasedCountDuringInterval); - } - - // update arena avatar state - myArenaAvatarState.UpdateEquipment(equipments); - myArenaAvatarState.UpdateCostumes(costumes); - myArenaAvatarState.LastBattleBlockIndex = context.BlockIndex; - var runeStates = new List(); - foreach (var address in runeInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // get enemy equipped items - var enemyItemSlotStateAddress = ItemSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyItemSlotState = states.TryGetState(enemyItemSlotStateAddress, out List rawEnemyItemSlotState) - ? new ItemSlotState(rawEnemyItemSlotState) - : new ItemSlotState(BattleType.Arena); - var enemyRuneSlotStateAddress = RuneSlotState.DeriveAddress(enemyAvatarAddress, BattleType.Arena); - var enemyRuneSlotState = states.TryGetState(enemyRuneSlotStateAddress, out List enemyRawRuneSlotState) - ? new RuneSlotState(enemyRawRuneSlotState) - : new RuneSlotState(BattleType.Arena); - - var enemyRuneStates = new List(); - var enemyRuneSlotInfos = enemyRuneSlotState.GetEquippedRuneSlotInfos(); - foreach (var address in enemyRuneSlotInfos.Select(info => RuneState.DeriveAddress(myAvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - enemyRuneStates.Add(new RuneState(rawRuneState)); - } - } - - // simulate - var enemyAvatarState = states.GetEnemyAvatarState(enemyAvatarAddress); - ExtraMyArenaPlayerDigest = new ArenaPlayerDigest( - avatarState, - equipments, - costumes, - runeStates); - ExtraEnemyArenaPlayerDigest = new ArenaPlayerDigest( - enemyAvatarState, - enemyItemSlotState.Equipments, - enemyItemSlotState.Costumes, - enemyRuneStates); - ExtraPreviousMyScore = myArenaScore.Score; - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var winCount = 0; - var defeatCount = 0; - var rewards = new List(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV3(context.Random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - context.Random, - sheets.GetSheet(), - sheets.GetSheet(), - ExtraMyArenaPlayerDigest.Level, - maxCount: ArenaHelper.GetRewardCount(ExtraPreviousMyScore)); - rewards.AddRange(reward); - } - - // add reward - foreach (var itemBase in rewards.OrderBy(x => x.Id)) - { - avatarState.inventory.AddItem(itemBase); - } - - // add medal - if (roundData.ArenaType != ArenaType.OffSeason && - winCount > 0) - { - var materialSheet = sheets.GetSheet(); - var medal = ArenaHelper.GetMedal( - roundData.ChampionshipId, - roundData.Round, - materialSheet); - avatarState.inventory.AddItem(medal, count: winCount); - } - - // update record - var (myWinScore, myDefeatScore, enemyWinScore) = - ArenaHelper.GetScores(ExtraPreviousMyScore, enemyArenaScore.Score); - var myScore = (myWinScore * winCount) + (myDefeatScore * defeatCount); - myArenaScore.AddScore(myScore); - enemyArenaScore.AddScore(enemyWinScore * winCount); - arenaInformation.UpdateRecord(winCount, defeatCount); - - if (migrationRequired) - { - states = states - .SetState(myAvatarAddress, avatarState.SerializeV2()) - .SetState( - myAvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(myArenaScoreAdr, myArenaScore.Serialize()) - .SetState(enemyArenaScoreAdr, enemyArenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState( - myAvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - } -} diff --git a/Lib9c/Action/Buy0.cs b/Lib9c/Action/Buy0.cs deleted file mode 100644 index 24ff811c61..0000000000 --- a/Lib9c/Action/Buy0.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy")] - public class Buy0 : GameAction, IBuy0, IBuyV1 - { - public Address buyerAvatarAddress { get; set; } - public Address sellerAgentAddress { get; set; } - public Address sellerAvatarAddress { get; set; } - public Guid productId { get; set; } - public Buy7.BuyerResult buyerResult; - public Buy7.SellerResult sellerResult; - - Address IBuyV1.BuyerAvatarAddress => buyerAvatarAddress; - Address IBuyV1.SellerAgentAddress => sellerAgentAddress; - Address IBuyV1.SellerAvatarAddress => sellerAvatarAddress; - Guid IBuyV1.ProductId => productId; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["buyerAvatarAddress"] = buyerAvatarAddress.Serialize(), - ["sellerAgentAddress"] = sellerAgentAddress.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["productId"] = productId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue["buyerAvatarAddress"].ToAddress(); - sellerAgentAddress = plainValue["sellerAgentAddress"].ToAddress(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - productId = plainValue["productId"].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - sellerAgentAddress, - GoldCurrencyState.Address); - return states.SetState(ShopState.Address, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress, sellerAvatarAddress); - - if (ctx.Signer.Equals(sellerAgentAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer is the seller."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{Addresses}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); - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - 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); - // 상점에서 구매할 아이템을 찾는다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) was failed to get from the shop." - ); - } - - ShopItem shopItem = new ShopItem((Dictionary)products[productIdSerialized]); - if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) of seller ({shopItem.SellerAgentAddress}) is different from ({sellerAgentAddress})." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the seller agent/avatar was failed to load from {sellerAgentAddress}/{sellerAvatarAddress}." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 돈은 있냐? - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < shopItem.Price) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the buyer ({ctx.Signer}) has no sufficient gold: {buyerBalance} < {shopItem.Price}", - ctx.Signer, - buyerBalance - ); - } - - var tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - var taxedPrice = shopItem.Price - tax; - - // 세금을 송금한다. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // 구매자의 돈을 판매자에게 송금한다. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - // 구매자, 판매자에게 결과 메일 전송 - buyerResult = new Buy7.BuyerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable - }; - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - buyerResult.id = buyerMail.id; - - sellerResult = new Buy7.SellerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - gold = taxedPrice - }; - var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - ctx.BlockIndex); - sellerResult.id = sellerMail.id; - - buyerAvatarState.Update2(buyerMail); - buyerAvatarState.UpdateFromAddItem2(buyerResult.itemUsable, false); - sellerAvatarState.Update2(sellerMail); - - // 퀘스트 업데이트 - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price); - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - var materialSheet = states.GetSheet(); - buyerAvatarState.UpdateQuestRewards2(materialSheet); - sellerAvatarState.UpdateQuestRewards2(materialSheet); - - //Avoid InvalidBlockStateRootHashException to 50000 index. - if (sellerAvatarState.questList.Any(q => q.Complete && !q.IsPaidInAction)) - { - var prevIds = sellerAvatarState.questList.completedQuestIds; - sellerAvatarState.UpdateQuestRewards(materialSheet); - sellerAvatarState.questList.completedQuestIds = prevIds; - } - if (context.BlockIndex != 4742 && buyerAvatarState.questList.Any(q => q.Complete && !q.IsPaidInAction)) - { - var prevIds = buyerAvatarState.questList.completedQuestIds; - buyerAvatarState.UpdateQuestRewards(materialSheet); - buyerAvatarState.questList.completedQuestIds = prevIds; - } - - states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Buy Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Buy10.cs b/Lib9c/Action/Buy10.cs deleted file mode 100644 index 6fa144f6f2..0000000000 --- a/Lib9c/Action/Buy10.cs +++ /dev/null @@ -1,336 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy10")] - public class Buy10 : GameAction, IBuy5, IBuyV2 - { - public const int TaxRate = 8; - public const int ErrorCodeFailedLoadingState = 1; - public const int ErrorCodeItemDoesNotExist = 2; - public const int ErrorCodeShopItemExpired = 3; - public const int ErrorCodeInsufficientBalance = 4; - public const int ErrorCodeInvalidAddress = 5; - public const int ErrorCodeInvalidPrice = 6; - public const int ErrorCodeInvalidOrderId = 7; - public const int ErrorCodeInvalidTradableId = 8; - public const int ErrorCodeInvalidItemType = 9; - public const int ErrorCodeDuplicateSell = 10; - - public Address buyerAvatarAddress { get; set; } - public List<(Guid orderId, int errorCode)> errors = new List<(Guid orderId, int errorCode)>(); - public IEnumerable purchaseInfos; - IEnumerable IBuy5.purchaseInfos => purchaseInfos; - - Address IBuyV2.BuyerAvatarAddress => buyerAvatarAddress; - IEnumerable IBuyV2.PurchaseInfos => purchaseInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [BuyerAvatarAddressKey] = buyerAvatarAddress.Serialize(), - [PurchaseInfosKey] = purchaseInfos - .OrderBy(p => p.OrderId) - .Select(p => p.Serialize()) - .Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue[BuyerAvatarAddressKey].ToAddress(); - purchaseInfos = plainValue[PurchaseInfosKey].ToList(value => new PurchaseInfo((Dictionary)value)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var buyerInventoryAddress = buyerAvatarAddress.Derive(LegacyInventoryKey); - var buyerWorldInformationAddress = buyerAvatarAddress.Derive(LegacyWorldInformationKey); - var buyerQuestListAddress = buyerAvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - foreach (var purchaseInfo in purchaseInfos) - { - var sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - var sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address orderReceiptAddress = OrderReceipt.DeriveAddress(purchaseInfo.OrderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - states = states - .SetState(shardedShopAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .SetState(sellerInventoryAddress, MarkChanged) - .SetState(sellerWorldInformationAddress, MarkChanged) - .SetState(sellerQuestListAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(digestListAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - purchaseInfo.SellerAgentAddress, - GoldCurrencyState.Address); - } - - return states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(buyerInventoryAddress, MarkChanged) - .SetState(buyerWorldInformationAddress, MarkChanged) - .SetState(buyerQuestListAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100220ObsoleteIndex, 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.TryGetAvatarStateV2(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState, out _)) - { - 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); - } - - MaterialItemSheet materialSheet = states.GetSheet(); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address sellerAgentAddress = purchaseInfo.SellerAgentAddress; - Address sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - Address sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - Address sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Guid orderId = purchaseInfo.OrderId; - Address orderAddress = Order.DeriveAddress(orderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - - if (purchaseInfo.SellerAgentAddress == ctx.Signer) - { - errors.Add((orderId, ErrorCodeInvalidAddress)); - continue; - } - - if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - if (!states.TryGetState(orderAddress, out Dictionary rawOrder)) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - Order order = OrderFactory.Deserialize(rawOrder); - - var shardedShopState = new ShardedShopStateV2(shopStateDict); - - try - { - shardedShopState.Remove(order, context.BlockIndex); - } - catch (OrderIdDoesNotExistException) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - 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); - - - if (!states.TryGetAvatarStateV2(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState, out _)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetState(digestListAddress, out Dictionary rawDigestList)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - var digestList = new OrderDigestListState(rawDigestList); - - // migration method - sellerAvatarState.inventory.UnlockInvalidSlot(digestList, sellerAgentAddress, sellerAvatarAddress); - sellerAvatarState.inventory.ReconfigureFungibleItem(digestList, order.TradableId); - sellerAvatarState.inventory.LockByReferringToDigestList(digestList, order.TradableId, context.BlockIndex); - // - - digestList.Remove(orderId); - - var errorCode = order.ValidateTransfer(sellerAvatarState, purchaseInfo.TradableId, purchaseInfo.Price, context.BlockIndex); - if (errorCode != 0) - { - errors.Add((orderId, errorCode)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Check Balance. - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < order.Price) - { - errors.Add((orderId, ErrorCodeInsufficientBalance)); - continue; - } - - OrderReceipt orderReceipt; - try - { - orderReceipt = order.Transfer(sellerAvatarState, buyerAvatarState, context.BlockIndex); - } - catch (ItemDoesNotExistException) - { - errors.Add((orderId, ErrorCodeItemDoesNotExist)); - continue; - } - - Address orderReceiptAddress = OrderReceipt.DeriveAddress(orderId); - if (!(states.GetState(orderReceiptAddress) is null)) - { - errors.Add((orderId, ErrorCodeDuplicateSell)); - continue; - } - - var expirationMail = sellerAvatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - sellerAvatarState.mailBox.Remove(expirationMail); - } - - var orderSellerMail = new OrderSellerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - var orderBuyerMail = new OrderBuyerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - - buyerAvatarState.Update(orderBuyerMail); - sellerAvatarState.Update(orderSellerMail); - - // // Update quest. - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, order.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, order.Price); - - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - buyerAvatarState.UpdateQuestRewards(materialSheet); - sellerAvatarState.UpdateQuestRewards(materialSheet); - - FungibleAssetValue tax = order.GetTax(); - var taxedPrice = order.Price - tax; - - // Transfer tax. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // Transfer seller. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - states = states - .SetState(digestListAddress, digestList.Serialize()) - .SetState(orderReceiptAddress, orderReceipt.Serialize()) - .SetState(sellerInventoryAddress, sellerAvatarState.inventory.Serialize()) - .SetState(sellerWorldInformationAddress, sellerAvatarState.worldInformation.Serialize()) - .SetState(sellerQuestListAddress, sellerAvatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, sellerAvatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(shardedShopAddress, shardedShopState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - - states = states - .SetState(buyerInventoryAddress, buyerAvatarState.inventory.Serialize()) - .SetState(buyerWorldInformationAddress, buyerAvatarState.worldInformation.Serialize()) - .SetState(buyerQuestListAddress, buyerAvatarState.questList.Serialize()) - .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; - } - } -} diff --git a/Lib9c/Action/Buy11.cs b/Lib9c/Action/Buy11.cs deleted file mode 100644 index 4d40f07a68..0000000000 --- a/Lib9c/Action/Buy11.cs +++ /dev/null @@ -1,354 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Updated at https://github.com/planetarium/lib9c/pull/1176 - /// - [Serializable] - [ActionType("buy11")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class Buy11 : GameAction, IBuy5, IBuyV2 - { - public static Address GetFeeStoreAddress() => Addresses.Shop.Derive("_0_0"); - - public const int TaxRate = 8; - public const int ErrorCodeFailedLoadingState = 1; - public const int ErrorCodeItemDoesNotExist = 2; - public const int ErrorCodeShopItemExpired = 3; - public const int ErrorCodeInsufficientBalance = 4; - public const int ErrorCodeInvalidAddress = 5; - public const int ErrorCodeInvalidPrice = 6; - public const int ErrorCodeInvalidOrderId = 7; - public const int ErrorCodeInvalidTradableId = 8; - public const int ErrorCodeInvalidItemType = 9; - public const int ErrorCodeDuplicateSell = 10; - - public Address buyerAvatarAddress { get; set; } - public List<(Guid orderId, int errorCode)> errors = new List<(Guid orderId, int errorCode)>(); - public IEnumerable purchaseInfos; - IEnumerable IBuy5.purchaseInfos => purchaseInfos; - - Address IBuyV2.BuyerAvatarAddress => buyerAvatarAddress; - IEnumerable IBuyV2.PurchaseInfos => purchaseInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [BuyerAvatarAddressKey] = buyerAvatarAddress.Serialize(), - [PurchaseInfosKey] = purchaseInfos - .OrderBy(p => p.OrderId) - .Select(p => p.Serialize()) - .Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue[BuyerAvatarAddressKey].ToAddress(); - purchaseInfos = plainValue[PurchaseInfosKey].ToList(value => new PurchaseInfo((Dictionary)value)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var buyerInventoryAddress = buyerAvatarAddress.Derive(LegacyInventoryKey); - var buyerWorldInformationAddress = buyerAvatarAddress.Derive(LegacyWorldInformationKey); - var buyerQuestListAddress = buyerAvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - foreach (var purchaseInfo in purchaseInfos) - { - var sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - var sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address orderReceiptAddress = OrderReceipt.DeriveAddress(purchaseInfo.OrderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - states = states - .SetState(shardedShopAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .SetState(sellerInventoryAddress, MarkChanged) - .SetState(sellerWorldInformationAddress, MarkChanged) - .SetState(sellerQuestListAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(digestListAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - purchaseInfo.SellerAgentAddress, - GetFeeStoreAddress()); - } - - return states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(buyerInventoryAddress, MarkChanged) - .SetState(buyerWorldInformationAddress, MarkChanged) - .SetState(buyerQuestListAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var arenaSheetAddress = Addresses.GetSheetAddress(); - var arenaSheetState = states.GetState(arenaSheetAddress); - if (arenaSheetState != null) - { - // exception handling for v100240. - if (context.BlockIndex > 4374175) - { - } - else - { - throw new ActionObsoletedException(nameof(Buy11)); - } - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Buy exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState, out _)) - { - 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); - } - - MaterialItemSheet materialSheet = states.GetSheet(); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address sellerAgentAddress = purchaseInfo.SellerAgentAddress; - Address sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - Address sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - Address sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Guid orderId = purchaseInfo.OrderId; - Address orderAddress = Order.DeriveAddress(orderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - - if (purchaseInfo.SellerAgentAddress == ctx.Signer) - { - errors.Add((orderId, ErrorCodeInvalidAddress)); - continue; - } - - if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - if (!states.TryGetState(orderAddress, out Dictionary rawOrder)) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - Order order = OrderFactory.Deserialize(rawOrder); - - var shardedShopState = new ShardedShopStateV2(shopStateDict); - - try - { - shardedShopState.Remove(order, context.BlockIndex); - } - catch (OrderIdDoesNotExistException) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - 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); - - - if (!states.TryGetAvatarStateV2(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState, out _)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetState(digestListAddress, out Dictionary rawDigestList)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - var digestList = new OrderDigestListState(rawDigestList); - - // migration method - sellerAvatarState.inventory.UnlockInvalidSlot(digestList, sellerAgentAddress, sellerAvatarAddress); - sellerAvatarState.inventory.ReconfigureFungibleItem(digestList, order.TradableId); - sellerAvatarState.inventory.LockByReferringToDigestList(digestList, order.TradableId, context.BlockIndex); - // - - digestList.Remove(orderId); - - var errorCode = order.ValidateTransfer(sellerAvatarState, purchaseInfo.TradableId, purchaseInfo.Price, context.BlockIndex); - if (errorCode != 0) - { - errors.Add((orderId, errorCode)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Check Balance. - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < order.Price) - { - errors.Add((orderId, ErrorCodeInsufficientBalance)); - continue; - } - - OrderReceipt orderReceipt; - try - { - orderReceipt = order.Transfer(sellerAvatarState, buyerAvatarState, context.BlockIndex); - } - catch (ItemDoesNotExistException) - { - errors.Add((orderId, ErrorCodeItemDoesNotExist)); - continue; - } - - Address orderReceiptAddress = OrderReceipt.DeriveAddress(orderId); - if (!(states.GetState(orderReceiptAddress) is null)) - { - errors.Add((orderId, ErrorCodeDuplicateSell)); - continue; - } - - var expirationMail = sellerAvatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - sellerAvatarState.mailBox.Remove(expirationMail); - } - - var orderSellerMail = new OrderSellerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - var orderBuyerMail = new OrderBuyerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - - buyerAvatarState.Update(orderBuyerMail); - sellerAvatarState.Update(orderSellerMail); - - // // Update quest. - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, order.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, order.Price); - - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - buyerAvatarState.UpdateQuestRewards(materialSheet); - sellerAvatarState.UpdateQuestRewards(materialSheet); - - FungibleAssetValue tax = order.GetTax(); - var taxedPrice = order.Price - tax; - - // Transfer tax. - states = states.TransferAsset( - context, - context.Signer, - GetFeeStoreAddress(), - tax); - - // Transfer seller. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - states = states - .SetState(digestListAddress, digestList.Serialize()) - .SetState(orderReceiptAddress, orderReceipt.Serialize()) - .SetState(sellerInventoryAddress, sellerAvatarState.inventory.Serialize()) - .SetState(sellerWorldInformationAddress, sellerAvatarState.worldInformation.Serialize()) - .SetState(sellerQuestListAddress, sellerAvatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, sellerAvatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(shardedShopAddress, shardedShopState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - - states = states - .SetState(buyerInventoryAddress, buyerAvatarState.inventory.Serialize()) - .SetState(buyerWorldInformationAddress, buyerAvatarState.worldInformation.Serialize()) - .SetState(buyerQuestListAddress, buyerAvatarState.questList.Serialize()) - .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; - } - } -} diff --git a/Lib9c/Action/Buy2.cs b/Lib9c/Action/Buy2.cs deleted file mode 100644 index 84443e357c..0000000000 --- a/Lib9c/Action/Buy2.cs +++ /dev/null @@ -1,239 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy2")] - public class Buy2 : GameAction, IBuy0, IBuyV1 - { - public const int TaxRate = 8; - - public Address buyerAvatarAddress { get; set; } - public Address sellerAgentAddress { get; set; } - public Address sellerAvatarAddress { get; set; } - public Guid productId { get; set; } - public Buy7.BuyerResult buyerResult; - public Buy7.SellerResult sellerResult; - - Address IBuyV1.BuyerAvatarAddress => buyerAvatarAddress; - Address IBuyV1.SellerAgentAddress => sellerAgentAddress; - Address IBuyV1.SellerAvatarAddress => sellerAvatarAddress; - Guid IBuyV1.ProductId => productId; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["buyerAvatarAddress"] = buyerAvatarAddress.Serialize(), - ["sellerAgentAddress"] = sellerAgentAddress.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["productId"] = productId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue["buyerAvatarAddress"].ToAddress(); - sellerAgentAddress = plainValue["sellerAgentAddress"].ToAddress(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - productId = plainValue["productId"].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - sellerAgentAddress, - GoldCurrencyState.Address); - return states.SetState(ShopState.Address, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress, sellerAvatarAddress); - - if (ctx.Signer.Equals(sellerAgentAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer is the seller."); - } - - 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); - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - 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); - // 상점에서 구매할 아이템을 찾는다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) was failed to get from the shop." - ); - } - - ShopItem shopItem = new ShopItem((Dictionary)products[productIdSerialized]); - if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) of seller ({shopItem.SellerAgentAddress}) is different from ({sellerAgentAddress})." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the seller agent/avatar was failed to load from {sellerAgentAddress}/{sellerAvatarAddress}." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 돈은 있냐? - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < shopItem.Price) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the buyer ({ctx.Signer}) has no sufficient gold: {buyerBalance} < {shopItem.Price}", - ctx.Signer, - buyerBalance - ); - } - - var tax = shopItem.Price.DivRem(100, out _) * TaxRate; - var taxedPrice = shopItem.Price - tax; - - // 세금을 송금한다. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // 구매자의 돈을 판매자에게 송금한다. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - // 구매자, 판매자에게 결과 메일 전송 - buyerResult = new Buy7.BuyerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable - }; - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - buyerResult.id = buyerMail.id; - - sellerResult = new Buy7.SellerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - gold = taxedPrice - }; - var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - ctx.BlockIndex); - sellerResult.id = sellerMail.id; - - buyerAvatarState.Update3(buyerMail); - buyerAvatarState.UpdateFromAddItem2(buyerResult.itemUsable, false); - sellerAvatarState.Update3(sellerMail); - - // 퀘스트 업데이트 - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price); - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - var materialSheet = states.GetSheet(); - 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(buyerAvatarAddress, buyerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Buy Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Buy3.cs b/Lib9c/Action/Buy3.cs deleted file mode 100644 index 4bbda4d299..0000000000 --- a/Lib9c/Action/Buy3.cs +++ /dev/null @@ -1,247 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy3")] - public class Buy3 : GameAction, IBuy0, IBuyV1 - { - public Address buyerAvatarAddress { get; set; } - public Address sellerAgentAddress { get; set; } - public Address sellerAvatarAddress { get; set; } - public Guid productId { get; set; } - public Buy7.BuyerResult buyerResult; - public Buy7.SellerResult sellerResult; - - Address IBuyV1.BuyerAvatarAddress => buyerAvatarAddress; - Address IBuyV1.SellerAgentAddress => sellerAgentAddress; - Address IBuyV1.SellerAvatarAddress => sellerAvatarAddress; - Guid IBuyV1.ProductId => productId; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["buyerAvatarAddress"] = buyerAvatarAddress.Serialize(), - ["sellerAgentAddress"] = sellerAgentAddress.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["productId"] = productId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue["buyerAvatarAddress"].ToAddress(); - sellerAgentAddress = plainValue["sellerAgentAddress"].ToAddress(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - productId = plainValue["productId"].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - sellerAgentAddress, - GoldCurrencyState.Address); - return states.SetState(ShopState.Address, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress, sellerAvatarAddress); - - if (ctx.Signer.Equals(sellerAgentAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer is the seller."); - } - - 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); - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - 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); - // 상점에서 구매할 아이템을 찾는다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) was failed to get from the shop." - ); - } - - ShopItem shopItem = new ShopItem((Dictionary)products[productIdSerialized]); - if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) of seller ({shopItem.SellerAgentAddress}) is different from ({sellerAgentAddress})." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the seller agent/avatar was failed to load from {sellerAgentAddress}/{sellerAvatarAddress}." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 돈은 있냐? - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < shopItem.Price) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the buyer ({ctx.Signer}) has no sufficient gold: {buyerBalance} < {shopItem.Price}", - ctx.Signer, - buyerBalance - ); - } - - var tax = shopItem.Price.DivRem(100, out _) * Buy7.TaxRate; - var taxedPrice = shopItem.Price - tax; - - // 세금을 송금한다. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // 구매자의 돈을 판매자에게 송금한다. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - // 구매자, 판매자에게 결과 메일 전송 - buyerResult = new Buy7.BuyerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume - }; - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - buyerResult.id = buyerMail.id; - - 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; - - buyerAvatarState.Update2(buyerMail); - if (buyerResult.itemUsable != null) - { - buyerAvatarState.UpdateFromAddItem2(buyerResult.itemUsable, false); - } - - if (buyerResult.costume != null) - { - buyerAvatarState.UpdateFromAddCostume(buyerResult.costume, false); - } - sellerAvatarState.Update2(sellerMail); - - // 퀘스트 업데이트 - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price); - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - var materialSheet = states.GetSheet(); - 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(buyerAvatarAddress, buyerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Buy Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Buy4.cs b/Lib9c/Action/Buy4.cs deleted file mode 100644 index 87e579d356..0000000000 --- a/Lib9c/Action/Buy4.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy4")] - public class Buy4 : GameAction, IBuy0, IBuyV1 - { - public Address buyerAvatarAddress { get; set; } - public Address sellerAgentAddress { get; set; } - public Address sellerAvatarAddress { get; set; } - public Guid productId { get; set; } - public Buy7.BuyerResult buyerResult; - public Buy7.SellerResult sellerResult; - - Address IBuyV1.BuyerAvatarAddress => buyerAvatarAddress; - Address IBuyV1.SellerAgentAddress => sellerAgentAddress; - Address IBuyV1.SellerAvatarAddress => sellerAvatarAddress; - Guid IBuyV1.ProductId => productId; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["buyerAvatarAddress"] = buyerAvatarAddress.Serialize(), - ["sellerAgentAddress"] = sellerAgentAddress.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["productId"] = productId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue["buyerAvatarAddress"].ToAddress(); - sellerAgentAddress = plainValue["sellerAgentAddress"].ToAddress(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - productId = plainValue["productId"].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - sellerAgentAddress, - GoldCurrencyState.Address); - return states.SetState(ShopState.Address, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, buyerAvatarAddress, sellerAvatarAddress); - - if (ctx.Signer.Equals(sellerAgentAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer is the seller."); - } - - 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); - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - 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); - // 상점에서 구매할 아이템을 찾는다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) was failed to get from the shop." - ); - } - - ShopItem shopItem = new ShopItem((Dictionary)products[productIdSerialized]); - if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) of seller ({shopItem.SellerAgentAddress}) is different from ({sellerAgentAddress})." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the seller agent/avatar was failed to load from {sellerAgentAddress}/{sellerAvatarAddress}." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 돈은 있냐? - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < shopItem.Price) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the buyer ({ctx.Signer}) has no sufficient gold: {buyerBalance} < {shopItem.Price}", - ctx.Signer, - buyerBalance - ); - } - - var tax = shopItem.Price.DivRem(100, out _) * Buy7.TaxRate; - var taxedPrice = shopItem.Price - tax; - - // 세금을 송금한다. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // 구매자의 돈을 판매자에게 송금한다. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - // 구매자, 판매자에게 결과 메일 전송 - buyerResult = new Buy7.BuyerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume - }; - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - buyerResult.id = buyerMail.id; - - 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; - - buyerAvatarState.Update(buyerMail); - if (buyerResult.itemUsable != null) - { - buyerAvatarState.UpdateFromAddItem2(buyerResult.itemUsable, false); - } - - if (buyerResult.costume != null) - { - buyerAvatarState.UpdateFromAddCostume(buyerResult.costume, false); - } - sellerAvatarState.Update(sellerMail); - - // 퀘스트 업데이트 - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price); - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - var materialSheet = states.GetSheet(); - 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(buyerAvatarAddress, buyerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Buy Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Buy6.cs b/Lib9c/Action/Buy6.cs deleted file mode 100644 index 4000559eae..0000000000 --- a/Lib9c/Action/Buy6.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy6")] - public class Buy6 : GameAction, IBuy5, IBuyV2 - { - public const int TaxRate = 8; - public const int ErrorCodeFailedLoadingState = 1; - public const int ErrorCodeItemDoesNotExist = 2; - public const int ErrorCodeShopItemExpired = 3; - public const int ErrorCodeInsufficientBalance = 4; - public const int ErrorCodeInvalidAddress = 5; - - public Address buyerAvatarAddress { get; set; } - public IEnumerable purchaseInfos; - IEnumerable IBuy5.purchaseInfos => purchaseInfos.Cast(); - public Buy7.BuyerMultipleResult buyerMultipleResult; - public Buy7.SellerMultipleResult sellerMultipleResult; - - Address IBuyV2.BuyerAvatarAddress => buyerAvatarAddress; - IEnumerable IBuyV2.PurchaseInfos => purchaseInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [BuyerAvatarAddressKey] = buyerAvatarAddress.Serialize(), - [PurchaseInfosKey] = purchaseInfos - .OrderBy(p => p.productId) - .Select(p => p.Serialize()) - .Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue[BuyerAvatarAddressKey].ToAddress(); - purchaseInfos = plainValue[PurchaseInfosKey].ToList(StateExtensions.ToPurchaseInfo); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - 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( - ctx, - GoldCurrencyMock, - ctx.Signer, - purchaseInfo.sellerAgentAddress, - GoldCurrencyState.Address); - } - return states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(Addresses.Shop, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.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 purchaseResults = new List(); - List sellerResults = new List(); - MaterialItemSheet materialSheet = states.GetSheet(); - 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)); - - // 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)) - { - 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, - context.Signer, - GoldCurrencyState.Address, - tax); - - // Transfer seller. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - products = (List) products.Remove(productSerialized); - shopStateDict = shopStateDict.SetItem(ProductsKey, products); - - ITradableItem tradableItem; - int count = 1; - if (!(shopItem.ItemUsable is null)) - { - tradableItem = shopItem.ItemUsable; - } - else if (!(shopItem.Costume is null)) - { - tradableItem = shopItem.Costume; - } - else - { - tradableItem = shopItem.TradableFungibleItem; - count = shopItem.TradableFungibleItemCount; - } - - if (!sellerAvatarState.inventory.RemoveTradableItemV1(tradableItem, count) && !fromLegacy) - { - purchaseResult.errorCode = ErrorCodeItemDoesNotExist; - continue; - } - - tradableItem.RequiredBlockIndex = context.BlockIndex; - - // Send result mail for buyer, seller. - purchaseResult.shopItem = shopItem; - purchaseResult.itemUsable = shopItem.ItemUsable; - purchaseResult.costume = shopItem.Costume; - purchaseResult.tradableFungibleItem = shopItem.TradableFungibleItem; - purchaseResult.tradableFungibleItemCount = shopItem.TradableFungibleItemCount; - 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, - tradableFungibleItem = shopItem.TradableFungibleItem, - tradableFungibleItemCount = shopItem.TradableFungibleItemCount, - 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); - } - - if (purchaseResult.tradableFungibleItem is TradableMaterial material) - { - buyerAvatarState.UpdateFromAddItem2(material, shopItem.TradableFungibleItemCount, 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; - } - } -} diff --git a/Lib9c/Action/Buy8.cs b/Lib9c/Action/Buy8.cs deleted file mode 100644 index 2eec6b38a6..0000000000 --- a/Lib9c/Action/Buy8.cs +++ /dev/null @@ -1,333 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy8")] - public class Buy8 : GameAction, IBuy5, IBuyV2 - { - public const int TaxRate = 8; - public const int ErrorCodeFailedLoadingState = 1; - public const int ErrorCodeItemDoesNotExist = 2; - public const int ErrorCodeShopItemExpired = 3; - public const int ErrorCodeInsufficientBalance = 4; - public const int ErrorCodeInvalidAddress = 5; - public const int ErrorCodeInvalidPrice = 6; - public const int ErrorCodeInvalidOrderId = 7; - public const int ErrorCodeInvalidTradableId = 8; - public const int ErrorCodeInvalidItemType = 9; - public const int ErrorCodeDuplicateSell = 10; - - public Address buyerAvatarAddress { get; set; } - public List<(Guid orderId, int errorCode)> errors = new List<(Guid orderId, int errorCode)>(); - public IEnumerable purchaseInfos; - IEnumerable IBuy5.purchaseInfos => purchaseInfos.Cast(); - - Address IBuyV2.BuyerAvatarAddress => buyerAvatarAddress; - IEnumerable IBuyV2.PurchaseInfos => purchaseInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [BuyerAvatarAddressKey] = buyerAvatarAddress.Serialize(), - [PurchaseInfosKey] = purchaseInfos - .OrderBy(p => p.OrderId) - .Select(p => p.Serialize()) - .Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue[BuyerAvatarAddressKey].ToAddress(); - purchaseInfos = plainValue[PurchaseInfosKey].ToList(value => new PurchaseInfo((Dictionary)value)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var buyerInventoryAddress = buyerAvatarAddress.Derive(LegacyInventoryKey); - var buyerWorldInformationAddress = buyerAvatarAddress.Derive(LegacyWorldInformationKey); - var buyerQuestListAddress = buyerAvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - foreach (var purchaseInfo in purchaseInfos) - { - var sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - var sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address orderReceiptAddress = OrderReceipt.DeriveAddress(purchaseInfo.OrderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - states = states - .SetState(shardedShopAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .SetState(sellerInventoryAddress, MarkChanged) - .SetState(sellerWorldInformationAddress, MarkChanged) - .SetState(sellerQuestListAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(digestListAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - purchaseInfo.SellerAgentAddress, - GoldCurrencyState.Address); - } - - return states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(buyerInventoryAddress, MarkChanged) - .SetState(buyerWorldInformationAddress, MarkChanged) - .SetState(buyerQuestListAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.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.TryGetAvatarStateV2(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState, out _)) - { - 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); - } - - MaterialItemSheet materialSheet = states.GetSheet(); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address sellerAgentAddress = purchaseInfo.SellerAgentAddress; - Address sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - Address sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - Address sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Guid orderId = purchaseInfo.OrderId; - Address orderAddress = Order.DeriveAddress(orderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - - - if (purchaseInfo.SellerAgentAddress == ctx.Signer) - { - errors.Add((orderId, ErrorCodeInvalidAddress)); - continue; - } - - if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - if (!states.TryGetState(orderAddress, out Dictionary rawOrder)) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - Order order = OrderFactory.Deserialize(rawOrder); - - var shardedShopState = new ShardedShopStateV2(shopStateDict); - - try - { - shardedShopState.Remove(order, context.BlockIndex); - } - catch (OrderIdDoesNotExistException) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - 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); - - - if (!states.TryGetAvatarStateV2(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState, out _)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - int errorCode = order.ValidateTransfer2(sellerAvatarState, purchaseInfo.TradableId, purchaseInfo.Price, context.BlockIndex); - - if (errorCode != 0) - { - errors.Add((orderId, errorCode)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Check Balance. - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < order.Price) - { - errors.Add((orderId, ErrorCodeInsufficientBalance)); - continue; - } - - OrderReceipt orderReceipt; - try - { - orderReceipt = order.Transfer2(sellerAvatarState, buyerAvatarState, context.BlockIndex); - } - catch (ItemDoesNotExistException) - { - errors.Add((orderId, ErrorCodeItemDoesNotExist)); - continue; - } - - Address orderReceiptAddress = OrderReceipt.DeriveAddress(orderId); - if (!(states.GetState(orderReceiptAddress) is null)) - { - errors.Add((orderId, ErrorCodeDuplicateSell)); - continue; - } - - if (!states.TryGetState(digestListAddress, out Dictionary rawDigestList)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - var digestList = new OrderDigestListState(rawDigestList); - digestList.Remove(orderId); - - var expirationMail = sellerAvatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - sellerAvatarState.mailBox.Remove(expirationMail); - } - - var orderSellerMail = new OrderSellerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - var orderBuyerMail = new OrderBuyerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - - buyerAvatarState.Update(orderBuyerMail); - sellerAvatarState.Update(orderSellerMail); - - // // Update quest. - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, order.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, order.Price); - - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - buyerAvatarState.UpdateQuestRewards2(materialSheet); - sellerAvatarState.UpdateQuestRewards2(materialSheet); - - FungibleAssetValue tax = order.GetTax(); - var taxedPrice = order.Price - tax; - - // Transfer tax. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // Transfer seller. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - states = states - .SetState(digestListAddress, digestList.Serialize()) - .SetState(orderReceiptAddress, orderReceipt.Serialize()) - .SetState(sellerInventoryAddress, sellerAvatarState.inventory.Serialize()) - .SetState(sellerWorldInformationAddress, sellerAvatarState.worldInformation.Serialize()) - .SetState(sellerQuestListAddress, sellerAvatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, sellerAvatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(shardedShopAddress, shardedShopState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - - states = states - .SetState(buyerInventoryAddress, buyerAvatarState.inventory.Serialize()) - .SetState(buyerWorldInformationAddress, buyerAvatarState.worldInformation.Serialize()) - .SetState(buyerQuestListAddress, buyerAvatarState.questList.Serialize()) - .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; - } - } -} diff --git a/Lib9c/Action/Buy9.cs b/Lib9c/Action/Buy9.cs deleted file mode 100644 index 1c083eb4d0..0000000000 --- a/Lib9c/Action/Buy9.cs +++ /dev/null @@ -1,339 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy9")] - public class Buy9 : GameAction, IBuy5, IBuyV2 - { - public const int TaxRate = 8; - public const int ErrorCodeFailedLoadingState = 1; - public const int ErrorCodeItemDoesNotExist = 2; - public const int ErrorCodeShopItemExpired = 3; - public const int ErrorCodeInsufficientBalance = 4; - public const int ErrorCodeInvalidAddress = 5; - public const int ErrorCodeInvalidPrice = 6; - public const int ErrorCodeInvalidOrderId = 7; - public const int ErrorCodeInvalidTradableId = 8; - public const int ErrorCodeInvalidItemType = 9; - public const int ErrorCodeDuplicateSell = 10; - - public Address buyerAvatarAddress { get; set; } - public List<(Guid orderId, int errorCode)> errors = new List<(Guid orderId, int errorCode)>(); - public IEnumerable purchaseInfos; - IEnumerable IBuy5.purchaseInfos => purchaseInfos; - - Address IBuyV2.BuyerAvatarAddress => buyerAvatarAddress; - IEnumerable IBuyV2.PurchaseInfos => purchaseInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [BuyerAvatarAddressKey] = buyerAvatarAddress.Serialize(), - [PurchaseInfosKey] = purchaseInfos - .OrderBy(p => p.OrderId) - .Select(p => p.Serialize()) - .Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue[BuyerAvatarAddressKey].ToAddress(); - purchaseInfos = plainValue[PurchaseInfosKey].ToList(value => new PurchaseInfo((Dictionary)value)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var buyerInventoryAddress = buyerAvatarAddress.Derive(LegacyInventoryKey); - var buyerWorldInformationAddress = buyerAvatarAddress.Derive(LegacyWorldInformationKey); - var buyerQuestListAddress = buyerAvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - foreach (var purchaseInfo in purchaseInfos) - { - var sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - var sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address orderReceiptAddress = OrderReceipt.DeriveAddress(purchaseInfo.OrderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - states = states - .SetState(shardedShopAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged) - .SetState(sellerInventoryAddress, MarkChanged) - .SetState(sellerWorldInformationAddress, MarkChanged) - .SetState(sellerQuestListAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(digestListAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - purchaseInfo.SellerAgentAddress, - GoldCurrencyState.Address); - } - - return states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(buyerInventoryAddress, MarkChanged) - .SetState(buyerWorldInformationAddress, MarkChanged) - .SetState(buyerQuestListAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.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.TryGetAvatarStateV2(ctx.Signer, buyerAvatarAddress, out var buyerAvatarState, out _)) - { - 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); - } - - MaterialItemSheet materialSheet = states.GetSheet(); - - foreach (var purchaseInfo in purchaseInfos) - { - Address shardedShopAddress = - ShardedShopStateV2.DeriveAddress(purchaseInfo.ItemSubType, purchaseInfo.OrderId); - Address sellerAgentAddress = purchaseInfo.SellerAgentAddress; - Address sellerAvatarAddress = purchaseInfo.SellerAvatarAddress; - Address sellerInventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var sellerWorldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - Address sellerQuestListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Guid orderId = purchaseInfo.OrderId; - Address orderAddress = Order.DeriveAddress(orderId); - Address digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - - - if (purchaseInfo.SellerAgentAddress == ctx.Signer) - { - errors.Add((orderId, ErrorCodeInvalidAddress)); - continue; - } - - if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - if (!states.TryGetState(orderAddress, out Dictionary rawOrder)) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - Order order = OrderFactory.Deserialize(rawOrder); - - var shardedShopState = new ShardedShopStateV2(shopStateDict); - - try - { - shardedShopState.Remove(order, context.BlockIndex); - } - catch (OrderIdDoesNotExistException) - { - errors.Add((orderId, ErrorCodeInvalidOrderId)); - continue; - } - - 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); - - - if (!states.TryGetAvatarStateV2(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState, out _)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // ValidateTransfer will no longer be required in the next version. (current version : buy9) - var errorCode = sellerAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _) - ? order.ValidateTransfer(sellerAvatarState, purchaseInfo.TradableId, purchaseInfo.Price, context.BlockIndex) - : order.ValidateTransfer2(sellerAvatarState, purchaseInfo.TradableId, purchaseInfo.Price, context.BlockIndex); - - if (errorCode != 0) - { - errors.Add((orderId, errorCode)); - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Check Balance. - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < order.Price) - { - errors.Add((orderId, ErrorCodeInsufficientBalance)); - continue; - } - - OrderReceipt orderReceipt; - try - { - // Transfer will no longer be required in the next version. (current version : buy9) - orderReceipt = sellerAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _) - ? order.Transfer3(sellerAvatarState, buyerAvatarState, context.BlockIndex) - : order.Transfer2(sellerAvatarState, buyerAvatarState, context.BlockIndex); - } - catch (ItemDoesNotExistException) - { - errors.Add((orderId, ErrorCodeItemDoesNotExist)); - continue; - } - - Address orderReceiptAddress = OrderReceipt.DeriveAddress(orderId); - if (!(states.GetState(orderReceiptAddress) is null)) - { - errors.Add((orderId, ErrorCodeDuplicateSell)); - continue; - } - - if (!states.TryGetState(digestListAddress, out Dictionary rawDigestList)) - { - errors.Add((orderId, ErrorCodeFailedLoadingState)); - continue; - } - - var digestList = new OrderDigestListState(rawDigestList); - digestList.Remove(orderId); - - var expirationMail = sellerAvatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - sellerAvatarState.mailBox.Remove(expirationMail); - } - - var orderSellerMail = new OrderSellerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - var orderBuyerMail = new OrderBuyerMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - - buyerAvatarState.Update(orderBuyerMail); - sellerAvatarState.Update(orderSellerMail); - - // // Update quest. - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, order.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, order.Price); - - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - buyerAvatarState.UpdateQuestRewards(materialSheet); - sellerAvatarState.UpdateQuestRewards(materialSheet); - - FungibleAssetValue tax = order.GetTax(); - var taxedPrice = order.Price - tax; - - // Transfer tax. - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // Transfer seller. - states = states.TransferAsset( - context, - context.Signer, - sellerAgentAddress, - taxedPrice - ); - - states = states - .SetState(digestListAddress, digestList.Serialize()) - .SetState(orderReceiptAddress, orderReceipt.Serialize()) - .SetState(sellerInventoryAddress, sellerAvatarState.inventory.Serialize()) - .SetState(sellerWorldInformationAddress, sellerAvatarState.worldInformation.Serialize()) - .SetState(sellerQuestListAddress, sellerAvatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, sellerAvatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(shardedShopAddress, shardedShopState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - - states = states - .SetState(buyerInventoryAddress, buyerAvatarState.inventory.Serialize()) - .SetState(buyerWorldInformationAddress, buyerAvatarState.worldInformation.Serialize()) - .SetState(buyerQuestListAddress, buyerAvatarState.questList.Serialize()) - .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; - } - } -} diff --git a/Lib9c/Action/BuyMultiple.cs b/Lib9c/Action/BuyMultiple.cs deleted file mode 100644 index a53777c2f5..0000000000 --- a/Lib9c/Action/BuyMultiple.cs +++ /dev/null @@ -1,453 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/331 - /// Updated at many pull requests - /// Obsoleted at https://github.com/planetarium/lib9c/pull/487 - /// Updated at many pull requests - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("buy_multiple")] - public class BuyMultiple : GameAction, IBuyMultipleV1 - { - public Address buyerAvatarAddress; - public IEnumerable purchaseInfos; - public BuyerResult buyerResult; - public SellerResult sellerResult; - - Address IBuyMultipleV1.BuyerAvatarAddress => buyerAvatarAddress; - IEnumerable IBuyMultipleV1.PurchaseInfos => purchaseInfos.Select(x => x.Serialize()); - - public const int ERROR_CODE_FAILED_LOADING_STATE = 1; - public const int ERROR_CODE_ITEM_DOES_NOT_EXIST = 2; - public const int ERROR_CODE_SHOPITEM_EXPIRED = 3; - public const int ERROR_CODE_INSUFFICIENT_BALANCE = 4; - - [Serializable] - public class PurchaseInfo : IComparable, IComparable - { - public static bool operator >(PurchaseInfo left, PurchaseInfo right) => left.CompareTo(right) > 0; - - public static bool operator <(PurchaseInfo left, PurchaseInfo right) => left.CompareTo(right) < 0; - - public static bool operator >=(PurchaseInfo left, PurchaseInfo right) => left.CompareTo(right) >= 0; - - public static bool operator <=(PurchaseInfo left, PurchaseInfo right) => left.CompareTo(right) <= 0; - - public static bool operator ==(PurchaseInfo left, PurchaseInfo right) => left.Equals(right); - public static bool operator !=(PurchaseInfo left, PurchaseInfo right) => !(left == right); - - protected bool Equals(PurchaseInfo other) - { - return productId.Equals(other.productId) && sellerAgentAddress.Equals(other.sellerAgentAddress) && sellerAvatarAddress.Equals(other.sellerAvatarAddress); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((PurchaseInfo) obj); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = productId.GetHashCode(); - hashCode = (hashCode * 397) ^ sellerAgentAddress.GetHashCode(); - hashCode = (hashCode * 397) ^ sellerAvatarAddress.GetHashCode(); - return hashCode; - } - } - - public Guid productId; - public Address sellerAgentAddress; - public Address sellerAvatarAddress; - - public PurchaseInfo(Guid productId, Address sellerAgentAddress, Address sellerAvatarAddress) - { - this.productId = productId; - this.sellerAgentAddress = sellerAgentAddress; - this.sellerAvatarAddress = sellerAvatarAddress; - } - - public PurchaseInfo(Bencodex.Types.Dictionary serialized) - { - productId = serialized["productId"].ToGuid(); - sellerAvatarAddress = serialized["sellerAvatarAddress"].ToAddress(); - sellerAgentAddress = serialized["sellerAgentAddress"].ToAddress(); - } - - public IValue Serialize() => -#pragma warning disable LAA1002 - new Bencodex.Types.Dictionary(new Dictionary - { - [(Text) "productId"] = productId.Serialize(), - [(Text) "sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - [(Text) "sellerAgentAddress"] = sellerAgentAddress.Serialize(), - }); -#pragma warning restore LAA1002 - public int CompareTo(PurchaseInfo other) - { - return productId.CompareTo(other.productId); - } - - public int CompareTo(object obj) - { - if (obj is PurchaseInfo other) - { - return CompareTo(other); - } - - throw new ArgumentException(nameof(obj)); - } - } - - [Serializable] - public class PurchaseResult : Buy7.BuyerResult - { - public int errorCode = 0; - public Guid productId; - - public PurchaseResult(Guid shopProductId) - { - productId = shopProductId; - } - - public PurchaseResult(Bencodex.Types.Dictionary serialized) : base(serialized) - { - errorCode = serialized["errorCode"].ToInteger(); - productId = serialized["productId"].ToGuid(); - } - - public override IValue Serialize() => -#pragma warning disable LAA1002 - new Bencodex.Types.Dictionary(new Dictionary - { - [(Text) "errorCode"] = errorCode.Serialize(), - [(Text) "productId"] = productId.Serialize(), - }.Union((Bencodex.Types.Dictionary)base.Serialize())); -#pragma warning restore LAA1002 - } - - [Serializable] - public class BuyerResult - { - public IEnumerable purchaseResults; - - public BuyerResult() - { - } - - public BuyerResult(Bencodex.Types.Dictionary serialized) - { - purchaseResults = serialized["purchaseResults"].ToList(StateExtensions.ToPurchaseResultLegacy); - } - - public IValue Serialize() => -#pragma warning disable LAA1002 - new Bencodex.Types.Dictionary(new Dictionary - { - [(Text) "purchaseResults"] = purchaseResults - .OrderBy(i => i) - .Select(g => g.Serialize()).Serialize() - }); -#pragma warning restore LAA1002 - } - - [Serializable] - public class SellerResult - { - public IEnumerable sellerResults; - - public SellerResult() - { - } - - public SellerResult(Bencodex.Types.Dictionary serialized) - { - sellerResults = serialized["sellerResults"].ToList(StateExtensions.ToSellerResult); - } - - public IValue Serialize() => -#pragma warning disable LAA1002 - new Bencodex.Types.Dictionary(new Dictionary - { - [(Text) "sellerResults"] = sellerResults - .OrderBy(i => i) - .Select(g => g.Serialize()).Serialize() - }); -#pragma warning restore LAA1002 - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["buyerAvatarAddress"] = buyerAvatarAddress.Serialize(), - ["products"] = purchaseInfos - .OrderBy(i => i) - .Select(g => g.Serialize()) - .Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - buyerAvatarAddress = plainValue["buyerAvatarAddress"].ToAddress(); - purchaseInfos = plainValue["products"].ToList(StateExtensions.ToPurchaseInfoLegacy); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - if (ctx.Rehearsal) - { - states = states - .SetState(buyerAvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged); - - foreach (var info in purchaseInfos) - { - var sellerAgentAddress = info.sellerAgentAddress; - var sellerAvatarAddress = info.sellerAvatarAddress; - - states = states.SetState(sellerAvatarAddress, MarkChanged) - .MarkBalanceChanged( - ctx, - GoldCurrencyMock, - ctx.Signer, - sellerAgentAddress, - GoldCurrencyState.Address); - } - - return states.SetState(ShopState.Address, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var availableInfos = purchaseInfos.Where(p => !(p is null)); - - var sellerAgentAddresses = availableInfos.Select(p => p.sellerAgentAddress); - var sellerAvatarAddresses = availableInfos.Select(p => p.sellerAvatarAddress); - var avatarAddresses = sellerAvatarAddresses.Prepend(buyerAvatarAddress); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddresses.ToArray()); - - if (sellerAgentAddresses.Any(a => a.Equals(ctx.Signer))) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer is the seller."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{Addresses}BuyMultiple 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}BuyMultiple 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); - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}BuyMultiple Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - - var sellerAvatarAddressesString = string.Join(", ", sellerAvatarAddresses.Select(a => a.ToString())); - - Log.Verbose( - "{AddressesHex}Execute BuyMultiple; buyer: {Buyer} sellers: {Seller}", - addressesHex, - buyerAvatarAddress, - sellerAvatarAddressesString); - - // Get products in `ShopState`. - Dictionary productDict = (Dictionary) shopStateDict["products"]; - - buyerResult = new BuyerResult(); - sellerResult = new SellerResult(); - var purchaseResults = new List(); - var sellerResults = new List(); - var materialSheet = states.GetSheet(); - - foreach (var productInfo in purchaseInfos) - { - if (productInfo is null) - { - continue; - } - var productId = productInfo.productId; - var purchaseResult = new PurchaseResult(productId); - purchaseResults.Add(purchaseResult); - - - IKey productIdSerialized = (IKey) productId.Serialize(); - if (!productDict.ContainsKey(productIdSerialized)) - { - purchaseResult.errorCode = ERROR_CODE_ITEM_DOES_NOT_EXIST; - continue; - } - - ShopItem shopItem = new ShopItem((Dictionary) productDict[productIdSerialized]); - purchaseResult.shopItem = shopItem; - purchaseResult.itemUsable = shopItem.ItemUsable; - purchaseResult.costume = shopItem.Costume; - - sw.Restart(); - - var avatarAddress = productInfo.sellerAvatarAddress; - if (!states.TryGetAvatarState(productInfo.sellerAgentAddress, - avatarAddress, - out var sellerAvatarState)) - { - purchaseResult.errorCode = ERROR_CODE_FAILED_LOADING_STATE; - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}BuyMultiple Get Seller AgentAvatarState: {Elapsed}", avatarAddress, sw.Elapsed); - sw.Restart(); - - if (!shopItem.SellerAgentAddress.Equals(productInfo.sellerAgentAddress)) - { - purchaseResult.errorCode = ERROR_CODE_ITEM_DOES_NOT_EXIST; - continue; - } - sw.Stop(); - Log.Verbose("{AddressesHex}BuyMultiple Get Item: {Elapsed}", addressesHex, sw.Elapsed); - - if (0 < shopItem.ExpiredBlockIndex && shopItem.ExpiredBlockIndex < context.BlockIndex) - { - purchaseResult.errorCode = ERROR_CODE_SHOPITEM_EXPIRED; - continue; - } - - // Check buyer's balance - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < shopItem.Price) - { - purchaseResult.errorCode = ERROR_CODE_INSUFFICIENT_BALANCE; - continue; - } - - var tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - var taxedPrice = shopItem.Price - tax; - - // Transfer tax - states = states.TransferAsset( - context, - context.Signer, - GoldCurrencyState.Address, - tax); - - // Transfer paid money (taxed) to the seller. - states = states.TransferAsset( - context, - context.Signer, - productInfo.sellerAgentAddress, - taxedPrice - ); - - productDict = (Dictionary)productDict.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", productDict); - - var buyerMail = new BuyerMail(purchaseResult, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - purchaseResult.id = buyerMail.id; - - var sellerResultToAdd = new Buy7.SellerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume, - gold = taxedPrice - }; - var sellerMail = new SellerMail(sellerResultToAdd, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - ctx.BlockIndex); - sellerResultToAdd.id = sellerMail.id; - sellerResults.Add(sellerResultToAdd); - - 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; - sellerAvatarState.UpdateQuestRewards2(materialSheet); - - sw.Restart(); - states = states.SetState(productInfo.sellerAvatarAddress, sellerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}BuyMultiple Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - } - - buyerResult.purchaseResults = purchaseResults; - sellerResult.sellerResults = sellerResults; - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - - buyerAvatarState.UpdateQuestRewards2(materialSheet); - - sw.Restart(); - states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}BuyMultiple Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}BuyMultiple Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Debug("{AddressesHex}BuyMultiple Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/CancelMonsterCollect.cs b/Lib9c/Action/CancelMonsterCollect.cs deleted file mode 100644 index 3b69f1ef67..0000000000 --- a/Lib9c/Action/CancelMonsterCollect.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/383 - /// Renamed at https://github.com/planetarium/lib9c/pull/400 - /// Obsoleted at https://github.com/planetarium/lib9c/pull/487 - /// Updated at many pull requests - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("cancel_monster_collect")] - public class CancelMonsterCollect : GameAction, ICancelMonsterCollectV1 - { - public int collectRound; - public int level; - - int ICancelMonsterCollectV1.CollectRound => collectRound; - int ICancelMonsterCollectV1.Level => level; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - Address collectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectRound); - if (context.Rehearsal) - { - return states - .SetState(collectionAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, collectionAddress, context.Signer); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, context.Signer); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CancelMonsterCollect exec started", addressesHex); - 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(); - 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(); - monsterCollectionState.Update(level, rewardLevel, monsterCollectionRewardSheet); - for (int i = currentLevel; i > level; i--) - { - balance += monsterCollectionSheet[i].RequiredGold * currency; - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CancelMonsterCollect Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(collectionAddress, monsterCollectionState.Serialize()) - .TransferAsset(context, collectionAddress, context.Signer, balance); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [MonsterCollectionRoundKey] = collectRound.Serialize(), - [LevelKey] = level.Serialize(), - }.ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - collectRound = plainValue[MonsterCollectionRoundKey].ToInteger(); - level = plainValue[LevelKey].ToInteger(); - } - } -} diff --git a/Lib9c/Action/ChargeActionPoint0.cs b/Lib9c/Action/ChargeActionPoint0.cs deleted file mode 100644 index 2803cd215e..0000000000 --- a/Lib9c/Action/ChargeActionPoint0.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("charge_action_point")] - public class ChargeActionPoint0 : GameAction, IChargeActionPointV1 - { - public Address avatarAddress; - - Address IChargeActionPointV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states.SetState(avatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStates(context.Signer, avatarAddress, out var _, out var avatarState)) - { - return states; - } - - var row = states.GetSheet().Values.FirstOrDefault(r => r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem2(apStone)) - { - Log.Error("{AddressesHex}Not enough item {ApStone}", addressesHex, apStone); - return states; - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - return states; - } - - avatarState.actionPoint = gameConfigState.ActionPointMax; - return states.SetState(avatarAddress, avatarState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - } -} diff --git a/Lib9c/Action/ChargeActionPoint2.cs b/Lib9c/Action/ChargeActionPoint2.cs deleted file mode 100644 index 1ff088cecb..0000000000 --- a/Lib9c/Action/ChargeActionPoint2.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("charge_action_point2")] - public class ChargeActionPoint2 : GameAction, IChargeActionPointV1 - { - public Address avatarAddress; - - Address IChargeActionPointV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states.SetState(avatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAvatarState(context.Signer, avatarAddress, out var avatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var row = states.GetSheet().Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - avatarState.actionPoint = gameConfigState.ActionPointMax; - return states.SetState(avatarAddress, avatarState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - } -} diff --git a/Lib9c/Action/ClaimMonsterCollectionReward0.cs b/Lib9c/Action/ClaimMonsterCollectionReward0.cs deleted file mode 100644 index 52f445b96f..0000000000 --- a/Lib9c/Action/ClaimMonsterCollectionReward0.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("claim_monster_collection_reward")] - public class ClaimMonsterCollectionReward0 : GameAction, IClaimMonsterCollectionRewardV1 - { - public Address avatarAddress; - public int collectionRound; - - Address IClaimMonsterCollectionRewardV1.AvatarAddress => avatarAddress; - int IClaimMonsterCollectionRewardV1.CollectionRound => collectionRound; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - Address collectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectionRound); - - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(avatarAddress, MarkChanged) - .SetState(collectionAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.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 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(); - 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(context, collectionAddress, context.Signer, gold); - } - } - - return states - .SetState(avatarAddress, avatarState.Serialize()) - .SetState(collectionAddress, monsterCollectionState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [MonsterCollectionRoundKey] = collectionRound.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - collectionRound = plainValue[MonsterCollectionRoundKey].ToInteger(); - } - } -} diff --git a/Lib9c/Action/ClaimMonsterCollectionReward2.cs b/Lib9c/Action/ClaimMonsterCollectionReward2.cs deleted file mode 100644 index 7523b52cac..0000000000 --- a/Lib9c/Action/ClaimMonsterCollectionReward2.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("claim_monster_collection_reward2")] - public class ClaimMonsterCollectionReward2 : GameAction, IClaimMonsterCollectionRewardV2 - { - public Address avatarAddress; - - Address IClaimMonsterCollectionRewardV2.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - 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(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out AgentState agentState, out AvatarState avatarState, out _)) - { - 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 rewards = - monsterCollectionState.CalculateRewards( - states.GetSheet(), - 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()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward1.cs b/Lib9c/Action/ClaimStakeReward1.cs deleted file mode 100644 index 20ed67fa90..0000000000 --- a/Lib9c/Action/ClaimStakeReward1.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Extensions; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [ActionType("claim_stake_reward")] - [ActionObsolete(ObsoleteIndex)] - public class ClaimStakeReward1 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - public const long ObsoleteIndex = ActionObsoleteConfig.V200030ObsoleteIndex; - - internal Address AvatarAddress { get; private set; } - - Address IClaimStakeRewardV1.AvatarAddress => AvatarAddress; - - public ClaimStakeReward1(Address avatarAddress) - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward1() : base() - { - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - CheckObsolete(ObsoleteIndex, context); - var states = context.PreviousState; - if (!states.TryGetStakeState(context.Signer, out StakeState stakeState)) - { - throw new FailedLoadStateException(nameof(StakeState)); - } - - var sheets = states.GetSheets(sheetTypes: new[] - { - typeof(StakeRegularRewardSheet), - typeof(ConsumableItemSheet), - typeof(CostumeItemSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - }); - - var stakeRegularRewardSheet = sheets.GetSheet(); - - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - - if (!stakeState.IsClaimable(context.BlockIndex)) - { - throw new RequiredBlockIndexException(); - } - - var avatarState = states.GetAvatarStateV2(AvatarAddress); - int level = stakeRegularRewardSheet.FindLevelByStakedAmount(context.Signer, stakedAmount); - var rewards = stakeRegularRewardSheet[level].Rewards; - ItemSheet itemSheet = sheets.GetItemSheet(); - var accumulatedRewards = stakeState.CalculateAccumulatedItemRewardsV1(context.BlockIndex); - foreach (var reward in rewards) - { - var (quantity, _) = stakedAmount.DivRem(currency * reward.Rate); - if (quantity < 1) - { - // If the quantity is zero, it doesn't add the item into inventory. - continue; - } - - ItemSheet.Row row = itemSheet[reward.ItemId]; - ItemBase item = row is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(row, context.Random); - avatarState.inventory.AddItem(item, (int) quantity * accumulatedRewards); - } - - if (states.TryGetSheet( - out var stakeRegularFixedRewardSheet)) - { - var fixedRewards = stakeRegularFixedRewardSheet[level].Rewards; - foreach (var reward in fixedRewards) - { - ItemSheet.Row row = itemSheet[reward.ItemId]; - ItemBase item = row is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(row, context.Random); - avatarState.inventory.AddItem(item, reward.Count * accumulatedRewards); - } - } - - stakeState.Claim(context.BlockIndex); - return states.SetState(stakeState.address, stakeState.Serialize()) - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward3.cs b/Lib9c/Action/ClaimStakeReward3.cs deleted file mode 100644 index b32991500d..0000000000 --- a/Lib9c/Action/ClaimStakeReward3.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1458 - /// - [ActionType(ActionTypeText)] - [ActionObsolete(ObsoleteBlockIndex)] - public class ClaimStakeReward3 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - private const string ActionTypeText = "claim_stake_reward3"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200030ObsoleteIndex; - - /// - /// This is the version 1 of the stake reward sheet. - /// The version 1 is used for calculating the reward for the stake - /// that is accumulated before the table patch. - /// - public static class V1 - { - public const int MaxLevel = 5; - public const string StakeRegularRewardSheetCsv = - @"level,required_gold,item_id,rate,type -1,50,400000,10,Item -1,50,500000,800,Item -1,50,20001,6000,Rune -2,500,400000,8,Item -2,500,500000,800,Item -2,500,20001,6000,Rune -3,5000,400000,5,Item -3,5000,500000,800,Item -3,5000,20001,6000,Rune -4,50000,400000,5,Item -4,50000,500000,800,Item -4,50000,20001,6000,Rune -5,500000,400000,5,Item -5,500000,500000,800,Item -5,500000,20001,6000,Rune"; - - public const string StakeRegularFixedRewardSheetCsv = - @"level,required_gold,item_id,count -1,50,500000,1 -2,500,500000,2 -3,5000,500000,2 -4,50000,500000,2 -5,500000,500000,2"; - } - - // NOTE: Use this when the or - // is patched. - // public static class V2 - // { - // } - - private readonly ImmutableSortedDictionary< - string, - ImmutableSortedDictionary> _stakeRewardHistoryDict; - - internal Address AvatarAddress { get; private set; } - - Address IClaimStakeRewardV1.AvatarAddress => AvatarAddress; - - public ClaimStakeReward3(Address avatarAddress) : this() - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward3() - { - var regularRewardSheetV1 = new StakeRegularRewardSheet(); - regularRewardSheetV1.Set(V1.StakeRegularRewardSheetCsv); - var fixedRewardSheetV1 = new StakeRegularFixedRewardSheet(); - fixedRewardSheetV1.Set(V1.StakeRegularFixedRewardSheetCsv); - _stakeRewardHistoryDict = - new Dictionary> - { - { - "StakeRegularRewardSheet", new Dictionary - { - { 1, regularRewardSheetV1 }, - }.ToImmutableSortedDictionary() - }, - { - "StakeRegularFixedRewardSheet", - new Dictionary - { - { 1, fixedRewardSheetV1 } - }.ToImmutableSortedDictionary() - }, - }.ToImmutableSortedDictionary(); - } - - private IAccountStateDelta ProcessReward( - IActionContext context, - IAccountStateDelta states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedAmount, - int itemRewardStep, - int runeRewardStep, - List fixedReward, - List regularReward) - { - var stakedCurrency = stakedAmount.Currency; - - // Regular Reward - foreach (var reward in regularReward) - { - switch (reward.Type) - { - case StakeRegularRewardSheet.StakeRewardType.Item: - var (quantity, _) = stakedAmount.DivRem(stakedCurrency * reward.Rate); - if (quantity < 1) - { - // If the quantity is zero, it doesn't add the item into inventory. - continue; - } - - ItemSheet.Row row = itemSheet[reward.ItemId]; - ItemBase item = row is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(row, context.Random); - avatarState.inventory.AddItem(item, (int)quantity * itemRewardStep); - break; - case StakeRegularRewardSheet.StakeRewardType.Rune: - var runeReward = runeRewardStep * - RuneHelper.CalculateStakeReward(stakedAmount, reward.Rate); - if (runeReward < 1 * RuneHelper.StakeRune) - { - continue; - } - - states = states.MintAsset(context, AvatarAddress, runeReward); - break; - default: - break; - } - } - - // Fixed Reward - foreach (var reward in fixedReward) - { - ItemSheet.Row row = itemSheet[reward.ItemId]; - ItemBase item = row is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(row, context.Random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - CheckActionAvailable(ClaimStakeReward2.ObsoletedIndex, context); - CheckObsolete(ObsoleteBlockIndex, context); - - var states = context.PreviousState; - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - if (!states.TryGetStakeState(context.Signer, out StakeState stakeState)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(StakeState), - StakeState.DeriveAddress(context.Signer)); - } - - if (!stakeState.IsClaimable(context.BlockIndex, out _, out _)) - { - throw new RequiredBlockIndexException( - ActionTypeText, - addressesHex, - context.BlockIndex); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(AvatarState), - AvatarAddress); - } - - var sheets = states.GetSheets(sheetTypes: new[] - { - typeof(StakeRegularRewardSheet), - typeof(ConsumableItemSheet), - typeof(CostumeItemSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - }); - - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - var stakeRegularRewardSheet = sheets.GetSheet(); - var level = - stakeRegularRewardSheet.FindLevelByStakedAmount(context.Signer, stakedAmount); - var itemSheet = sheets.GetItemSheet(); - stakeState.CalculateAccumulatedItemRewardsV1( - context.BlockIndex, - out var itemV1Step, - out var itemV2Step); - stakeState.CalculateAccumulatedRuneRewardsV1( - context.BlockIndex, - out var runeV1Step, - out var runeV2Step); - if (itemV1Step > 0) - { - var v1Level = Math.Min(level, V1.MaxLevel); - var regularFixedSheetV1Row = (StakeRegularFixedRewardSheet)_stakeRewardHistoryDict[ - "StakeRegularFixedRewardSheet"][1]; - var fixedRewardV1 = regularFixedSheetV1Row[v1Level].Rewards; - var regularSheetV1Row = (StakeRegularRewardSheet)_stakeRewardHistoryDict[ - "StakeRegularRewardSheet"][1]; - var regularRewardV1 = regularSheetV1Row[v1Level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV1Step, - runeV1Step, - fixedRewardV1, - regularRewardV1); - } - - if (itemV2Step > 0) - { - var regularFixedReward = - states.TryGetSheet(out var fixedRewardSheet) - ? fixedRewardSheet[level].Rewards - : new List(); - var regularReward = sheets.GetSheet()[level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV2Step, - runeV2Step, - regularFixedReward, - regularReward); - } - - stakeState.Claim(context.BlockIndex); - - if (migrationRequired) - { - states = states - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - avatarState.address.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - return states - .SetState(stakeState.address, stakeState.Serialize()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward4.cs b/Lib9c/Action/ClaimStakeReward4.cs deleted file mode 100644 index f3e1219635..0000000000 --- a/Lib9c/Action/ClaimStakeReward4.cs +++ /dev/null @@ -1,334 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1458 - /// - [ActionType(ActionTypeText)] - [ActionObsolete(ObsoleteBlockIndex)] - public class ClaimStakeReward4 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - private const string ActionTypeText = "claim_stake_reward4"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200031ObsoleteIndex; - - /// - /// This is the version 1 of the stake reward sheet. - /// The version 1 is used for calculating the reward for the stake - /// that is accumulated before the table patch. - /// - public static class V1 - { - public const int MaxLevel = 5; - - public const string StakeRegularRewardSheetCsv = - @"level,required_gold,item_id,rate,type -1,50,400000,10,Item -1,50,500000,800,Item -1,50,20001,6000,Rune -2,500,400000,8,Item -2,500,500000,800,Item -2,500,20001,6000,Rune -3,5000,400000,5,Item -3,5000,500000,800,Item -3,5000,20001,6000,Rune -4,50000,400000,5,Item -4,50000,500000,800,Item -4,50000,20001,6000,Rune -5,500000,400000,5,Item -5,500000,500000,800,Item -5,500000,20001,6000,Rune"; - - public const string StakeRegularFixedRewardSheetCsv = - @"level,required_gold,item_id,count -1,50,500000,1 -2,500,500000,2 -3,5000,500000,2 -4,50000,500000,2 -5,500000,500000,2"; - - private static StakeRegularRewardSheet _stakeRegularRewardSheet; - private static StakeRegularFixedRewardSheet _stakeRegularFixedRewardSheet; - - public static StakeRegularRewardSheet StakeRegularRewardSheet - { - get - { - if (_stakeRegularRewardSheet is null) - { - _stakeRegularRewardSheet = new StakeRegularRewardSheet(); - _stakeRegularRewardSheet.Set(StakeRegularRewardSheetCsv); - } - - return _stakeRegularRewardSheet; - } - } - - public static StakeRegularFixedRewardSheet StakeRegularFixedRewardSheet - { - get - { - if (_stakeRegularFixedRewardSheet is null) - { - _stakeRegularFixedRewardSheet = new StakeRegularFixedRewardSheet(); - _stakeRegularFixedRewardSheet.Set(StakeRegularFixedRewardSheetCsv); - } - - return _stakeRegularFixedRewardSheet; - } - } - } - - // NOTE: Use this when the or - // is patched. - // public static class V2 - // { - // } - - internal Address AvatarAddress { get; private set; } - - Address IClaimStakeRewardV1.AvatarAddress => AvatarAddress; - - public ClaimStakeReward4(Address avatarAddress) : this() - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward4() - { - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - CheckObsolete(ObsoleteBlockIndex, context); - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - var states = context.PreviousState; - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - if (!states.TryGetStakeState(context.Signer, out var stakeState)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(StakeState), - StakeState.DeriveAddress(context.Signer)); - } - - if (!stakeState.IsClaimable(context.BlockIndex, out _, out _)) - { - throw new RequiredBlockIndexException( - ActionTypeText, - addressesHex, - context.BlockIndex); - } - - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(AvatarState), - AvatarAddress); - } - - var sheets = states.GetSheets(sheetTypes: new[] - { - typeof(StakeRegularRewardSheet), - typeof(ConsumableItemSheet), - typeof(CostumeItemSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - }); - - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - var stakeRegularRewardSheet = sheets.GetSheet(); - var level = - stakeRegularRewardSheet.FindLevelByStakedAmount(context.Signer, stakedAmount); - var itemSheet = sheets.GetItemSheet(); - stakeState.CalculateAccumulatedItemRewardsV2( - context.BlockIndex, - out var itemV1Step, - out var itemV2Step); - stakeState.CalculateAccumulatedRuneRewardsV2( - context.BlockIndex, - out var runeV1Step, - out var runeV2Step); - stakeState.CalculateAccumulatedCurrencyRewardsV1( - context.BlockIndex, - out var currencyV1Step, - out var currencyV2Step); - if (itemV1Step > 0) - { - var v1Level = Math.Min(level, V1.MaxLevel); - var fixedRewardV1 = V1.StakeRegularFixedRewardSheet[v1Level].Rewards; - var regularRewardV1 = V1.StakeRegularRewardSheet[v1Level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV1Step, - runeV1Step, - currencyV1Step, - fixedRewardV1, - regularRewardV1); - } - - if (itemV2Step > 0) - { - var regularFixedReward = - states.TryGetSheet(out var fixedRewardSheet) - ? fixedRewardSheet[level].Rewards - : new List(); - var regularReward = sheets.GetSheet()[level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV2Step, - runeV2Step, - currencyV2Step, - regularFixedReward, - regularReward); - } - - stakeState.Claim(context.BlockIndex); - - if (migrationRequired) - { - states = states - .SetState(avatarState.address, avatarState.SerializeV2()) - .SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - avatarState.address.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - - return states - .SetState(stakeState.address, stakeState.Serialize()) - .SetState( - avatarState.address.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()); - } - - private IAccountStateDelta ProcessReward( - IActionContext context, - IAccountStateDelta states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedAmount, - int itemRewardStep, - int runeRewardStep, - int currencyRewardStep, - List fixedReward, - List regularReward) - { - var stakedCurrency = stakedAmount.Currency; - - // Regular Reward - foreach (var reward in regularReward) - { - switch (reward.Type) - { - case StakeRegularRewardSheet.StakeRewardType.Item: - var (quantity, _) = stakedAmount.DivRem(stakedCurrency * reward.Rate); - if (quantity < 1) - { - // If the quantity is zero, it doesn't add the item into inventory. - continue; - } - - ItemSheet.Row row = itemSheet[reward.ItemId]; - ItemBase item = row is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(row, context.Random); - avatarState.inventory.AddItem(item, (int)quantity * itemRewardStep); - break; - case StakeRegularRewardSheet.StakeRewardType.Rune: - var runeReward = runeRewardStep * - RuneHelper.CalculateStakeReward(stakedAmount, reward.Rate); - if (runeReward < 1 * RuneHelper.StakeRune) - { - continue; - } - - states = states.MintAsset(context, AvatarAddress, runeReward); - break; - case StakeRegularRewardSheet.StakeRewardType.Currency: - if (string.IsNullOrEmpty(reward.CurrencyTicker)) - { - throw new NullReferenceException("currency ticker is null or empty"); - } - - var rewardCurrency = - Currencies.GetMinterlessCurrency(reward.CurrencyTicker); - var rewardCurrencyQuantity = - stakedAmount.DivRem(reward.Rate * stakedAmount.Currency).Quotient; - if (rewardCurrencyQuantity <= 0) - { - continue; - } - - states = states.MintAsset( - context, - context.Signer, - rewardCurrencyQuantity * currencyRewardStep * rewardCurrency); - break; - default: - break; - } - } - - // Fixed Reward - foreach (var reward in fixedReward) - { - ItemSheet.Row row = itemSheet[reward.ItemId]; - ItemBase item = row is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(row, context.Random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - } -} diff --git a/Lib9c/Action/CombinationConsumable0.cs b/Lib9c/Action/CombinationConsumable0.cs deleted file mode 100644 index df744f1c03..0000000000 --- a/Lib9c/Action/CombinationConsumable0.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Material = Nekoyume.Model.Item.Material; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_consumable")] - public class CombinationConsumable0 : GameAction, ICombinationConsumableV1 - { - public Address AvatarAddress; - public int recipeId; - public int slotIndex; - - Address ICombinationConsumableV1.AvatarAddress => AvatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["recipeId"] = recipeId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - public CombinationConsumable0() - { - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - recipeId = plainValue["recipeId"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination 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}Combination Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if(!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - - Log.Verbose("{AddressesHex}Execute Combination; player: {Player}", addressesHex, AvatarAddress); - var consumableItemSheet = states.GetSheet(); - var recipeRow = states.GetSheet().Values.FirstOrDefault(r => r.Id == recipeId); - if (recipeRow is null) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemRecipeSheet), recipeId); - } - var materials = new Dictionary(); - foreach (var materialInfo in recipeRow.Materials.OrderBy(r => r.Id)) - { - var materialId = materialInfo.Id; - var count = materialInfo.Count; - if (avatarState.inventory.HasItem(materialId, count)) - { - avatarState.inventory.TryGetItem(materialId, out var inventoryItem); - var material = (Material) inventoryItem.item; - materials[material] = count; - avatarState.inventory.RemoveFungibleItem2(material, count); - } - else - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({materialId} * {count})"); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var result = new CombinationConsumable5.ResultModel - { - materials = materials, - itemType = ItemType.Consumable, - - }; - - var costAP = recipeRow.RequiredActionPoint; - if (avatarState.actionPoint < costAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costAP}" - ); - } - - // ap 차감. - avatarState.actionPoint -= costAP; - result.actionPoint = costAP; - - var resultConsumableItemId = recipeRow.ResultConsumableItemId; - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get Food id: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - result.recipeId = recipeRow.Id; - - if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemSheet), resultConsumableItemId); - } - - // 조합 결과 획득. - var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; - var itemId = ctx.Random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - result.id = mail.id; - avatarState.Update2(mail); - avatarState.UpdateFromCombination2(itemUsable); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - states = states.SetState(AvatarAddress, avatarState.Serialize()); - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(slotAddress, slotState.Serialize()); - } - - private static ItemUsable GetFood(ConsumableItemSheet.Row equipmentItemRow, Guid itemId, long ctxBlockIndex) - { - return ItemFactory.CreateItemUsable(equipmentItemRow, itemId, ctxBlockIndex); - } - } -} diff --git a/Lib9c/Action/CombinationConsumable2.cs b/Lib9c/Action/CombinationConsumable2.cs deleted file mode 100644 index 97a475b1cd..0000000000 --- a/Lib9c/Action/CombinationConsumable2.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Material = Nekoyume.Model.Item.Material; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_consumable2")] - public class CombinationConsumable2 : GameAction, ICombinationConsumableV1 - { - public Address AvatarAddress; - public int recipeId; - public int slotIndex; - - Address ICombinationConsumableV1.AvatarAddress => AvatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["recipeId"] = recipeId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - public CombinationConsumable2() - { - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - recipeId = plainValue["recipeId"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination 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}Combination Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if(!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - - Log.Verbose("{AddressesHex}Execute Combination; player: {Player}", addressesHex, AvatarAddress); - var consumableItemSheet = states.GetSheet(); - var recipeRow = states.GetSheet().Values.FirstOrDefault(r => r.Id == recipeId); - if (recipeRow is null) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemRecipeSheet), recipeId); - } - var materials = new Dictionary(); - foreach (var materialInfo in recipeRow.Materials.OrderBy(r => r.Id)) - { - var materialId = materialInfo.Id; - var count = materialInfo.Count; - if (avatarState.inventory.HasItem(materialId, count)) - { - avatarState.inventory.TryGetItem(materialId, out var inventoryItem); - var material = (Material) inventoryItem.item; - materials[material] = count; - avatarState.inventory.RemoveFungibleItem2(material, count); - } - else - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({materialId} * {count})"); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var result = new CombinationConsumable5.ResultModel - { - materials = materials, - itemType = ItemType.Consumable, - - }; - - var costAP = recipeRow.RequiredActionPoint; - if (avatarState.actionPoint < costAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costAP}" - ); - } - - // ap 차감. - avatarState.actionPoint -= costAP; - result.actionPoint = costAP; - - var resultConsumableItemId = recipeRow.ResultConsumableItemId; - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get Food id: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - result.recipeId = recipeRow.Id; - - if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemSheet), resultConsumableItemId); - } - - // 조합 결과 획득. - var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; - var itemId = ctx.Random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - result.id = mail.id; - avatarState.Update3(mail); - avatarState.UpdateFromCombination2(itemUsable); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - states = states.SetState(AvatarAddress, avatarState.Serialize()); - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(slotAddress, slotState.Serialize()); - } - - private static ItemUsable GetFood(ConsumableItemSheet.Row equipmentItemRow, Guid itemId, long ctxBlockIndex) - { - return ItemFactory.CreateItemUsable(equipmentItemRow, itemId, ctxBlockIndex); - } - } -} diff --git a/Lib9c/Action/CombinationConsumable3.cs b/Lib9c/Action/CombinationConsumable3.cs deleted file mode 100644 index a5ebc40676..0000000000 --- a/Lib9c/Action/CombinationConsumable3.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Material = Nekoyume.Model.Item.Material; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_consumable3")] - public class CombinationConsumable3 : GameAction, ICombinationConsumableV1 - { - public Address AvatarAddress; - public int recipeId; - public int slotIndex; - - Address ICombinationConsumableV1.AvatarAddress => AvatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["recipeId"] = recipeId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - public CombinationConsumable3() - { - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - recipeId = plainValue["recipeId"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination 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}Combination Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if(!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - - Log.Verbose("{AddressesHex}Execute Combination; player: {Player}", addressesHex, AvatarAddress); - var consumableItemSheet = states.GetSheet(); - var recipeRow = states.GetSheet().Values.FirstOrDefault(r => r.Id == recipeId); - if (recipeRow is null) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemRecipeSheet), recipeId); - } - var materials = new Dictionary(); - foreach (var materialInfo in recipeRow.Materials.OrderBy(r => r.Id)) - { - var materialId = materialInfo.Id; - var count = materialInfo.Count; - if (avatarState.inventory.HasItem(materialId, count)) - { - avatarState.inventory.TryGetItem(materialId, out var inventoryItem); - var material = (Material) inventoryItem.item; - materials[material] = count; - avatarState.inventory.RemoveFungibleItem2(material, count); - } - else - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({materialId} * {count})"); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var result = new CombinationConsumable5.ResultModel - { - materials = materials, - itemType = ItemType.Consumable, - - }; - - var costAP = recipeRow.RequiredActionPoint; - if (avatarState.actionPoint < costAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costAP}" - ); - } - - // ap 차감. - avatarState.actionPoint -= costAP; - result.actionPoint = costAP; - - var resultConsumableItemId = recipeRow.ResultConsumableItemId; - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get Food id: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - result.recipeId = recipeRow.Id; - - if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemSheet), resultConsumableItemId); - } - - // 조합 결과 획득. - var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; - var itemId = ctx.Random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - result.id = mail.id; - avatarState.Update(mail); - avatarState.UpdateFromCombination2(itemUsable); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - states = states.SetState(AvatarAddress, avatarState.Serialize()); - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(slotAddress, slotState.Serialize()); - } - - private static ItemUsable GetFood(ConsumableItemSheet.Row equipmentItemRow, Guid itemId, long ctxBlockIndex) - { - return ItemFactory.CreateItemUsable(equipmentItemRow, itemId, ctxBlockIndex); - } - } -} diff --git a/Lib9c/Action/CombinationConsumable4.cs b/Lib9c/Action/CombinationConsumable4.cs deleted file mode 100644 index 1ffd36c630..0000000000 --- a/Lib9c/Action/CombinationConsumable4.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Material = Nekoyume.Model.Item.Material; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_consumable4")] - public class CombinationConsumable4 : GameAction, ICombinationConsumableV1 - { - public Address AvatarAddress; - public int recipeId; - public int slotIndex; - - Address ICombinationConsumableV1.AvatarAddress => AvatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["recipeId"] = recipeId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - public CombinationConsumable4() - { - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - recipeId = plainValue["recipeId"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination 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}Combination Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if(!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - - Log.Verbose("{AddressesHex}Execute Combination; player: {Player}", addressesHex, AvatarAddress); - var consumableItemSheet = states.GetSheet(); - var recipeRow = states.GetSheet().Values.FirstOrDefault(r => r.Id == recipeId); - if (recipeRow is null) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemRecipeSheet), recipeId); - } - var materials = new Dictionary(); - foreach (var materialInfo in recipeRow.Materials.OrderBy(r => r.Id)) - { - var materialId = materialInfo.Id; - var count = materialInfo.Count; - if (avatarState.inventory.HasItem(materialId, count)) - { - avatarState.inventory.TryGetItem(materialId, out var inventoryItem); - var material = (Material) inventoryItem.item; - materials[material] = count; - avatarState.inventory.RemoveFungibleItem2(material, count); - } - else - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({materialId} * {count})"); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var result = new CombinationConsumable5.ResultModel - { - materials = materials, - itemType = ItemType.Consumable, - - }; - - var costAP = recipeRow.RequiredActionPoint; - if (avatarState.actionPoint < costAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costAP}" - ); - } - - // ap 차감. - avatarState.actionPoint -= costAP; - result.actionPoint = costAP; - - var resultConsumableItemId = recipeRow.ResultConsumableItemId; - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get Food id: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - result.recipeId = recipeRow.Id; - - if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemSheet), resultConsumableItemId); - } - - // 조합 결과 획득. - var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; - var itemId = ctx.Random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - result.id = mail.id; - avatarState.Update(mail); - avatarState.UpdateFromCombination2(itemUsable); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - states = states.SetState(AvatarAddress, avatarState.Serialize()); - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(slotAddress, slotState.Serialize()); - } - - private static ItemUsable GetFood(ConsumableItemSheet.Row equipmentItemRow, Guid itemId, long ctxBlockIndex) - { - return ItemFactory.CreateItemUsable(equipmentItemRow, itemId, ctxBlockIndex); - } - } -} diff --git a/Lib9c/Action/CombinationConsumable6.cs b/Lib9c/Action/CombinationConsumable6.cs deleted file mode 100644 index 04f96a42df..0000000000 --- a/Lib9c/Action/CombinationConsumable6.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Material = Nekoyume.Model.Item.Material; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_consumable6")] - public class CombinationConsumable6 : GameAction, ICombinationConsumableV1 - { - public Address AvatarAddress; - public int recipeId; - public int slotIndex; - - Address ICombinationConsumableV1.AvatarAddress => AvatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["recipeId"] = recipeId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - recipeId = plainValue["recipeId"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if(!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - - Log.Verbose("{AddressesHex}Execute Combination; player: {Player}", addressesHex, AvatarAddress); - var consumableItemSheet = states.GetSheet(); - var recipeRow = states.GetSheet().Values.FirstOrDefault(r => r.Id == recipeId); - if (recipeRow is null) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemRecipeSheet), recipeId); - } - var materials = new Dictionary(); - foreach (var materialInfo in recipeRow.Materials.OrderBy(r => r.Id)) - { - var materialId = materialInfo.Id; - var count = materialInfo.Count; - if (avatarState.inventory.HasItem(materialId, count)) - { - avatarState.inventory.TryGetItem(materialId, out var inventoryItem); - if (!(inventoryItem.item is Material material)) - { - throw new InvalidMaterialException($"Aborted because material id({materialId}) not valid"); - } - - materials[material] = count; - avatarState.inventory.RemoveFungibleItem(material, count); - } - else - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({materialId} * {count})"); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var result = new CombinationConsumable5.ResultModel - { - materials = materials, - itemType = ItemType.Consumable, - }; - - var costAP = recipeRow.RequiredActionPoint; - if (avatarState.actionPoint < costAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costAP}" - ); - } - - // ap 차감. - avatarState.actionPoint -= costAP; - result.actionPoint = costAP; - - var resultConsumableItemId = recipeRow.ResultConsumableItemId; - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get Food id: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - result.recipeId = recipeRow.Id; - - if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemSheet), resultConsumableItemId); - } - - // 조합 결과 획득. - var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; - var itemId = ctx.Random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - result.id = mail.id; - avatarState.Update(mail); - avatarState.UpdateFromCombination(itemUsable); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - states = states.SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(slotAddress, slotState.Serialize()); - } - - private static ItemUsable GetFood(ConsumableItemSheet.Row equipmentItemRow, Guid itemId, long ctxBlockIndex) - { - return ItemFactory.CreateItemUsable(equipmentItemRow, itemId, ctxBlockIndex); - } - } -} diff --git a/Lib9c/Action/CombinationConsumable7.cs b/Lib9c/Action/CombinationConsumable7.cs deleted file mode 100644 index 6ce54a881f..0000000000 --- a/Lib9c/Action/CombinationConsumable7.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Material = Nekoyume.Model.Item.Material; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_consumable7")] - public class CombinationConsumable7 : GameAction, ICombinationConsumableV1 - { - public Address AvatarAddress; - public int recipeId; - public int slotIndex; - - Address ICombinationConsumableV1.AvatarAddress => AvatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["recipeId"] = recipeId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - recipeId = plainValue["recipeId"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if(!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - - Log.Verbose("{AddressesHex}Execute Combination; player: {Player}", addressesHex, AvatarAddress); - var consumableItemSheet = states.GetSheet(); - var recipeRow = states.GetSheet().Values.FirstOrDefault(r => r.Id == recipeId); - if (recipeRow is null) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemRecipeSheet), recipeId); - } - var materials = new Dictionary(); - foreach (var materialInfo in recipeRow.Materials.OrderBy(r => r.Id)) - { - var materialId = materialInfo.Id; - var count = materialInfo.Count; - if (avatarState.inventory.HasItem(materialId, count)) - { - avatarState.inventory.TryGetItem(materialId, out var inventoryItem); - if (!(inventoryItem.item is Material material)) - { - throw new InvalidMaterialException($"Aborted because material id({materialId}) not valid"); - } - - materials[material] = count; - avatarState.inventory.RemoveFungibleItem(material, context.BlockIndex, count); - } - else - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({materialId} * {count})"); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var result = new CombinationConsumable5.ResultModel - { - materials = materials, - itemType = ItemType.Consumable, - }; - - var costAP = recipeRow.RequiredActionPoint; - if (avatarState.actionPoint < costAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costAP}" - ); - } - - // ap 차감. - avatarState.actionPoint -= costAP; - result.actionPoint = costAP; - - var resultConsumableItemId = recipeRow.ResultConsumableItemId; - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Get Food id: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - result.recipeId = recipeRow.Id; - - if (!consumableItemSheet.TryGetValue(resultConsumableItemId, out var consumableItemRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(ConsumableItemSheet), resultConsumableItemId); - } - - // 조합 결과 획득. - var requiredBlockIndex = ctx.BlockIndex + recipeRow.RequiredBlockIndex; - var itemId = ctx.Random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - result.id = mail.id; - avatarState.Update(mail); - avatarState.UpdateFromCombination(itemUsable); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - states = states.SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}Combination Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Combination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(slotAddress, slotState.Serialize()); - } - - private static ItemUsable GetFood(ConsumableItemSheet.Row equipmentItemRow, Guid itemId, long ctxBlockIndex) - { - return ItemFactory.CreateItemUsable(equipmentItemRow, itemId, ctxBlockIndex); - } - } -} diff --git a/Lib9c/Action/CombinationEquipment0.cs b/Lib9c/Action/CombinationEquipment0.cs deleted file mode 100644 index 39cf09631e..0000000000 --- a/Lib9c/Action/CombinationEquipment0.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment")] - public class CombinationEquipment0 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public Address AvatarAddress; - public int RecipeId; - public int SlotIndex; - public int? SubRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => AvatarAddress; - int ICombinationEquipmentV1.RecipeId => RecipeId; - int ICombinationEquipmentV1.SlotIndex => SlotIndex; - int? ICombinationEquipmentV1.SubRecipeId => SubRecipeId; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}"); - } - - var recipeSheet = states.GetSheet(); - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - - // 레시피 검증 - if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId); - } - - if (!(SubRecipeId is null)) - { - if (!recipe.SubRecipeIds.Contains((int) SubRecipeId)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet." - ); - } - } - - // 메인 레시피 해금 검사. - if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current); - } - - if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId); - } - - if (!avatarState.inventory.RemoveFungibleItem2(material.ItemId, recipe.MaterialCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount})" - ); - } - - var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); - materials[equipmentMaterial] = recipe.MaterialCount; - - BigInteger requiredGold = recipe.RequiredGold; - var requiredActionPoint = recipe.RequiredActionPoint; - var equipmentItemSheet = states.GetSheet(); - - // 장비 제작 - if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId); - } - - var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - - // 서브 레시피 검증 - HashSet optionIds = null; - if (SubRecipeId.HasValue) - { - var subSheet = states.GetSheet(); - var subId = (int) SubRecipeId; - if (!subSheet.TryGetValue(subId, out var subRecipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId); - } - - requiredBlockIndex += subRecipe.RequiredBlockIndex; - requiredGold += subRecipe.RequiredGold; - requiredActionPoint += subRecipe.RequiredActionPoint; - - foreach (var materialInfo in subRecipe.Materials) - { - if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id); - } - - if (!avatarState.inventory.RemoveFungibleItem2(subMaterialRow.ItemId, - materialInfo.Count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count})" - ); - } - - var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); - materials[subMaterial] = materialInfo.Count; - } - - optionIds = SelectOption(states.GetSheet(), states.GetSheet(), - subRecipe, ctx.Random, equipment); - equipment.Update(requiredBlockIndex); - } - - // 자원 검증 - FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - if (agentBalance < (states.GetGoldCurrency() * requiredGold) || avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } - - avatarState.actionPoint -= requiredActionPoint; - if (!(optionIds is null)) - { - foreach (var id in optionIds.OrderBy(id => id)) - { - agentState.unlockedOptions.Add(id); - } - } - - // FIXME: BlacksmithAddress 계좌로 돈이 쌓이기만 하는데 이걸 어떻게 순환시킬지 기획이 필요. - if (requiredGold > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredGold - ); - } - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = requiredActionPoint, - gold = requiredGold, - materials = materials, - itemUsable = equipment, - recipeId = RecipeId, - subRecipeId = SubRecipeId, - itemType = ItemType.Equipment, - }; - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - requiredBlockIndex); - result.id = mail.id; - avatarState.Update2(mail); - avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(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; - } - - return states - .SetState(AvatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(ctx.Signer, agentState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["recipeId"] = RecipeId.Serialize(), - ["subRecipeId"] = SubRecipeId.Serialize(), - ["slotIndex"] = SlotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - RecipeId = plainValue["recipeId"].ToInteger(); - SubRecipeId = plainValue["subRecipeId"].ToNullableInteger(); - SlotIndex = plainValue["slotIndex"].ToInteger(); - } - - public static DecimalStat GetStat(EquipmentItemOptionSheet.Row row, IRandom random) - { - var value = random.Next(row.StatMin, row.StatMax + 1); - return new DecimalStat(row.StatType, value); - } - - public static Skill GetSkill(EquipmentItemOptionSheet.Row row, SkillSheet skillSheet, - IRandom random) - { - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == row.SkillId); - var dmg = random.Next(row.SkillDamageMin, row.SkillDamageMax + 1); - var chance = random.Next(row.SkillChanceMin, row.SkillChanceMax + 1); - var skill = SkillFactory.GetV1(skillRow, dmg, chance); - return skill; - } - catch (InvalidOperationException) - { - return null; - } - } - - public static HashSet SelectOption( - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet, - EquipmentItemSubRecipeSheet.Row subRecipe, - IRandom random, - Equipment equipment - ) - { - var optionSelector = new WeightedSelector(random); - var optionIds = new HashSet(); - - // Skip sort subRecipe.Options because it had been already sorted in WeightedSelector.Select(); - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - optionSelector.Add(optionRow, optionInfo.Ratio); - } - - IEnumerable optionRows = - new EquipmentItemOptionSheet.Row[0]; - try - { - optionRows = optionSelector.SelectV1(subRecipe.MaxOptionLimit); - } - catch (Exception e) when ( - e is InvalidCountException || - e is ListEmptyException - ) - { - return optionIds; - } - finally - { - foreach (var optionRow in optionRows.OrderBy(r => r.Id)) - { - if (optionRow.StatType != StatType.NONE) - { - var statMap = GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(statMap.StatType, statMap.BaseValue); - } - else - { - var skill = GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - } - } - - optionIds.Add(optionRow.Id); - } - } - - return optionIds; - } - } -} diff --git a/Lib9c/Action/CombinationEquipment10.cs b/Lib9c/Action/CombinationEquipment10.cs deleted file mode 100644 index 6e39365ad4..0000000000 --- a/Lib9c/Action/CombinationEquipment10.cs +++ /dev/null @@ -1,390 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment10")] - public class CombinationEquipment10 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => avatarAddress; - int ICombinationEquipmentV1.RecipeId => recipeId; - int ICombinationEquipmentV1.SlotIndex => slotIndex; - int? ICombinationEquipmentV1.SubRecipeId => subRecipeId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(context.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100220ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNCG = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - // Validate RecipeId - var equipmentItemRecipeSheet = states.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = states.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = states.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = states.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNCG += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNCG += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - // Remove Required Materials - var inventory = avatarState.inventory; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - if (!materialItemSheet.TryGetValue(pair.Key, out materialRow) || - !inventory.RemoveFungibleItem(materialRow.ItemId, context.BlockIndex, pair.Value)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - // ~Remove Required Materials - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNCG > 0L) - { - states = states.TransferAsset( - context, - context.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * costNCG - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - states.GetSheet(), - states.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNCG, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment11.cs b/Lib9c/Action/CombinationEquipment11.cs deleted file mode 100644 index 6fa7a039ab..0000000000 --- a/Lib9c/Action/CombinationEquipment11.cs +++ /dev/null @@ -1,405 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Updated at https://github.com/planetarium/lib9c/pull/1176 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment11")] - public class CombinationEquipment11 : GameAction, ICombinationEquipmentV1 - { - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => avatarAddress; - int ICombinationEquipmentV1.RecipeId => recipeId; - int ICombinationEquipmentV1.SlotIndex => slotIndex; - int? ICombinationEquipmentV1.SubRecipeId => subRecipeId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(context.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, ItemEnhancement10.GetFeeStoreAddress()); - } - - CheckObsolete(ActionObsoleteConfig.V100270ObsoleteIndex, context); - - var arenaSheetAddress = Addresses.GetSheetAddress(); - var arenaSheetState = states.GetState(arenaSheetAddress); - if (arenaSheetState != null) - { - // exception handling for v100240. - if (context.BlockIndex > 4374131 && context.BlockIndex < 4374214) - { - } - else - { - throw new ActionObsoletedException(nameof(CombinationEquipment11)); - } - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNCG = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - // Validate RecipeId - var equipmentItemRecipeSheet = states.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = states.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = states.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = states.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNCG += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNCG += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - // Remove Required Materials - var inventory = avatarState.inventory; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - if (!materialItemSheet.TryGetValue(pair.Key, out materialRow) || - !inventory.RemoveFungibleItem(materialRow.ItemId, context.BlockIndex, pair.Value)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - // ~Remove Required Materials - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNCG > 0L) - { - states = states.TransferAsset( - context, - context.Signer, - ItemEnhancement10.GetFeeStoreAddress(), - states.GetGoldCurrency() * costNCG - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - states.GetSheet(), - states.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNCG, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment12.cs b/Lib9c/Action/CombinationEquipment12.cs deleted file mode 100644 index 347d970baa..0000000000 --- a/Lib9c/Action/CombinationEquipment12.cs +++ /dev/null @@ -1,471 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Updated at https://github.com/planetarium/lib9c/pull/1164 - /// - [Serializable] - [ActionType("combination_equipment12")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class CombinationEquipment12 : GameAction, ICombinationEquipmentV2 - { - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - public const string PayByCrystalKey = "p"; - public bool payByCrystal; - - Address ICombinationEquipmentV2.AvatarAddress => avatarAddress; - int ICombinationEquipmentV2.RecipeId => recipeId; - int ICombinationEquipmentV2.SlotIndex => slotIndex; - int? ICombinationEquipmentV2.SubRecipeId => subRecipeId; - bool ICombinationEquipmentV2.PayByCrystal => payByCrystal; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - [PayByCrystalKey] = payByCrystal.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - payByCrystal = plainValue[PayByCrystalKey].ToBoolean(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - - if (recipeId != 1) - { - var unlockedRecipeIdsAddress = avatarAddress.Derive("recipe_ids"); - if (!states.TryGetState(unlockedRecipeIdsAddress, out List rawIds)) - { - throw new FailedLoadStateException("can't find UnlockedRecipeList."); - } - - List unlockedIds = rawIds.ToList(StateExtensions.ToInteger); - if (!unlockedIds.Contains(recipeId)) - { - throw new InvalidRecipeIdException($"unlock {recipeId} first."); - } - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Tutorial Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Tutorial Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNCG = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - var costCrystal = 0 * CrystalCalculator.CRYSTAL; - - Dictionary sheets = states.GetSheets(sheetTypes: new[] - { - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(SkillSheet), - typeof(CrystalMaterialCostSheet), - typeof(CrystalFluctuationSheet), - }); - - // Validate RecipeId - var equipmentItemRecipeSheet = sheets.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = sheets.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = sheets.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = sheets.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNCG += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNCG += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - // Remove Required Materials - var inventory = avatarState.inventory; - var crystalMaterialSheet = sheets.GetSheet(); - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - var itemId = pair.Key; - var requiredCount = pair.Value; - if (materialItemSheet.TryGetValue(itemId, out materialRow)) - { - int itemCount = inventory.TryGetItem(itemId, out Inventory.Item item) - ? item.count - : 0; - if (itemCount < requiredCount && payByCrystal) - { - costCrystal += CrystalCalculator.CalculateMaterialCost( - itemId, - requiredCount - itemCount, - crystalMaterialSheet); - requiredCount = itemCount; - } - - if (requiredCount > 0 && !inventory.RemoveFungibleItem(materialRow.ItemId, context.BlockIndex, - requiredCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - else - { - throw new SheetRowNotFoundException(nameof(MaterialItemSheet), itemId); - } - } - // ~Remove Required Materials - if (costCrystal > 0 * CrystalCalculator.CRYSTAL) - { - var crystalFluctuationSheet = sheets.GetSheet(); - var row = crystalFluctuationSheet.Values - .First(r => r.Type == CrystalFluctuationSheet.ServiceType.Combination); - var (dailyCostState, weeklyCostState, _, _) = states.GetCrystalCostStates(context.BlockIndex, row.BlockInterval); - // 1x fixed crystal cost. - costCrystal = CrystalCalculator.CalculateCombinationCost(costCrystal, row: row, prevWeeklyCostState: null, beforePrevWeeklyCostState: null); - // Update Daily Formula. - dailyCostState.Count++; - dailyCostState.CRYSTAL += costCrystal; - // Update Weekly Formula. - weeklyCostState.Count++; - weeklyCostState.CRYSTAL += costCrystal; - - var crystalBalance = states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - if (costCrystal > crystalBalance) - { - throw new NotEnoughFungibleAssetValueException($"required {costCrystal}, but balance is {crystalBalance}"); - } - - states = states - .SetState(dailyCostState.Address, dailyCostState.Serialize()) - .SetState(weeklyCostState.Address, weeklyCostState.Serialize()) - .TransferAsset(context, context.Signer, Addresses.MaterialCost, costCrystal); - } - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNCG > 0L) - { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - - states = states.TransferAsset( - context, - context.Signer, - feeStoreAddress, - states.GetGoldCurrency() * costNCG - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNCG, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment13.cs b/Lib9c/Action/CombinationEquipment13.cs deleted file mode 100644 index b9e3110104..0000000000 --- a/Lib9c/Action/CombinationEquipment13.cs +++ /dev/null @@ -1,656 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1264 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment13")] - public class CombinationEquipment13 : GameAction, ICombinationEquipmentV3 - { - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - public const string PayByCrystalKey = "p"; - public bool payByCrystal; - public const string UseHammerPointKey = "h"; - public bool useHammerPoint; - public const int BasicSubRecipeHammerPoint = 1; - public const int SpecialSubRecipeHammerPoint = 2; - - Address ICombinationEquipmentV3.AvatarAddress => avatarAddress; - int ICombinationEquipmentV3.RecipeId => recipeId; - int ICombinationEquipmentV3.SlotIndex => slotIndex; - int? ICombinationEquipmentV3.SubRecipeId => subRecipeId; - bool ICombinationEquipmentV3.PayByCrystal => payByCrystal; - bool ICombinationEquipmentV3.UseHammerPoint => useHammerPoint; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - [PayByCrystalKey] = payByCrystal.Serialize(), - [UseHammerPointKey] = useHammerPoint.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - payByCrystal = plainValue[PayByCrystalKey].ToBoolean(); - useHammerPoint = plainValue[UseHammerPointKey].ToBoolean(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100282ObsoleteIndex, context); - - if (recipeId != 1) - { - var unlockedRecipeIdsAddress = avatarAddress.Derive("recipe_ids"); - if (!states.TryGetState(unlockedRecipeIdsAddress, out List rawIds)) - { - throw new FailedLoadStateException("can't find UnlockedRecipeList."); - } - - List unlockedIds = rawIds.ToList(StateExtensions.ToInteger); - if (!unlockedIds.Contains(recipeId)) - { - throw new InvalidRecipeIdException($"unlock {recipeId} first."); - } - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Tutorial Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Tutorial Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNcg = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - Dictionary sheets = states.GetSheets(sheetTypes: new[] - { - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(SkillSheet), - typeof(CrystalMaterialCostSheet), - typeof(CrystalFluctuationSheet), - typeof(CrystalHammerPointSheet), - typeof(ConsumableItemRecipeSheet), - }); - - // Validate RecipeId - var equipmentItemRecipeSheet = sheets.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = sheets.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = sheets.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = sheets.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNcg += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNcg += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - var existHammerPointSheet = - sheets.TryGetSheet(out CrystalHammerPointSheet hammerPointSheet); - var hammerPointAddress = - Addresses.GetHammerPointStateAddress(avatarAddress, recipeId); - var hammerPointState = new HammerPointState(hammerPointAddress, recipeId); - CrystalHammerPointSheet.Row hammerPointRow = null; - if (existHammerPointSheet) - { - if (states.TryGetState(hammerPointAddress, out List serialized)) - { - hammerPointState = - new HammerPointState(hammerPointAddress, serialized); - } - - // Validate HammerPointSheet by recipeId - if (!hammerPointSheet.TryGetValue(recipeId, out hammerPointRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(CrystalHammerPointSheet), - recipeId); - } - } - - if (useHammerPoint) - { - if (!existHammerPointSheet) - { - throw new FailedLoadSheetException(typeof(CrystalHammerPointSheet)); - } - - if (recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)) - { - throw new ArgumentException( - $"Can not super craft with mimisbrunnr recipe. Subrecipe id: {subRecipeId}"); - } - - states = UseAssetsBySuperCraft( - states, - context, - hammerPointRow, - hammerPointState); - } - else - { - states = UseAssetsByNormalCombination( - states, - context, - avatarState, - hammerPointState, - sheets, - materialItemSheet, - hammerPointSheet, - recipeRow, - requiredFungibleItems, - addressesHex); - } - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNcg > 0L) - { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - - states = states.TransferAsset( - context, - context.Signer, - feeStoreAddress, - states.GetGoldCurrency() * costNcg - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - - if (useHammerPoint) - { - if (!equipment.Skills.Any()) - { - AddSkillOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - } - - var firstFoodRow = sheets.GetSheet() - .First; - if (firstFoodRow is null) - { - throw new SheetRowNotFoundException( - $"{nameof(ConsumableItemRecipeSheet)}'s first row is null.", 0); - } - - endBlockIndex = equipment.RequiredBlockIndex = - context.BlockIndex + firstFoodRow.RequiredBlockIndex; - } - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNcg, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(hammerPointAddress,hammerPointState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - private IAccountStateDelta UseAssetsBySuperCraft( - IAccountStateDelta states, - IActionContext context, - CrystalHammerPointSheet.Row row, - HammerPointState hammerPointState) - { - var crystalBalance = states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - var hammerPointCost = CrystalCalculator.CRYSTAL * row.CRYSTAL; - if (crystalBalance < hammerPointCost) - { - throw new NotEnoughFungibleAssetValueException($"required {hammerPointCost}, but balance is {crystalBalance}"); - } - - hammerPointState.ResetHammerPoint(); - return states.TransferAsset( - context, - context.Signer, - Addresses.SuperCraft, - hammerPointCost); - } - - private IAccountStateDelta UseAssetsByNormalCombination( - IAccountStateDelta states, - IActionContext context, - AvatarState avatarState, - HammerPointState hammerPointState, - Dictionary sheets, - MaterialItemSheet materialItemSheet, - CrystalHammerPointSheet hammerPointSheet, - EquipmentItemRecipeSheet.Row recipeRow, - Dictionary requiredFungibleItems, - string addressesHex) - { - // Remove Required Materials - var inventory = avatarState.inventory; - var crystalMaterialSheet = sheets.GetSheet(); - var costCrystal = CrystalCalculator.CRYSTAL * 0; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - var itemId = pair.Key; - var requiredCount = pair.Value; - if (materialItemSheet.TryGetValue(itemId, out var materialRow)) - { - int itemCount = inventory.TryGetItem(itemId, out Inventory.Item item) - ? item.count - : 0; - if (itemCount < requiredCount && payByCrystal) - { - costCrystal += CrystalCalculator.CalculateMaterialCost( - itemId, - requiredCount - itemCount, - crystalMaterialSheet); - requiredCount = itemCount; - } - - if (requiredCount > 0 && !inventory.RemoveFungibleItem(materialRow.ItemId, - context.BlockIndex, - requiredCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - else - { - throw new SheetRowNotFoundException(nameof(MaterialItemSheet), itemId); - } - } - // ~Remove Required Materials - - if (costCrystal > 0 * CrystalCalculator.CRYSTAL) - { - var crystalFluctuationSheet = sheets.GetSheet(); - var row = crystalFluctuationSheet.Values - .First(r => r.Type == CrystalFluctuationSheet.ServiceType.Combination); - var (dailyCostState, weeklyCostState, _, _) = - states.GetCrystalCostStates(context.BlockIndex, row.BlockInterval); - // 1x fixed crystal cost. - costCrystal = CrystalCalculator.CalculateCombinationCost( - costCrystal, - row: row, - prevWeeklyCostState: null, - beforePrevWeeklyCostState: null); - // Update Daily Formula. - dailyCostState.Count++; - dailyCostState.CRYSTAL += costCrystal; - // Update Weekly Formula. - weeklyCostState.Count++; - weeklyCostState.CRYSTAL += costCrystal; - - var crystalBalance = - states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - if (costCrystal > crystalBalance) - { - throw new NotEnoughFungibleAssetValueException( - $"required {costCrystal}, but balance is {crystalBalance}"); - } - - states = states - .SetState(dailyCostState.Address, dailyCostState.Serialize()) - .SetState(weeklyCostState.Address, weeklyCostState.Serialize()) - .TransferAsset(context, context.Signer, Addresses.MaterialCost, costCrystal); - } - - var isBasicSubRecipe = !subRecipeId.HasValue || - recipeRow.SubRecipeIds[0] == subRecipeId.Value; - - hammerPointState.AddHammerPoint( - isBasicSubRecipe ? BasicSubRecipeHammerPoint : SpecialSubRecipeHammerPoint, - hammerPointSheet); - return states; - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - - public static void AddSkillOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - Skill skill; - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == optionRow.SkillId); - var dmg = random.Next(optionRow.SkillDamageMin, optionRow.SkillDamageMax + 1); - var chance = random.Next(optionRow.SkillChanceMin, optionRow.SkillChanceMax + 1); - skill = SkillFactory.GetV1(skillRow, dmg, chance); - } - catch (InvalidOperationException) - { - continue; - } - - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment14.cs b/Lib9c/Action/CombinationEquipment14.cs deleted file mode 100644 index 4c7624b9ce..0000000000 --- a/Lib9c/Action/CombinationEquipment14.cs +++ /dev/null @@ -1,666 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1334 - /// - [Serializable] - [ActionType("combination_equipment14")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class CombinationEquipment14 : GameAction, ICombinationEquipmentV3 - { - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - public const string PayByCrystalKey = "p"; - public bool payByCrystal; - public const string UseHammerPointKey = "h"; - public bool useHammerPoint; - public const int BasicSubRecipeHammerPoint = 1; - public const int SpecialSubRecipeHammerPoint = 2; - - Address ICombinationEquipmentV3.AvatarAddress => avatarAddress; - int ICombinationEquipmentV3.RecipeId => recipeId; - int ICombinationEquipmentV3.SlotIndex => slotIndex; - int? ICombinationEquipmentV3.SubRecipeId => subRecipeId; - bool ICombinationEquipmentV3.PayByCrystal => payByCrystal; - bool ICombinationEquipmentV3.UseHammerPoint => useHammerPoint; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - [PayByCrystalKey] = payByCrystal.Serialize(), - [UseHammerPointKey] = useHammerPoint.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - payByCrystal = plainValue[PayByCrystalKey].ToBoolean(); - useHammerPoint = plainValue[UseHammerPointKey].ToBoolean(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CombinationEquipment exec started", addressesHex); - if (recipeId != 1) - { - var unlockedRecipeIdsAddress = avatarAddress.Derive("recipe_ids"); - if (!states.TryGetState(unlockedRecipeIdsAddress, out List rawIds)) - { - throw new FailedLoadStateException("can't find UnlockedRecipeList."); - } - - List unlockedIds = rawIds.ToList(StateExtensions.ToInteger); - if (!unlockedIds.Contains(recipeId)) - { - throw new InvalidRecipeIdException($"unlock {recipeId} first."); - } - } - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Tutorial Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Tutorial Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNcg = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - Dictionary sheets = states.GetSheets(sheetTypes: new[] - { - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(SkillSheet), - typeof(CrystalMaterialCostSheet), - typeof(CrystalFluctuationSheet), - typeof(CrystalHammerPointSheet), - typeof(ConsumableItemRecipeSheet), - }); - - // Validate RecipeId - var equipmentItemRecipeSheet = sheets.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = sheets.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = sheets.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = sheets.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNcg += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNcg += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - var existHammerPointSheet = - sheets.TryGetSheet(out CrystalHammerPointSheet hammerPointSheet); - var hammerPointAddress = - Addresses.GetHammerPointStateAddress(avatarAddress, recipeId); - var hammerPointState = new HammerPointState(hammerPointAddress, recipeId); - CrystalHammerPointSheet.Row hammerPointRow = null; - if (existHammerPointSheet) - { - if (states.TryGetState(hammerPointAddress, out List serialized)) - { - hammerPointState = - new HammerPointState(hammerPointAddress, serialized); - } - - // Validate HammerPointSheet by recipeId - if (!hammerPointSheet.TryGetValue(recipeId, out hammerPointRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(CrystalHammerPointSheet), - recipeId); - } - } - - if (useHammerPoint) - { - if (!existHammerPointSheet) - { - throw new FailedLoadSheetException(typeof(CrystalHammerPointSheet)); - } - - if (recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)) - { - throw new ArgumentException( - $"Can not super craft with mimisbrunnr recipe. Subrecipe id: {subRecipeId}"); - } - - if (hammerPointState.HammerPoint < hammerPointRow.MaxPoint) - { - throw new NotEnoughHammerPointException( - $"Not enough hammer points. Need : {hammerPointRow.MaxPoint}, own : {hammerPointState.HammerPoint}"); - } - - states = UseAssetsBySuperCraft( - states, - context, - hammerPointRow, - hammerPointState); - } - else - { - states = UseAssetsByNormalCombination( - states, - context, - avatarState, - hammerPointState, - sheets, - materialItemSheet, - hammerPointSheet, - recipeRow, - requiredFungibleItems, - addressesHex); - } - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNcg > 0L) - { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - - states = states.TransferAsset( - context, - context.Signer, - feeStoreAddress, - states.GetGoldCurrency() * costNcg - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - - if (useHammerPoint) - { - if (!equipment.Skills.Any()) - { - AddSkillOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - } - - var firstFoodRow = sheets.GetSheet() - .First; - if (firstFoodRow is null) - { - throw new SheetRowNotFoundException( - $"{nameof(ConsumableItemRecipeSheet)}'s first row is null.", 0); - } - - endBlockIndex = equipment.RequiredBlockIndex = - context.BlockIndex + firstFoodRow.RequiredBlockIndex; - } - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNcg, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CombinationEquipment Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(hammerPointAddress,hammerPointState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - private IAccountStateDelta UseAssetsBySuperCraft( - IAccountStateDelta states, - IActionContext context, - CrystalHammerPointSheet.Row row, - HammerPointState hammerPointState) - { - var crystalBalance = states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - var hammerPointCost = CrystalCalculator.CRYSTAL * row.CRYSTAL; - if (crystalBalance < hammerPointCost) - { - throw new NotEnoughFungibleAssetValueException($"required {hammerPointCost}, but balance is {crystalBalance}"); - } - - hammerPointState.ResetHammerPoint(); - return states.TransferAsset( - context, - context.Signer, - Addresses.SuperCraft, - hammerPointCost); - } - - private IAccountStateDelta UseAssetsByNormalCombination( - IAccountStateDelta states, - IActionContext context, - AvatarState avatarState, - HammerPointState hammerPointState, - Dictionary sheets, - MaterialItemSheet materialItemSheet, - CrystalHammerPointSheet hammerPointSheet, - EquipmentItemRecipeSheet.Row recipeRow, - Dictionary requiredFungibleItems, - string addressesHex) - { - // Remove Required Materials - var inventory = avatarState.inventory; - var crystalMaterialSheet = sheets.GetSheet(); - var costCrystal = CrystalCalculator.CRYSTAL * 0; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - var itemId = pair.Key; - var requiredCount = pair.Value; - if (materialItemSheet.TryGetValue(itemId, out var materialRow)) - { - int itemCount = inventory.TryGetItem(itemId, out Inventory.Item item) - ? item.count - : 0; - if (itemCount < requiredCount && payByCrystal) - { - costCrystal += CrystalCalculator.CalculateMaterialCost( - itemId, - requiredCount - itemCount, - crystalMaterialSheet); - requiredCount = itemCount; - } - - if (requiredCount > 0 && !inventory.RemoveFungibleItem(materialRow.ItemId, - context.BlockIndex, - requiredCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - else - { - throw new SheetRowNotFoundException(nameof(MaterialItemSheet), itemId); - } - } - // ~Remove Required Materials - - if (costCrystal > 0 * CrystalCalculator.CRYSTAL) - { - var crystalFluctuationSheet = sheets.GetSheet(); - var row = crystalFluctuationSheet.Values - .First(r => r.Type == CrystalFluctuationSheet.ServiceType.Combination); - var (dailyCostState, weeklyCostState, _, _) = - states.GetCrystalCostStates(context.BlockIndex, row.BlockInterval); - // 1x fixed crystal cost. - costCrystal = CrystalCalculator.CalculateCombinationCost( - costCrystal, - row: row, - prevWeeklyCostState: null, - beforePrevWeeklyCostState: null); - // Update Daily Formula. - dailyCostState.Count++; - dailyCostState.CRYSTAL += costCrystal; - // Update Weekly Formula. - weeklyCostState.Count++; - weeklyCostState.CRYSTAL += costCrystal; - - var crystalBalance = - states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - if (costCrystal > crystalBalance) - { - throw new NotEnoughFungibleAssetValueException( - $"required {costCrystal}, but balance is {crystalBalance}"); - } - - states = states - .SetState(dailyCostState.Address, dailyCostState.Serialize()) - .SetState(weeklyCostState.Address, weeklyCostState.Serialize()) - .TransferAsset(context, context.Signer, Addresses.MaterialCost, costCrystal); - } - - var isBasicSubRecipe = !subRecipeId.HasValue || - recipeRow.SubRecipeIds[0] == subRecipeId.Value; - - hammerPointState.AddHammerPoint( - isBasicSubRecipe ? BasicSubRecipeHammerPoint : SpecialSubRecipeHammerPoint, - hammerPointSheet); - return states; - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - - public static void AddSkillOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - Skill skill; - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == optionRow.SkillId); - var dmg = random.Next(optionRow.SkillDamageMin, optionRow.SkillDamageMax + 1); - var chance = random.Next(optionRow.SkillChanceMin, optionRow.SkillChanceMax + 1); - skill = SkillFactory.GetV1(skillRow, dmg, chance); - } - catch (InvalidOperationException) - { - continue; - } - - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment15.cs b/Lib9c/Action/CombinationEquipment15.cs deleted file mode 100644 index 75e9efcd7e..0000000000 --- a/Lib9c/Action/CombinationEquipment15.cs +++ /dev/null @@ -1,667 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1749 - /// - [Serializable] - [ActionType("combination_equipment15")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class CombinationEquipment15 : GameAction, ICombinationEquipmentV3 - { - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - public const string PayByCrystalKey = "p"; - public bool payByCrystal; - public const string UseHammerPointKey = "h"; - public bool useHammerPoint; - public const int BasicSubRecipeHammerPoint = 1; - public const int SpecialSubRecipeHammerPoint = 2; - - Address ICombinationEquipmentV3.AvatarAddress => avatarAddress; - int ICombinationEquipmentV3.RecipeId => recipeId; - int ICombinationEquipmentV3.SlotIndex => slotIndex; - int? ICombinationEquipmentV3.SubRecipeId => subRecipeId; - bool ICombinationEquipmentV3.PayByCrystal => payByCrystal; - bool ICombinationEquipmentV3.UseHammerPoint => useHammerPoint; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - [PayByCrystalKey] = payByCrystal.Serialize(), - [UseHammerPointKey] = useHammerPoint.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - payByCrystal = plainValue[PayByCrystalKey].ToBoolean(); - useHammerPoint = plainValue[UseHammerPointKey].ToBoolean(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CombinationEquipment exec started", addressesHex); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Tutorial Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Tutorial Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNcg = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - Dictionary sheets = states.GetSheets(sheetTypes: new[] - { - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSheet), - typeof(MaterialItemSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(SkillSheet), - typeof(CrystalMaterialCostSheet), - typeof(CrystalFluctuationSheet), - typeof(CrystalHammerPointSheet), - typeof(ConsumableItemRecipeSheet), - }); - - // Validate RecipeId - var equipmentItemRecipeSheet = sheets.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = sheets.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = sheets.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate Recipe Unlocked. - if (equipmentItemRecipeSheet[recipeId].CRYSTAL != 0) - { - var unlockedRecipeIdsAddress = avatarAddress.Derive("recipe_ids"); - if (!states.TryGetState(unlockedRecipeIdsAddress, out List rawIds)) - { - throw new FailedLoadStateException("can't find UnlockedRecipeList."); - } - - var unlockedIds = rawIds.ToList(StateExtensions.ToInteger); - if (!unlockedIds.Contains(recipeId)) - { - throw new InvalidRecipeIdException($"unlock {recipeId} first."); - } - - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - } - // ~Validate Recipe Unlocked - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = sheets.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNcg += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNcg += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - var existHammerPointSheet = - sheets.TryGetSheet(out CrystalHammerPointSheet hammerPointSheet); - var hammerPointAddress = - Addresses.GetHammerPointStateAddress(avatarAddress, recipeId); - var hammerPointState = new HammerPointState(hammerPointAddress, recipeId); - CrystalHammerPointSheet.Row hammerPointRow = null; - if (existHammerPointSheet) - { - if (states.TryGetState(hammerPointAddress, out List serialized)) - { - hammerPointState = - new HammerPointState(hammerPointAddress, serialized); - } - - // Validate HammerPointSheet by recipeId - if (!hammerPointSheet.TryGetValue(recipeId, out hammerPointRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(CrystalHammerPointSheet), - recipeId); - } - } - - if (useHammerPoint) - { - if (!existHammerPointSheet) - { - throw new FailedLoadSheetException(typeof(CrystalHammerPointSheet)); - } - - if (recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)) - { - throw new ArgumentException( - $"Can not super craft with mimisbrunnr recipe. Subrecipe id: {subRecipeId}"); - } - - if (hammerPointState.HammerPoint < hammerPointRow.MaxPoint) - { - throw new NotEnoughHammerPointException( - $"Not enough hammer points. Need : {hammerPointRow.MaxPoint}, own : {hammerPointState.HammerPoint}"); - } - - states = UseAssetsBySuperCraft( - states, - context, - hammerPointRow, - hammerPointState); - } - else - { - states = UseAssetsByNormalCombination( - states, - context, - avatarState, - hammerPointState, - sheets, - materialItemSheet, - hammerPointSheet, - recipeRow, - requiredFungibleItems, - addressesHex); - } - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNcg > 0L) - { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - - states = states.TransferAsset( - context, - context.Signer, - feeStoreAddress, - states.GetGoldCurrency() * costNcg - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment)ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - - if (useHammerPoint) - { - if (!equipment.Skills.Any()) - { - AddSkillOption( - agentState, - equipment, - context.Random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - } - - var firstFoodRow = sheets.GetSheet() - .First; - if (firstFoodRow is null) - { - throw new SheetRowNotFoundException( - $"{nameof(ConsumableItemRecipeSheet)}'s first row is null.", 0); - } - - endBlockIndex = equipment.RequiredBlockIndex = - context.BlockIndex + firstFoodRow.RequiredBlockIndex; - } - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNcg, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CombinationEquipment Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(hammerPointAddress, hammerPointState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - private IAccountStateDelta UseAssetsBySuperCraft( - IAccountStateDelta states, - IActionContext context, - CrystalHammerPointSheet.Row row, - HammerPointState hammerPointState) - { - var crystalBalance = states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - var hammerPointCost = CrystalCalculator.CRYSTAL * row.CRYSTAL; - if (crystalBalance < hammerPointCost) - { - throw new NotEnoughFungibleAssetValueException($"required {hammerPointCost}, but balance is {crystalBalance}"); - } - - hammerPointState.ResetHammerPoint(); - return states.TransferAsset( - context, - context.Signer, - Addresses.SuperCraft, - hammerPointCost); - } - - private IAccountStateDelta UseAssetsByNormalCombination( - IAccountStateDelta states, - IActionContext context, - AvatarState avatarState, - HammerPointState hammerPointState, - Dictionary sheets, - MaterialItemSheet materialItemSheet, - CrystalHammerPointSheet hammerPointSheet, - EquipmentItemRecipeSheet.Row recipeRow, - Dictionary requiredFungibleItems, - string addressesHex) - { - // Remove Required Materials - var inventory = avatarState.inventory; - var crystalMaterialSheet = sheets.GetSheet(); - var costCrystal = CrystalCalculator.CRYSTAL * 0; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - var itemId = pair.Key; - var requiredCount = pair.Value; - if (materialItemSheet.TryGetValue(itemId, out var materialRow)) - { - int itemCount = inventory.TryGetItem(itemId, out Inventory.Item item) - ? item.count - : 0; - if (itemCount < requiredCount && payByCrystal) - { - costCrystal += CrystalCalculator.CalculateMaterialCost( - itemId, - requiredCount - itemCount, - crystalMaterialSheet); - requiredCount = itemCount; - } - - if (requiredCount > 0 && !inventory.RemoveFungibleItem(materialRow.ItemId, - context.BlockIndex, - requiredCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - else - { - throw new SheetRowNotFoundException(nameof(MaterialItemSheet), itemId); - } - } - // ~Remove Required Materials - - if (costCrystal > 0 * CrystalCalculator.CRYSTAL) - { - var crystalFluctuationSheet = sheets.GetSheet(); - var row = crystalFluctuationSheet.Values - .First(r => r.Type == CrystalFluctuationSheet.ServiceType.Combination); - var (dailyCostState, weeklyCostState, _, _) = - states.GetCrystalCostStates(context.BlockIndex, row.BlockInterval); - // 1x fixed crystal cost. - costCrystal = CrystalCalculator.CalculateCombinationCost( - costCrystal, - row: row, - prevWeeklyCostState: null, - beforePrevWeeklyCostState: null); - // Update Daily Formula. - dailyCostState.Count++; - dailyCostState.CRYSTAL += costCrystal; - // Update Weekly Formula. - weeklyCostState.Count++; - weeklyCostState.CRYSTAL += costCrystal; - - var crystalBalance = - states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - if (costCrystal > crystalBalance) - { - throw new NotEnoughFungibleAssetValueException( - $"required {costCrystal}, but balance is {crystalBalance}"); - } - - states = states - .SetState(dailyCostState.Address, dailyCostState.Serialize()) - .SetState(weeklyCostState.Address, weeklyCostState.Serialize()) - .TransferAsset(context, context.Signer, Addresses.MaterialCost, costCrystal); - } - - var isBasicSubRecipe = !subRecipeId.HasValue || - recipeRow.SubRecipeIds[0] == subRecipeId.Value; - - hammerPointState.AddHammerPoint( - isBasicSubRecipe ? BasicSubRecipeHammerPoint : SpecialSubRecipeHammerPoint, - hammerPointSheet); - return states; - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - - public static void AddSkillOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - Skill skill; - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == optionRow.SkillId); - var dmg = random.Next(optionRow.SkillDamageMin, optionRow.SkillDamageMax + 1); - var chance = random.Next(optionRow.SkillChanceMin, optionRow.SkillChanceMax + 1); - skill = SkillFactory.GetV1(skillRow, dmg, chance); - } - catch (InvalidOperationException) - { - continue; - } - - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment2.cs b/Lib9c/Action/CombinationEquipment2.cs deleted file mode 100644 index 6db7091743..0000000000 --- a/Lib9c/Action/CombinationEquipment2.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment2")] - public class CombinationEquipment2 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public Address AvatarAddress; - public int RecipeId; - public int SlotIndex; - public int? SubRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => AvatarAddress; - int ICombinationEquipmentV1.RecipeId => RecipeId; - int ICombinationEquipmentV1.SlotIndex => SlotIndex; - int? ICombinationEquipmentV1.SubRecipeId => SubRecipeId; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}"); - } - - var recipeSheet = states.GetSheet(); - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - - // 레시피 검증 - if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId); - } - - if (!(SubRecipeId is null)) - { - if (!recipe.SubRecipeIds.Contains((int) SubRecipeId)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet." - ); - } - } - - // 메인 레시피 해금 검사. - if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current); - } - - if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId); - } - - if (!avatarState.inventory.RemoveFungibleItem2(material.ItemId, recipe.MaterialCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount})" - ); - } - - var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); - materials[equipmentMaterial] = recipe.MaterialCount; - - BigInteger requiredGold = recipe.RequiredGold; - var requiredActionPoint = recipe.RequiredActionPoint; - var equipmentItemSheet = states.GetSheet(); - - // 장비 제작 - if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId); - } - - var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - - // 서브 레시피 검증 - HashSet optionIds = null; - if (SubRecipeId.HasValue) - { - var subSheet = states.GetSheet(); - var subId = (int) SubRecipeId; - if (!subSheet.TryGetValue(subId, out var subRecipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId); - } - - requiredBlockIndex += subRecipe.RequiredBlockIndex; - requiredGold += subRecipe.RequiredGold; - requiredActionPoint += subRecipe.RequiredActionPoint; - - foreach (var materialInfo in subRecipe.Materials) - { - if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id); - } - - if (!avatarState.inventory.RemoveFungibleItem2(subMaterialRow.ItemId, - materialInfo.Count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count})" - ); - } - - var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); - materials[subMaterial] = materialInfo.Count; - } - - optionIds = SelectOption(states.GetSheet(), states.GetSheet(), - subRecipe, ctx.Random, equipment); - equipment.Update(requiredBlockIndex); - } - - // 자원 검증 - FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - if (agentBalance < (states.GetGoldCurrency() * requiredGold) || avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } - - avatarState.actionPoint -= requiredActionPoint; - if (!(optionIds is null)) - { - foreach (var id in optionIds.OrderBy(id => id)) - { - agentState.unlockedOptions.Add(id); - } - } - - // FIXME: BlacksmithAddress 계좌로 돈이 쌓이기만 하는데 이걸 어떻게 순환시킬지 기획이 필요. - if (requiredGold > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredGold - ); - } - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = requiredActionPoint, - gold = requiredGold, - materials = materials, - itemUsable = equipment, - recipeId = RecipeId, - subRecipeId = SubRecipeId, - itemType = ItemType.Equipment, - }; - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - requiredBlockIndex); - result.id = mail.id; - avatarState.Update3(mail); - avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId); - avatarState.UpdateFromCombination2(equipment); - avatarState.UpdateQuestRewards2(materialSheet); - return states - .SetState(AvatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(ctx.Signer, agentState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["recipeId"] = RecipeId.Serialize(), - ["subRecipeId"] = SubRecipeId.Serialize(), - ["slotIndex"] = SlotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - RecipeId = plainValue["recipeId"].ToInteger(); - SubRecipeId = plainValue["subRecipeId"].ToNullableInteger(); - SlotIndex = plainValue["slotIndex"].ToInteger(); - } - - private static DecimalStat GetStat(EquipmentItemOptionSheet.Row row, IRandom random) - { - var value = random.Next(row.StatMin, row.StatMax + 1); - return new DecimalStat(row.StatType, value); - } - - private static Skill GetSkill(EquipmentItemOptionSheet.Row row, SkillSheet skillSheet, - IRandom random) - { - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == row.SkillId); - var dmg = random.Next(row.SkillDamageMin, row.SkillDamageMax + 1); - var chance = random.Next(row.SkillChanceMin, row.SkillChanceMax + 1); - var skill = SkillFactory.GetV1(skillRow, dmg, chance); - return skill; - } - catch (InvalidOperationException) - { - return null; - } - } - - public static HashSet SelectOption( - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet, - EquipmentItemSubRecipeSheet.Row subRecipe, - IRandom random, - Equipment equipment - ) - { - var optionSelector = new WeightedSelector(random); - var optionIds = new HashSet(); - - // Skip sort subRecipe.Options because it had been already sorted in WeightedSelector.Select(); - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - optionSelector.Add(optionRow, optionInfo.Ratio); - } - - IEnumerable optionRows = - new EquipmentItemOptionSheet.Row[0]; - try - { - optionRows = optionSelector.SelectV1(subRecipe.MaxOptionLimit); - } - catch (Exception e) when ( - e is InvalidCountException || - e is ListEmptyException - ) - { - return optionIds; - } - finally - { - foreach (var optionRow in optionRows.OrderBy(r => r.Id)) - { - if (optionRow.StatType != StatType.NONE) - { - var stat = GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - } - else - { - var skill = GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - } - } - - optionIds.Add(optionRow.Id); - } - } - - return optionIds; - } - } -} diff --git a/Lib9c/Action/CombinationEquipment3.cs b/Lib9c/Action/CombinationEquipment3.cs deleted file mode 100644 index a643926669..0000000000 --- a/Lib9c/Action/CombinationEquipment3.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment3")] - public class CombinationEquipment3 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public Address AvatarAddress; - public int RecipeId; - public int SlotIndex; - public int? SubRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => AvatarAddress; - int ICombinationEquipmentV1.RecipeId => RecipeId; - int ICombinationEquipmentV1.SlotIndex => SlotIndex; - int? ICombinationEquipmentV1.SubRecipeId => SubRecipeId; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}"); - } - - var recipeSheet = states.GetSheet(); - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - - // Validate recipe. - if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId); - } - - if (!(SubRecipeId is null)) - { - if (!recipe.SubRecipeIds.Contains((int) SubRecipeId)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet." - ); - } - } - - // Validate main recipe is unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current); - } - - if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId); - } - - if (!avatarState.inventory.RemoveFungibleItem2(material.ItemId, recipe.MaterialCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount})" - ); - } - - var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); - materials[equipmentMaterial] = recipe.MaterialCount; - - BigInteger requiredGold = recipe.RequiredGold; - var requiredActionPoint = recipe.RequiredActionPoint; - var equipmentItemSheet = states.GetSheet(); - - // Validate equipment id. - if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId); - } - - var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - - // Validate sub recipe. - HashSet optionIds = null; - if (SubRecipeId.HasValue) - { - var subSheet = states.GetSheet(); - var subId = (int) SubRecipeId; - if (!subSheet.TryGetValue(subId, out var subRecipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId); - } - - requiredBlockIndex += subRecipe.RequiredBlockIndex; - requiredGold += subRecipe.RequiredGold; - requiredActionPoint += subRecipe.RequiredActionPoint; - - foreach (var materialInfo in subRecipe.Materials) - { - if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id); - } - - if (!avatarState.inventory.RemoveFungibleItem2(subMaterialRow.ItemId, - materialInfo.Count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count})" - ); - } - - var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); - materials[subMaterial] = materialInfo.Count; - } - - optionIds = SelectOption(states.GetSheet(), states.GetSheet(), - subRecipe, ctx.Random, equipment); - equipment.Update(requiredBlockIndex); - } - - // Validate NCG. - FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - if (agentBalance < states.GetGoldCurrency() * requiredGold) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the agent ({ctx.Signer}) has no sufficient gold: {agentBalance} < {requiredGold}", - ctx.Signer, - agentBalance - ); - } - - if (avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } - - avatarState.actionPoint -= requiredActionPoint; - if (!(optionIds is null)) - { - foreach (var id in optionIds.OrderBy(id => id)) - { - agentState.unlockedOptions.Add(id); - } - } - - // FIXME: BlacksmithAddress just accumulate NCG. we need plan how to circulate this. - if (requiredGold > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredGold - ); - } - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = requiredActionPoint, - gold = requiredGold, - materials = materials, - itemUsable = equipment, - recipeId = RecipeId, - subRecipeId = SubRecipeId, - itemType = ItemType.Equipment, - }; - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - requiredBlockIndex); - result.id = mail.id; - avatarState.Update3(mail); - avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId); - avatarState.UpdateFromCombination2(equipment); - avatarState.UpdateQuestRewards2(materialSheet); - return states - .SetState(AvatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(ctx.Signer, agentState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["recipeId"] = RecipeId.Serialize(), - ["subRecipeId"] = SubRecipeId.Serialize(), - ["slotIndex"] = SlotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - RecipeId = plainValue["recipeId"].ToInteger(); - SubRecipeId = plainValue["subRecipeId"].ToNullableInteger(); - SlotIndex = plainValue["slotIndex"].ToInteger(); - } - - private static DecimalStat GetStat(EquipmentItemOptionSheet.Row row, IRandom random) - { - var value = random.Next(row.StatMin, row.StatMax + 1); - return new DecimalStat(row.StatType, value); - } - - private static Skill GetSkill(EquipmentItemOptionSheet.Row row, SkillSheet skillSheet, - IRandom random) - { - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == row.SkillId); - var dmg = random.Next(row.SkillDamageMin, row.SkillDamageMax + 1); - var chance = random.Next(row.SkillChanceMin, row.SkillChanceMax + 1); - var skill = SkillFactory.GetV1(skillRow, dmg, chance); - return skill; - } - catch (InvalidOperationException) - { - return null; - } - } - - public static HashSet SelectOption( - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet, - EquipmentItemSubRecipeSheet.Row subRecipe, - IRandom random, - Equipment equipment - ) - { - var optionSelector = new WeightedSelector(random); - var optionIds = new HashSet(); - - // Skip sort subRecipe.Options because it had been already sorted in WeightedSelector.Select(); - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - optionSelector.Add(optionRow, optionInfo.Ratio); - } - - IEnumerable optionRows = - new EquipmentItemOptionSheet.Row[0]; - try - { - optionRows = optionSelector.Select(subRecipe.MaxOptionLimit); - } - catch (Exception e) when ( - e is InvalidCountException || - e is ListEmptyException - ) - { - return optionIds; - } - finally - { - foreach (var optionRow in optionRows.OrderBy(r => r.Id)) - { - if (optionRow.StatType != StatType.NONE) - { - var stat = GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - } - else - { - var skill = GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - } - } - - optionIds.Add(optionRow.Id); - } - } - - return optionIds; - } - } -} diff --git a/Lib9c/Action/CombinationEquipment4.cs b/Lib9c/Action/CombinationEquipment4.cs deleted file mode 100644 index 404af77268..0000000000 --- a/Lib9c/Action/CombinationEquipment4.cs +++ /dev/null @@ -1,344 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment4")] - public class CombinationEquipment4 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public Address AvatarAddress; - public int RecipeId; - public int SlotIndex; - public int? SubRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => AvatarAddress; - int ICombinationEquipmentV1.RecipeId => RecipeId; - int ICombinationEquipmentV1.SlotIndex => SlotIndex; - int? ICombinationEquipmentV1.SubRecipeId => SubRecipeId; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, AvatarAddress, out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}"); - } - - var recipeSheet = states.GetSheet(); - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - - // Validate recipe. - if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId); - } - - if (!(SubRecipeId is null)) - { - if (!recipe.SubRecipeIds.Contains((int) SubRecipeId)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet." - ); - } - } - - // Validate main recipe is unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current); - } - - if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId); - } - - if (!avatarState.inventory.RemoveFungibleItem2(material.ItemId, recipe.MaterialCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount})" - ); - } - - var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); - materials[equipmentMaterial] = recipe.MaterialCount; - - BigInteger requiredGold = recipe.RequiredGold; - var requiredActionPoint = recipe.RequiredActionPoint; - var equipmentItemSheet = states.GetSheet(); - - // Validate equipment id. - if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId); - } - - var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - - // Validate sub recipe. - HashSet optionIds = null; - if (SubRecipeId.HasValue) - { - var subSheet = states.GetSheet(); - var subId = (int) SubRecipeId; - if (!subSheet.TryGetValue(subId, out var subRecipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId); - } - - requiredBlockIndex += subRecipe.RequiredBlockIndex; - requiredGold += subRecipe.RequiredGold; - requiredActionPoint += subRecipe.RequiredActionPoint; - - foreach (var materialInfo in subRecipe.Materials) - { - if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id); - } - - if (!avatarState.inventory.RemoveFungibleItem2(subMaterialRow.ItemId, - materialInfo.Count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count})" - ); - } - - var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); - materials[subMaterial] = materialInfo.Count; - } - - optionIds = SelectOption(states.GetSheet(), states.GetSheet(), - subRecipe, ctx.Random, equipment); - equipment.Update(requiredBlockIndex); - } - - // Validate NCG. - FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - if (agentBalance < states.GetGoldCurrency() * requiredGold) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the agent ({ctx.Signer}) has no sufficient gold: {agentBalance} < {requiredGold}", - ctx.Signer, - agentBalance - ); - } - - if (avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } - - avatarState.actionPoint -= requiredActionPoint; - if (!(optionIds is null)) - { - foreach (var id in optionIds.OrderBy(id => id)) - { - agentState.unlockedOptions.Add(id); - } - } - - // FIXME: BlacksmithAddress just accumulate NCG. we need plan how to circulate this. - if (requiredGold > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredGold - ); - } - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = requiredActionPoint, - gold = requiredGold, - materials = materials, - itemUsable = equipment, - recipeId = RecipeId, - subRecipeId = SubRecipeId, - itemType = ItemType.Equipment, - }; - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId); - avatarState.UpdateFromCombination2(equipment); - avatarState.UpdateQuestRewards2(materialSheet); - return states - .SetState(AvatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(ctx.Signer, agentState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["recipeId"] = RecipeId.Serialize(), - ["subRecipeId"] = SubRecipeId.Serialize(), - ["slotIndex"] = SlotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - RecipeId = plainValue["recipeId"].ToInteger(); - SubRecipeId = plainValue["subRecipeId"].ToNullableInteger(); - SlotIndex = plainValue["slotIndex"].ToInteger(); - } - - private static DecimalStat GetStat(EquipmentItemOptionSheet.Row row, IRandom random) - { - var value = random.Next(row.StatMin, row.StatMax + 1); - return new DecimalStat(row.StatType, value); - } - - private static Skill GetSkill(EquipmentItemOptionSheet.Row row, SkillSheet skillSheet, - IRandom random) - { - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == row.SkillId); - var dmg = random.Next(row.SkillDamageMin, row.SkillDamageMax + 1); - var chance = random.Next(row.SkillChanceMin, row.SkillChanceMax + 1); - var skill = SkillFactory.GetV1(skillRow, dmg, chance); - return skill; - } - catch (InvalidOperationException) - { - return null; - } - } - - public static HashSet SelectOption( - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet, - EquipmentItemSubRecipeSheet.Row subRecipe, - IRandom random, - Equipment equipment - ) - { - var optionSelector = new WeightedSelector(random); - var optionIds = new HashSet(); - - // Skip sort subRecipe.Options because it had been already sorted in WeightedSelector.Select(); - foreach (var optionInfo in subRecipe.Options) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - optionSelector.Add(optionRow, optionInfo.Ratio); - } - - IEnumerable optionRows = - new EquipmentItemOptionSheet.Row[0]; - try - { - optionRows = optionSelector.Select(subRecipe.MaxOptionLimit); - } - catch (Exception e) when ( - e is InvalidCountException || - e is ListEmptyException - ) - { - return optionIds; - } - finally - { - foreach (var optionRow in optionRows.OrderBy(r => r.Id)) - { - if (optionRow.StatType != StatType.NONE) - { - var stat = GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - } - else - { - var skill = GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - } - } - - optionIds.Add(optionRow.Id); - } - } - - return optionIds; - } - } -} diff --git a/Lib9c/Action/CombinationEquipment6.cs b/Lib9c/Action/CombinationEquipment6.cs deleted file mode 100644 index 1d9eb76633..0000000000 --- a/Lib9c/Action/CombinationEquipment6.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment6")] - public class CombinationEquipment6 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public Address AvatarAddress; - public int RecipeId; - public int SlotIndex; - public int? SubRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => AvatarAddress; - int ICombinationEquipmentV1.RecipeId => RecipeId; - int ICombinationEquipmentV1.SlotIndex => SlotIndex; - int? ICombinationEquipmentV1.SubRecipeId => SubRecipeId; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, AvatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}"); - } - - var recipeSheet = states.GetSheet(); - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - - // Validate recipe. - if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId); - } - - if (!(SubRecipeId is null)) - { - if (!recipe.SubRecipeIds.Contains((int) SubRecipeId)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet." - ); - } - } - - // Validate main recipe is unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current); - } - - if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId); - } - - if (!avatarState.inventory.RemoveFungibleItem(material.ItemId, recipe.MaterialCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount})" - ); - } - - var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); - materials[equipmentMaterial] = recipe.MaterialCount; - - BigInteger requiredGold = recipe.RequiredGold; - var requiredActionPoint = recipe.RequiredActionPoint; - var equipmentItemSheet = states.GetSheet(); - - // Validate equipment id. - if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId); - } - - var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - - // Validate sub recipe. - HashSet optionIds = null; - if (SubRecipeId.HasValue) - { - var subSheet = states.GetSheet(); - var subId = (int) SubRecipeId; - if (!subSheet.TryGetValue(subId, out var subRecipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId); - } - - requiredBlockIndex += subRecipe.RequiredBlockIndex; - requiredGold += subRecipe.RequiredGold; - requiredActionPoint += subRecipe.RequiredActionPoint; - - foreach (var materialInfo in subRecipe.Materials) - { - if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id); - } - - if (!avatarState.inventory.RemoveFungibleItem(subMaterialRow.ItemId, - materialInfo.Count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count})" - ); - } - - var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); - materials[subMaterial] = materialInfo.Count; - } - - optionIds = CombinationEquipment4.SelectOption(states.GetSheet(), states.GetSheet(), - subRecipe, ctx.Random, equipment); - equipment.Update(requiredBlockIndex); - } - - // Validate NCG. - FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - if (agentBalance < states.GetGoldCurrency() * requiredGold) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the agent ({ctx.Signer}) has no sufficient gold: {agentBalance} < {requiredGold}", - ctx.Signer, - agentBalance - ); - } - - if (avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } - - avatarState.actionPoint -= requiredActionPoint; - if (!(optionIds is null)) - { - foreach (var id in optionIds.OrderBy(id => id)) - { - agentState.unlockedOptions.Add(id); - } - } - - // FIXME: BlacksmithAddress just accumulate NCG. we need plan how to circulate this. - if (requiredGold > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredGold - ); - } - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = requiredActionPoint, - gold = requiredGold, - materials = materials, - itemUsable = equipment, - recipeId = RecipeId, - subRecipeId = SubRecipeId, - itemType = ItemType.Equipment, - }; - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialSheet); - return states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(ctx.Signer, agentState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["recipeId"] = RecipeId.Serialize(), - ["subRecipeId"] = SubRecipeId.Serialize(), - ["slotIndex"] = SlotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - RecipeId = plainValue["recipeId"].ToInteger(); - SubRecipeId = plainValue["subRecipeId"].ToNullableInteger(); - SlotIndex = plainValue["slotIndex"].ToInteger(); - } - - public static DecimalStat GetStat(EquipmentItemOptionSheet.Row row, IRandom random) - { - var value = random.Next(row.StatMin, row.StatMax + 1); - return new DecimalStat(row.StatType, value); - } - - public static Skill GetSkill(EquipmentItemOptionSheet.Row row, SkillSheet skillSheet, - IRandom random) - { - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == row.SkillId); - var dmg = random.Next(row.SkillDamageMin, row.SkillDamageMax + 1); - var chance = random.Next(row.SkillChanceMin, row.SkillChanceMax + 1); - var skill = SkillFactory.GetV1(skillRow, dmg, chance); - return skill; - } - catch (InvalidOperationException) - { - return null; - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment7.cs b/Lib9c/Action/CombinationEquipment7.cs deleted file mode 100644 index 7e3baacf31..0000000000 --- a/Lib9c/Action/CombinationEquipment7.cs +++ /dev/null @@ -1,295 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment7")] - public class CombinationEquipment7 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public Address AvatarAddress; - public int RecipeId; - public int SlotIndex; - public int? SubRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => AvatarAddress; - int ICombinationEquipmentV1.RecipeId => RecipeId; - int ICombinationEquipmentV1.SlotIndex => SlotIndex; - int? ICombinationEquipmentV1.SubRecipeId => SubRecipeId; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, AvatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(AvatarAddress, SlotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state is failed to load"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {SlotIndex}"); - } - - var recipeSheet = states.GetSheet(); - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - - // Validate recipe. - if (!recipeSheet.TryGetValue(RecipeId, out var recipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemRecipeSheet), RecipeId); - } - - if (!(SubRecipeId is null)) - { - if (!recipe.SubRecipeIds.Contains((int) SubRecipeId)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {SubRecipeId} was failed to load from the sheet." - ); - } - } - - // Validate main recipe is unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipe.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, recipe.UnlockStage, current); - } - - if (!materialSheet.TryGetValue(recipe.MaterialId, out var material)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), recipe.MaterialId); - } - - if (!avatarState.inventory.RemoveFungibleItem(material.ItemId, context.BlockIndex, recipe.MaterialCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({material} * {recipe.MaterialCount}). BlockIndex({context.BlockIndex})" - ); - } - - var equipmentMaterial = ItemFactory.CreateMaterial(materialSheet, material.Id); - materials[equipmentMaterial] = recipe.MaterialCount; - - BigInteger requiredGold = recipe.RequiredGold; - var requiredActionPoint = recipe.RequiredActionPoint; - var equipmentItemSheet = states.GetSheet(); - - // Validate equipment id. - if (!equipmentItemSheet.TryGetValue(recipe.ResultEquipmentId, out var equipRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(equipmentItemSheet), recipe.ResultEquipmentId); - } - - var requiredBlockIndex = ctx.BlockIndex + recipe.RequiredBlockIndex; - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - ctx.Random.GenerateRandomGuid(), - requiredBlockIndex - ); - - // Validate sub recipe. - HashSet optionIds = null; - if (SubRecipeId.HasValue) - { - var subSheet = states.GetSheet(); - var subId = (int) SubRecipeId; - if (!subSheet.TryGetValue(subId, out var subRecipe)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(EquipmentItemSubRecipeSheet), subId); - } - - requiredBlockIndex += subRecipe.RequiredBlockIndex; - requiredGold += subRecipe.RequiredGold; - requiredActionPoint += subRecipe.RequiredActionPoint; - - foreach (var materialInfo in subRecipe.Materials) - { - if (!materialSheet.TryGetValue(materialInfo.Id, out var subMaterialRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(MaterialItemSheet), materialInfo.Id); - } - - if (!avatarState.inventory.RemoveFungibleItem( - subMaterialRow.ItemId, - context.BlockIndex, - materialInfo.Count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({subMaterialRow} * {materialInfo.Count}). BlockIndex({context.BlockIndex})" - ); - } - - var subMaterial = ItemFactory.CreateMaterial(materialSheet, materialInfo.Id); - materials[subMaterial] = materialInfo.Count; - } - - optionIds = CombinationEquipment4.SelectOption(states.GetSheet(), states.GetSheet(), - subRecipe, ctx.Random, equipment); - equipment.Update(requiredBlockIndex); - } - - // Validate NCG. - FungibleAssetValue agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - if (agentBalance < states.GetGoldCurrency() * requiredGold) - { - throw new InsufficientBalanceException( - $"{addressesHex}Aborted as the agent ({ctx.Signer}) has no sufficient gold: {agentBalance} < {requiredGold}", - ctx.Signer, - agentBalance - ); - } - - if (avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } - - avatarState.actionPoint -= requiredActionPoint; - if (!(optionIds is null)) - { - foreach (var id in optionIds.OrderBy(id => id)) - { - agentState.unlockedOptions.Add(id); - } - } - - // FIXME: BlacksmithAddress just accumulate NCG. we need plan how to circulate this. - if (requiredGold > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredGold - ); - } - - var result = new CombinationConsumable5.ResultModel - { - actionPoint = requiredActionPoint, - gold = requiredGold, - materials = materials, - itemUsable = equipment, - recipeId = RecipeId, - subRecipeId = SubRecipeId, - itemType = ItemType.Equipment, - }; - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - var mail = new CombinationMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), - requiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - avatarState.questList.UpdateCombinationEquipmentQuest(RecipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialSheet); - return states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(ctx.Signer, agentState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["recipeId"] = RecipeId.Serialize(), - ["subRecipeId"] = SubRecipeId.Serialize(), - ["slotIndex"] = SlotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - RecipeId = plainValue["recipeId"].ToInteger(); - SubRecipeId = plainValue["subRecipeId"].ToNullableInteger(); - SlotIndex = plainValue["slotIndex"].ToInteger(); - } - - public static DecimalStat GetStat(EquipmentItemOptionSheet.Row row, IRandom random) - { - var value = random.Next(row.StatMin, row.StatMax + 1); - return new DecimalStat(row.StatType, value); - } - - public static Skill GetSkill(EquipmentItemOptionSheet.Row row, SkillSheet skillSheet, - IRandom random) - { - try - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == row.SkillId); - var dmg = random.Next(row.SkillDamageMin, row.SkillDamageMax + 1); - var chance = random.Next(row.SkillChanceMin, row.SkillChanceMax + 1); - var skill = SkillFactory.GetV1(skillRow, dmg, chance); - return skill; - } - catch (InvalidOperationException) - { - return null; - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment8.cs b/Lib9c/Action/CombinationEquipment8.cs deleted file mode 100644 index d95c2a186e..0000000000 --- a/Lib9c/Action/CombinationEquipment8.cs +++ /dev/null @@ -1,386 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("combination_equipment8")] - public class CombinationEquipment8 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => avatarAddress; - int ICombinationEquipmentV1.RecipeId => recipeId; - int ICombinationEquipmentV1.SlotIndex => slotIndex; - int? ICombinationEquipmentV1.SubRecipeId => subRecipeId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(context.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100086ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNCG = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - // Validate RecipeId - var equipmentItemRecipeSheet = states.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = states.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = states.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = states.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNCG += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNCG += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - // Remove Required Materials - var inventory = avatarState.inventory; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - if (!materialItemSheet.TryGetValue(pair.Key, out materialRow) || - !inventory.RemoveFungibleItem(materialRow.ItemId, context.BlockIndex, pair.Value)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - // ~Remove Required Materials - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNCG > 0L) - { - states = states.TransferAsset( - context, - context.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * costNCG - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - states.GetSheet(), - states.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNCG, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options.OrderByDescending(e => e.Ratio)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - } -} diff --git a/Lib9c/Action/CombinationEquipment9.cs b/Lib9c/Action/CombinationEquipment9.cs deleted file mode 100644 index 7daeb0493b..0000000000 --- a/Lib9c/Action/CombinationEquipment9.cs +++ /dev/null @@ -1,388 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionType("combination_equipment9")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class CombinationEquipment9 : GameAction, ICombinationEquipmentV1 - { - public static readonly Address BlacksmithAddress = ItemEnhancement9.BlacksmithAddress; - - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - public const string SubRecipeIdKey = "i"; - public int? subRecipeId; - - Address ICombinationEquipmentV1.AvatarAddress => avatarAddress; - int ICombinationEquipmentV1.RecipeId => recipeId; - int ICombinationEquipmentV1.SlotIndex => slotIndex; - int? ICombinationEquipmentV1.SubRecipeId => subRecipeId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - [SubRecipeIdKey] = subRecipeId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - subRecipeId = plainValue[SubRecipeIdKey].ToNullableInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged) - .SetState(context.Signer, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, BlacksmithAddress); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out var agentState, - out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - // Validate Required Cleared Stage - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationEquipmentAction, - current); - } - // ~Validate Required Cleared Stage - - // Validate SlotIndex - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the slot state is failed to load: # {slotIndex}"); - } - - if (!slotState.Validate(avatarState, context.BlockIndex)) - { - throw new CombinationSlotUnlockException( - $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); - } - // ~Validate SlotIndex - - // Validate Work - var costActionPoint = 0; - var costNCG = 0L; - var endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - // Validate RecipeId - var equipmentItemRecipeSheet = states.GetSheet(); - if (!equipmentItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe Unlocked. - if (!avatarState.worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - recipeRow.UnlockStage, - current); - } - // ~Validate Recipe Unlocked - - // Validate Recipe ResultEquipmentId - var equipmentItemSheet = states.GetSheet(); - if (!equipmentItemSheet.TryGetValue(recipeRow.ResultEquipmentId, out var equipmentRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(equipmentItemSheet), - recipeRow.ResultEquipmentId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = states.GetSheet(); - if (!materialItemSheet.TryGetValue(recipeRow.MaterialId, out var materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - recipeRow.MaterialId); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += recipeRow.MaterialCount; - } - else - { - requiredFungibleItems[materialRow.Id] = recipeRow.MaterialCount; - } - // ~Validate Recipe Material - - // Validate SubRecipeId - EquipmentItemSubRecipeSheetV2.Row subRecipeRow = null; - if (subRecipeId.HasValue) - { - if (!recipeRow.SubRecipeIds.Contains(subRecipeId.Value)) - { - throw new SheetRowColumnException( - $"{addressesHex}Aborted as the sub recipe {subRecipeId.Value} was failed to load from the sheet." - ); - } - - var equipmentItemSubRecipeSheetV2 = states.GetSheet(); - if (!equipmentItemSubRecipeSheetV2.TryGetValue(subRecipeId.Value, out subRecipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(EquipmentItemSubRecipeSheetV2), - subRecipeId.Value); - } - - // Validate SubRecipe Material - for (var i = subRecipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = subRecipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out materialRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(MaterialItemSheet), - materialInfo.Id); - } - - if (requiredFungibleItems.ContainsKey(materialRow.Id)) - { - requiredFungibleItems[materialRow.Id] += materialInfo.Count; - } - else - { - requiredFungibleItems[materialRow.Id] = materialInfo.Count; - } - } - // ~Validate SubRecipe Material - - costActionPoint += subRecipeRow.RequiredActionPoint; - costNCG += subRecipeRow.RequiredGold; - endBlockIndex += subRecipeRow.RequiredBlockIndex; - } - // ~Validate SubRecipeId - - costActionPoint += recipeRow.RequiredActionPoint; - costNCG += recipeRow.RequiredGold; - endBlockIndex += recipeRow.RequiredBlockIndex; - // ~Validate Work - - // Remove Required Materials - var inventory = avatarState.inventory; - foreach (var pair in requiredFungibleItems.OrderBy(pair => pair.Key)) - { - if (!materialItemSheet.TryGetValue(pair.Key, out materialRow) || - !inventory.RemoveFungibleItem(materialRow.ItemId, context.BlockIndex, pair.Value)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({pair.Key} * {pair.Value})"); - } - } - // ~Remove Required Materials - - // Subtract Required ActionPoint - if (costActionPoint > 0) - { - if (avatarState.actionPoint < costActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {costActionPoint}" - ); - } - - avatarState.actionPoint -= costActionPoint; - } - // ~Subtract Required ActionPoint - - // Transfer Required NCG - if (costNCG > 0L) - { - states = states.TransferAsset( - context, - context.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * costNCG - ); - } - // ~Transfer Required NCG - - // Create Equipment - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - endBlockIndex); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - context.Random, - subRecipeRow, - states.GetSheet(), - states.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - } - // ~Create Equipment - - // Add or Update Equipment - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.questList.UpdateCombinationEquipmentQuest(recipeId); - avatarState.UpdateFromCombination(equipment); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Equipment - - // Update Slot - var mailId = context.Random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - gold = costNCG, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = equipment, - recipeId = recipeId, - subRecipeId = subRecipeId, - }; - slotState.Update(attachmentResult, context.BlockIndex, endBlockIndex); - // ~Update Slot - - // Create Mail - var mail = new CombinationMail( - attachmentResult, - context.BlockIndex, - mailId, - endBlockIndex); - avatarState.Update(mail); - // ~Create Mail - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - - public static void AddAndUnlockOption( - AgentState agentState, - Equipment equipment, - IRandom random, - EquipmentItemSubRecipeSheetV2.Row subRecipe, - EquipmentItemOptionSheet optionSheet, - SkillSheet skillSheet - ) - { - foreach (var optionInfo in subRecipe.Options - .OrderByDescending(e => e.Ratio) - .ThenBy(e => e.RequiredBlockIndex) - .ThenBy(e => e.Id)) - { - if (!optionSheet.TryGetValue(optionInfo.Id, out var optionRow)) - { - continue; - } - - var value = random.Next(1, GameConfig.MaximumProbability + 1); - if (value > optionInfo.Ratio) - { - continue; - } - - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - equipment.Update(equipment.RequiredBlockIndex + optionInfo.RequiredBlockIndex); - equipment.optionCountFromCombination++; - agentState.unlockedOptions.Add(optionRow.Id); - } - } - } - } - } -} diff --git a/Lib9c/Action/CreateAvatar2.cs b/Lib9c/Action/CreateAvatar2.cs deleted file mode 100644 index b1d1340447..0000000000 --- a/Lib9c/Action/CreateAvatar2.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("create_avatar2")] - public class CreateAvatar2 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - if (ctx.Rehearsal) - { - states = states.SetState(ctx.Signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(Addresses.Ranking, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, GoldCurrencyState.Address, context.Signer); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(ctx.Signer); - var agentState = existingAgentState ?? new AgentState(ctx.Signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - var rankingState = ctx.PreviousState.GetRankingState0(); - - var rankingMapAddress = rankingState.UpdateRankingMap(avatarAddress); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, rankingMapAddress); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards2(materialItemSheet); - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(Addresses.Ranking, rankingState.Serialize()) - .SetState(avatarAddress, avatarState.Serialize()); - } - } -} diff --git a/Lib9c/Action/CreateAvatar3.cs b/Lib9c/Action/CreateAvatar3.cs deleted file mode 100644 index e1b2193fc7..0000000000 --- a/Lib9c/Action/CreateAvatar3.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("create_avatar3")] - public class CreateAvatar3 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(ctx.Signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(Addresses.Ranking, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, GoldCurrencyState.Address, context.Signer); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(ctx.Signer); - var agentState = existingAgentState ?? new AgentState(ctx.Signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - var rankingState = ctx.PreviousState.GetRankingState0(); - - var rankingMapAddress = rankingState.UpdateRankingMap(avatarAddress); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, rankingMapAddress); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards2(materialItemSheet); - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(Addresses.Ranking, rankingState.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/CreateAvatar4.cs b/Lib9c/Action/CreateAvatar4.cs deleted file mode 100644 index 81dd74053f..0000000000 --- a/Lib9c/Action/CreateAvatar4.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("create_avatar4")] - public class CreateAvatar4 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(ctx.Signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(Addresses.Ranking, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, GoldCurrencyState.Address, context.Signer); - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(ctx.Signer); - var agentState = existingAgentState ?? new AgentState(ctx.Signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - var rankingState = ctx.PreviousState.GetRankingState0(); - - var rankingMapAddress = rankingState.UpdateRankingMap(avatarAddress); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, rankingMapAddress); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards(materialItemSheet); - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(Addresses.Ranking, rankingState.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/CreateAvatar5.cs b/Lib9c/Action/CreateAvatar5.cs deleted file mode 100644 index e725199999..0000000000 --- a/Lib9c/Action/CreateAvatar5.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("create_avatar5")] - public class CreateAvatar5 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(ctx.Signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(Addresses.Ranking, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, GoldCurrencyState.Address, context.Signer); - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(ctx.Signer); - var agentState = existingAgentState ?? new AgentState(ctx.Signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - RankingState1 rankingState = ctx.PreviousState.GetRankingState1(); - - var rankingMapAddress = rankingState.UpdateRankingMap(avatarAddress); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, rankingMapAddress); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards(materialItemSheet); - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(Addresses.Ranking, rankingState.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/CreateAvatar6.cs b/Lib9c/Action/CreateAvatar6.cs deleted file mode 100644 index ff34a5e5eb..0000000000 --- a/Lib9c/Action/CreateAvatar6.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionType("create_avatar6")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class CreateAvatar6 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(ctx.Signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(Addresses.Ranking, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, GoldCurrencyState.Address, context.Signer); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(ctx.Signer); - var agentState = existingAgentState ?? new AgentState(ctx.Signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - RankingState rankingState = ctx.PreviousState.GetRankingState(); - - var rankingMapAddress = rankingState.UpdateRankingMap(avatarAddress); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, rankingMapAddress); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards(materialItemSheet); - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(Addresses.Ranking, rankingState.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/CreateAvatar7.cs b/Lib9c/Action/CreateAvatar7.cs deleted file mode 100644 index 12fecd0924..0000000000 --- a/Lib9c/Action/CreateAvatar7.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/823 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionType("create_avatar7")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class CreateAvatar7 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(ctx.Signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(ctx.Signer); - var agentState = existingAgentState ?? new AgentState(ctx.Signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, default); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards(materialItemSheet); - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - } -} diff --git a/Lib9c/Action/CreateAvatar8.cs b/Lib9c/Action/CreateAvatar8.cs deleted file mode 100644 index aaca205414..0000000000 --- a/Lib9c/Action/CreateAvatar8.cs +++ /dev/null @@ -1,275 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Pet; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1158 - /// Updated at https://github.com/planetarium/lib9c/pull/1158 - /// - [Serializable] - [ActionType("create_avatar8")] - [ActionObsolete(ActionObsoleteConfig.V200040ObsoleteIndex)] - public class CreateAvatar8 : GameAction, ICreateAvatarV2 - { - public const string DeriveFormat = "avatar-state-{0}"; - - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - int ICreateAvatarV2.Index => index; - int ICreateAvatarV2.Hair => hair; - int ICreateAvatarV2.Lens => lens; - int ICreateAvatarV2.Ear => ear; - int ICreateAvatarV2.Tail => tail; - string ICreateAvatarV2.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["index"] = (Integer) index, - ["hair"] = (Integer) hair, - ["lens"] = (Integer) lens, - ["ear"] = (Integer) ear, - ["tail"] = (Integer) tail, - ["name"] = (Text) name, - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - index = (int) ((Integer) plainValue["index"]).Value; - hair = (int) ((Integer) plainValue["hair"]).Value; - lens = (int) ((Integer) plainValue["lens"]).Value; - ear = (int) ((Integer) plainValue["ear"]).Value; - tail = (int) ((Integer) plainValue["tail"]).Value; - name = (Text) plainValue["name"]; - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - CheckObsolete(ActionObsoleteConfig.V200040ObsoleteIndex, context); - IActionContext ctx = context; - var signer = ctx.Signer; - var states = ctx.PreviousState; - var avatarAddress = signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(signer, MarkChanged); - for (var i = 0; i < AvatarState.CombinationSlotCapacity; i++) - { - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - i - ) - ); - states = states.SetState(slotAddress, MarkChanged); - } - - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, signer); - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!Regex.IsMatch(name, GameConfig.AvatarNickNamePattern)) - { - throw new InvalidNamePatternException( - $"{addressesHex}Aborted as the input name {name} does not follow the allowed name pattern."); - } - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CreateAvatar exec started", addressesHex); - AgentState existingAgentState = states.GetAgentState(signer); - var agentState = existingAgentState ?? new AgentState(signer); - var avatarState = states.GetAvatarState(avatarAddress); - if (!(avatarState is null)) - { - throw new InvalidAddressException( - $"{addressesHex}Aborted as there is already an avatar at {avatarAddress}."); - } - - if (!(0 <= index && index < GameConfig.SlotCount)) - { - throw new AvatarIndexOutOfRangeException( - $"{addressesHex}Aborted as the index is out of range #{index}."); - } - - if (agentState.avatarAddresses.ContainsKey(index)) - { - throw new AvatarIndexAlreadyUsedException( - $"{addressesHex}Aborted as the signer already has an avatar at index #{index}."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute CreateAvatar; player: {AvatarAddress}", addressesHex, avatarAddress); - - agentState.avatarAddresses.Add(index, avatarAddress); - - // Avoid NullReferenceException in test - var materialItemSheet = ctx.PreviousState.GetSheet(); - - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, default); - - if (hair < 0) hair = 0; - if (lens < 0) lens = 0; - if (ear < 0) ear = 0; - if (tail < 0) tail = 0; - - avatarState.Customize(hair, lens, ear, tail); - - foreach (var address in avatarState.combinationSlotAddresses) - { - var slotState = - new CombinationSlotState(address, GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); - states = states.SetState(address, slotState.Serialize()); - } - - avatarState.UpdateQuestRewards(materialItemSheet); - - // Add Runes when executing on editor mode. -#if LIB9C_DEV_EXTENSIONS || UNITY_EDITOR - states = CreateAvatar0.AddRunesForTest(context, avatarAddress, states); - - // Add pets for test - if (states.TryGetSheet(out PetSheet petSheet)) - { - foreach (var row in petSheet) - { - var petState = new PetState(row.Id); - petState.LevelUp(); - var petStateAddress = PetState.DeriveAddress(avatarAddress, row.Id); - states = states.SetState(petStateAddress, petState.Serialize()); - } - } - - var recipeIds = new int[] { - 21, - 62, - 103, - 128, - 148, - 152, - }; - var equipmentSheet = states.GetSheet(); - var recipeSheet = states.GetSheet(); - var subRecipeSheet = states.GetSheet(); - var optionSheet = states.GetSheet(); - var skillSheet = states.GetSheet(); - var characterLevelSheet = states.GetSheet(); - var enhancementCostSheet = states.GetSheet(); - - avatarState.level = 300; - avatarState.exp = characterLevelSheet[300].Exp; - - // prepare equipments for test - foreach (var recipeId in recipeIds) - { - var recipeRow = recipeSheet[recipeId]; - var subRecipeId = recipeRow.SubRecipeIds[1]; - var subRecipeRow = subRecipeSheet[subRecipeId]; - var equipmentRow = equipmentSheet[recipeRow.ResultEquipmentId]; - - var equipment = (Equipment)ItemFactory.CreateItemUsable( - equipmentRow, - context.Random.GenerateRandomGuid(), - 0L, - madeWithMimisbrunnrRecipe: subRecipeRow.IsMimisbrunnrSubRecipe ?? false); - - foreach (var option in subRecipeRow.Options) - { - var optionRow = optionSheet[option.Id]; - // Add stats. - if (optionRow.StatType != StatType.NONE) - { - var statMap = new DecimalStat(optionRow.StatType, optionRow.StatMax); - equipment.StatsMap.AddStatAdditionalValue(statMap.StatType, statMap.TotalValue); - equipment.optionCountFromCombination++; - } - // Add skills. - else - { - var skillRow = skillSheet.OrderedList.First(r => r.Id == optionRow.SkillId); - var skill = SkillFactory.Get( - skillRow, - optionRow.SkillDamageMax, - optionRow.SkillChanceMax, - optionRow.StatDamageRatioMax, - optionRow.ReferencedStatType); - if (skill != null) - { - equipment.Skills.Add(skill); - equipment.optionCountFromCombination++; - } - } - } - - for (int i = 1; i <= 20; ++i) - { - var subType = equipment.ItemSubType; - var grade = equipment.Grade; - var costRow = enhancementCostSheet.Values - .First(x => x.ItemSubType == subType && - x.Grade == grade && - x.Level == i); - equipment.LevelUp(ctx.Random, costRow, true); - } - - avatarState.inventory.AddItem(equipment); - } -#endif - - sw.Stop(); - Log.Verbose("{AddressesHex}CreateAvatar CreateAvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}CreateAvatar Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(signer, agentState.Serialize()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()) - .MintAsset(ctx, signer, 50 * CrystalCalculator.CRYSTAL); - } - } -} diff --git a/Lib9c/Action/DailyReward0.cs b/Lib9c/Action/DailyReward0.cs deleted file mode 100644 index 73367be4dd..0000000000 --- a/Lib9c/Action/DailyReward0.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("daily_reward")] - public class DailyReward0 : GameAction, IDailyRewardV1 - { - public Address avatarAddress; - - Address IDailyRewardV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - return states.SetState(avatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out _, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the game config was failed to load."); - } - - if (ctx.BlockIndex - avatarState.dailyRewardReceivedIndex >= gameConfigState.DailyRewardInterval) - { - avatarState.dailyRewardReceivedIndex = ctx.BlockIndex; - avatarState.actionPoint = gameConfigState.ActionPointMax; - } - - return states.SetState(avatarAddress, avatarState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - } -} diff --git a/Lib9c/Action/DailyReward3.cs b/Lib9c/Action/DailyReward3.cs deleted file mode 100644 index a039c5f910..0000000000 --- a/Lib9c/Action/DailyReward3.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("daily_reward3")] - public class DailyReward3 : GameAction, IDailyRewardV1 - { - public Address avatarAddress; - public DailyReward2.DailyRewardResult dailyRewardResult; - private const int rewardItemId = 400000; - private const int rewardItemCount = 10; - - Address IDailyRewardV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - return states.SetState(avatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out _, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the game config was failed to load."); - } - - if (ctx.BlockIndex - avatarState.dailyRewardReceivedIndex >= gameConfigState.DailyRewardInterval) - { - avatarState.dailyRewardReceivedIndex = ctx.BlockIndex; - avatarState.actionPoint = gameConfigState.ActionPointMax; - } - - // create item - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - var material = ItemFactory.CreateMaterial(materialSheet, rewardItemId); - materials[material] = rewardItemCount; - - var result = new DailyReward2.DailyRewardResult - { - materials = materials, - }; - - // create mail - var mail = new DailyRewardMail(result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - ctx.BlockIndex); - - result.id = mail.id; - dailyRewardResult = result; - avatarState.Update(mail); - avatarState.UpdateFromAddItem2(material, rewardItemCount, false); - return states.SetState(avatarAddress, avatarState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - } -} diff --git a/Lib9c/Action/DailyReward4.cs b/Lib9c/Action/DailyReward4.cs deleted file mode 100644 index 5723486a14..0000000000 --- a/Lib9c/Action/DailyReward4.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("daily_reward4")] - public class DailyReward4 : GameAction, IDailyRewardV1 - { - public Address avatarAddress; - public DailyReward2.DailyRewardResult dailyRewardResult; - private const int rewardItemId = 400000; - private const int rewardItemCount = 10; - - Address IDailyRewardV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(avatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out _, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the game config was failed to load."); - } - - if (ctx.BlockIndex - avatarState.dailyRewardReceivedIndex >= gameConfigState.DailyRewardInterval) - { - avatarState.dailyRewardReceivedIndex = ctx.BlockIndex; - avatarState.actionPoint = gameConfigState.ActionPointMax; - } - - // create item - var materialSheet = states.GetSheet(); - var materials = new Dictionary(); - var material = ItemFactory.CreateMaterial(materialSheet, rewardItemId); - materials[material] = rewardItemCount; - - var result = new DailyReward2.DailyRewardResult - { - materials = materials, - }; - - // create mail - var mail = new DailyRewardMail(result, - ctx.BlockIndex, - ctx.Random.GenerateRandomGuid(), - ctx.BlockIndex); - - result.id = mail.id; - dailyRewardResult = result; - avatarState.Update(mail); - avatarState.UpdateFromAddItem2(material, rewardItemCount, false); - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - } -} diff --git a/Lib9c/Action/DailyReward5.cs b/Lib9c/Action/DailyReward5.cs deleted file mode 100644 index 22f674c3aa..0000000000 --- a/Lib9c/Action/DailyReward5.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("daily_reward5")] - public class DailyReward5 : GameAction, IDailyRewardV1 - { - public Address avatarAddress; - public const string AvatarAddressKey = "a"; - - Address IDailyRewardV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states.SetState(avatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out _, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the game config was failed to load."); - } - - if (context.BlockIndex < avatarState.dailyRewardReceivedIndex + gameConfigState.DailyRewardInterval) - { - var sb = new StringBuilder() - .Append($"{addressesHex}Not enough block index to receive daily rewards.") - .Append( - $" Expected: Equals or greater than ({avatarState.dailyRewardReceivedIndex + gameConfigState.DailyRewardInterval}).") - .Append($" Actual: ({context.BlockIndex})"); - throw new RequiredBlockIndexException(sb.ToString()); - } - - avatarState.dailyRewardReceivedIndex = context.BlockIndex; - avatarState.actionPoint = gameConfigState.ActionPointMax; - - return states.SetState(avatarAddress, avatarState.SerializeV2()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - } -} diff --git a/Lib9c/Action/DailyReward6.cs b/Lib9c/Action/DailyReward6.cs deleted file mode 100644 index d07d986fea..0000000000 --- a/Lib9c/Action/DailyReward6.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Helper; -using Nekoyume.Model.State; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/615 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionType("daily_reward6")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class DailyReward6 : GameAction, IDailyRewardV1 - { - public Address avatarAddress; - public const string AvatarAddressKey = "a"; - - Address IDailyRewardV1.AvatarAddress => avatarAddress; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, avatarAddress); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}DailyReward exec started", addressesHex); - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, out _, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the game config was failed to load."); - } - - if (context.BlockIndex < avatarState.dailyRewardReceivedIndex + gameConfigState.DailyRewardInterval) - { - var sb = new StringBuilder() - .Append($"{addressesHex}Not enough block index to receive daily rewards.") - .Append( - $" Expected: Equals or greater than ({avatarState.dailyRewardReceivedIndex + gameConfigState.DailyRewardInterval}).") - .Append($" Actual: ({context.BlockIndex})"); - throw new RequiredBlockIndexException(sb.ToString()); - } - - avatarState.dailyRewardReceivedIndex = context.BlockIndex; - avatarState.actionPoint = gameConfigState.ActionPointMax; - - if (gameConfigState.DailyRuneRewardAmount > 0) - { - states = states.MintAsset( - context, - avatarAddress, - RuneHelper.DailyRewardRune * gameConfigState.DailyRuneRewardAmount); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}DailyReward Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - } -} diff --git a/Lib9c/Action/EventDungeonBattleV1.cs b/Lib9c/Action/EventDungeonBattleV1.cs deleted file mode 100644 index db093108fd..0000000000 --- a/Lib9c/Action/EventDungeonBattleV1.cs +++ /dev/null @@ -1,414 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Exceptions; -using Nekoyume.Extensions; -using Nekoyume.Model.Event; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Event; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1218 - /// - [Serializable] - [ActionType(ActionTypeText)] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class EventDungeonBattleV1 : GameAction, IEventDungeonBattleV1 - { - private const string ActionTypeText = "event_dungeon_battle"; - public const int PlayCount = 1; - - public Address AvatarAddress; - public int EventScheduleId; - public int EventDungeonId; - public int EventDungeonStageId; - public List Equipments; - public List Costumes; - public List Foods; - public bool BuyTicketIfNeeded; - - Address IEventDungeonBattleV1.AvatarAddress => AvatarAddress; - int IEventDungeonBattleV1.EventScheduleId => EventScheduleId; - int IEventDungeonBattleV1.EventDungeonId => EventDungeonId; - int IEventDungeonBattleV1.EventDungeonStageId => EventDungeonStageId; - IEnumerable IEventDungeonBattleV1.Equipments => Equipments; - IEnumerable IEventDungeonBattleV1.Costumes => Costumes; - IEnumerable IEventDungeonBattleV1.Foods => Foods; - bool IEventDungeonBattleV1.BuyTicketIfNeeded => BuyTicketIfNeeded; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var list = Bencodex.Types.List.Empty - .Add(AvatarAddress.Serialize()) - .Add(EventScheduleId.Serialize()) - .Add(EventDungeonId.Serialize()) - .Add(EventDungeonStageId.Serialize()) - .Add(new Bencodex.Types.List( - Equipments - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Costumes - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Foods - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(BuyTicketIfNeeded.Serialize()); - - return new Dictionary - { - { "l", list }, - }.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - if (!plainValue.TryGetValue("l", out var serialized)) - { - throw new ArgumentException("plainValue must contain 'l'"); - } - - if (!(serialized is Bencodex.Types.List list)) - { - throw new ArgumentException("'l' must be a bencodex list"); - } - - if (list.Count < 8) - { - throw new ArgumentException("'l' must contain at least 8 items"); - } - - AvatarAddress = list[0].ToAddress(); - EventScheduleId = list[1].ToInteger(); - EventDungeonId = list[2].ToInteger(); - EventDungeonStageId = list[3].ToInteger(); - Equipments = ((List)list[4]).ToList(StateExtensions.ToGuid); - Costumes = ((List)list[5]).ToList(StateExtensions.ToGuid); - Foods = ((List)list[6]).ToList(StateExtensions.ToGuid); - BuyTicketIfNeeded = list[7].ToBoolean(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Execute() start", - ActionTypeText, - addressesHex); - - var sw = new Stopwatch(); - // Get AvatarState - sw.Start(); - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(AvatarState), - AvatarAddress); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] TryGetAvatarStateV2: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get AvatarState - - // Get sheets - sw.Restart(); - // FIXME Delete this check next hard fork. - bool useV100291Sheets = UseV100291Sheets(context.BlockIndex); - var sheets = useV100291Sheets - ? states.GetSheetsV100291( - containSimulatorSheets: true, - containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - }) - : states.GetSheetsV1( - containSimulatorSheets: true, - containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - }); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Get sheets: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get sheets - - // Validate fields. - sw.Restart(); - var scheduleSheet = sheets.GetSheet(); - var scheduleRow = scheduleSheet.ValidateFromActionForDungeon( - context.BlockIndex, - EventScheduleId, - EventDungeonId, - ActionTypeText, - addressesHex); - - var dungeonSheet = sheets.GetSheet(); - var dungeonRow = dungeonSheet.ValidateFromAction( - EventDungeonId, - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var stageSheet = sheets.GetSheet(); - var stageRow = stageSheet.ValidateFromAction( - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var equipmentAndCostumes = Equipments.Concat(Costumes); - avatarState.EquipItems(equipmentAndCostumes); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate fields. - - // Validate avatar's event dungeon info. - sw.Restart(); - var eventDungeonInfoAddr = EventDungeonInfo.DeriveAddress( - AvatarAddress, - EventDungeonId); - var eventDungeonInfo = states.GetState(eventDungeonInfoAddr) - is Bencodex.Types.List serializedEventDungeonInfoList - ? new EventDungeonInfo(serializedEventDungeonInfoList) - : new EventDungeonInfo(remainingTickets: scheduleRow.DungeonTicketsMax); - - // Update tickets. - { - var blockRange = context.BlockIndex - scheduleRow.StartBlockIndex; - if (blockRange > 0) - { - var interval = - (int)(blockRange / scheduleRow.DungeonTicketsResetIntervalBlockRange); - if (interval > eventDungeonInfo.ResetTicketsInterval) - { - eventDungeonInfo.ResetTickets( - interval, - scheduleRow.DungeonTicketsMax); - } - } - } - // ~Update tickets. - - if (!eventDungeonInfo.TryUseTickets(PlayCount)) - { - if (!BuyTicketIfNeeded) - { - throw new NotEnoughEventDungeonTicketsException( - ActionTypeText, - addressesHex, - PlayCount, - eventDungeonInfo.RemainingTickets); - } - - var currency = states.GetGoldCurrency(); - var cost = scheduleRow.GetDungeonTicketCostV1( - eventDungeonInfo.NumberOfTicketPurchases); - if (cost > 0L) - { - states = states.TransferAsset( - context, - context.Signer, - Addresses.EventDungeon, - cost * currency); - } - - // NOTE: The number of ticket purchases should be increased - // even if [`cost`] is 0. - eventDungeonInfo.IncreaseNumberOfTicketPurchases(); - } - - if (EventDungeonStageId != dungeonRow.StageBegin && - !eventDungeonInfo.IsCleared(EventDungeonStageId - 1)) - { - throw new StageNotClearedException( - ActionTypeText, - addressesHex, - EventDungeonStageId - 1, - eventDungeonInfo.ClearedStageId); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate avatar's event dungeon info. - - // Simulate - sw.Restart(); - var exp = scheduleRow.GetStageExp( - EventDungeonStageId.ToEventDungeonStageNumber(), - PlayCount); - var simulatorSheets = useV100291Sheets - ? sheets.GetSimulatorSheetsV100291() - : sheets.GetSimulatorSheetsV1(); - var simulator = new StageSimulatorV2( - context.Random, - avatarState, - Foods, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards( - context.Random, - stageRow, - sheets.GetSheet(), - PlayCount)); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Simulate: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Simulate - - // Update avatar's event dungeon info. - if (simulator.Log.IsClear) - { - sw.Restart(); - eventDungeonInfo.ClearStage(EventDungeonStageId); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Update event dungeon info: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - } - // ~Update avatar's event dungeon info. - - // Apply player to avatar state - sw.Restart(); - avatarState.Apply(simulator.Player, context.BlockIndex); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Apply player to avatar state: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Apply player to avatar state - - // Set states - sw.Restart(); - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - else - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Set states: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Set states - - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Total elapsed: {Elapsed}", - ActionTypeText, - addressesHex, - DateTimeOffset.UtcNow - started); - return states; - } - } -} diff --git a/Lib9c/Action/EventDungeonBattleV2.cs b/Lib9c/Action/EventDungeonBattleV2.cs deleted file mode 100644 index 4bc258bba5..0000000000 --- a/Lib9c/Action/EventDungeonBattleV2.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Exceptions; -using Nekoyume.Extensions; -using Nekoyume.Model.Event; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Event; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1321 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType(ActionTypeText)] - public class EventDungeonBattleV2 : GameAction, IEventDungeonBattleV1 - { - private const string ActionTypeText = "event_dungeon_battle2"; - public const int PlayCount = 1; - - public Address AvatarAddress; - public int EventScheduleId; - public int EventDungeonId; - public int EventDungeonStageId; - public List Equipments; - public List Costumes; - public List Foods; - public bool BuyTicketIfNeeded; - - Address IEventDungeonBattleV1.AvatarAddress => AvatarAddress; - int IEventDungeonBattleV1.EventScheduleId => EventScheduleId; - int IEventDungeonBattleV1.EventDungeonId => EventDungeonId; - int IEventDungeonBattleV1.EventDungeonStageId => EventDungeonStageId; - IEnumerable IEventDungeonBattleV1.Equipments => Equipments; - IEnumerable IEventDungeonBattleV1.Costumes => Costumes; - IEnumerable IEventDungeonBattleV1.Foods => Foods; - bool IEventDungeonBattleV1.BuyTicketIfNeeded => BuyTicketIfNeeded; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var list = Bencodex.Types.List.Empty - .Add(AvatarAddress.Serialize()) - .Add(EventScheduleId.Serialize()) - .Add(EventDungeonId.Serialize()) - .Add(EventDungeonStageId.Serialize()) - .Add(new Bencodex.Types.List( - Equipments - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Costumes - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Foods - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(BuyTicketIfNeeded.Serialize()); - - return new Dictionary - { - { "l", list }, - }.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - if (!plainValue.TryGetValue("l", out var serialized)) - { - throw new ArgumentException("plainValue must contain 'l'"); - } - - if (!(serialized is Bencodex.Types.List list)) - { - throw new ArgumentException("'l' must be a bencodex list"); - } - - if (list.Count < 8) - { - throw new ArgumentException("'l' must contain at least 8 items"); - } - - AvatarAddress = list[0].ToAddress(); - EventScheduleId = list[1].ToInteger(); - EventDungeonId = list[2].ToInteger(); - EventDungeonStageId = list[3].ToInteger(); - Equipments = ((List)list[4]).ToList(StateExtensions.ToGuid); - Costumes = ((List)list[5]).ToList(StateExtensions.ToGuid); - Foods = ((List)list[6]).ToList(StateExtensions.ToGuid); - BuyTicketIfNeeded = list[7].ToBoolean(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Execute() start", - ActionTypeText, - addressesHex); - - var sw = new Stopwatch(); - // Get AvatarState - sw.Start(); - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(AvatarState), - AvatarAddress); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] TryGetAvatarStateV2: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get AvatarState - - // Get sheets - sw.Restart(); - // FIXME Delete this check next hard fork. - bool useV100291Sheets = UseV100291Sheets(context.BlockIndex); - var sheets = useV100291Sheets - ? states.GetSheetsV100291( - containSimulatorSheets: true, - containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - }) - : states.GetSheetsV1( - containSimulatorSheets: true, - containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - }); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Get sheets: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get sheets - - // Validate fields. - sw.Restart(); - var scheduleSheet = sheets.GetSheet(); - var scheduleRow = scheduleSheet.ValidateFromActionForDungeon( - context.BlockIndex, - EventScheduleId, - EventDungeonId, - ActionTypeText, - addressesHex); - - var dungeonSheet = sheets.GetSheet(); - var dungeonRow = dungeonSheet.ValidateFromAction( - EventDungeonId, - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var stageSheet = sheets.GetSheet(); - var stageRow = stageSheet.ValidateFromAction( - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var equipmentAndCostumes = Equipments.Concat(Costumes); - avatarState.EquipItems(equipmentAndCostumes); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate fields. - - // Validate avatar's event dungeon info. - sw.Restart(); - var eventDungeonInfoAddr = EventDungeonInfo.DeriveAddress( - AvatarAddress, - EventDungeonId); - var eventDungeonInfo = states.GetState(eventDungeonInfoAddr) - is Bencodex.Types.List serializedEventDungeonInfoList - ? new EventDungeonInfo(serializedEventDungeonInfoList) - : new EventDungeonInfo(remainingTickets: scheduleRow.DungeonTicketsMax); - - // Update tickets. - { - var blockRange = context.BlockIndex - scheduleRow.StartBlockIndex; - if (blockRange > 0) - { - var interval = - (int)(blockRange / scheduleRow.DungeonTicketsResetIntervalBlockRange); - if (interval > eventDungeonInfo.ResetTicketsInterval) - { - eventDungeonInfo.ResetTickets( - interval, - scheduleRow.DungeonTicketsMax); - } - } - } - // ~Update tickets. - - if (!eventDungeonInfo.TryUseTickets(PlayCount)) - { - if (!BuyTicketIfNeeded) - { - throw new NotEnoughEventDungeonTicketsException( - ActionTypeText, - addressesHex, - PlayCount, - eventDungeonInfo.RemainingTickets); - } - - var currency = states.GetGoldCurrency(); - var cost = scheduleRow.GetDungeonTicketCost( - eventDungeonInfo.NumberOfTicketPurchases, - currency); - if (cost.Sign > 0) - { - states = states.TransferAsset( - context, - context.Signer, - Addresses.EventDungeon, - cost); - } - - // NOTE: The number of ticket purchases should be increased - // even if [`cost`] is 0. - eventDungeonInfo.IncreaseNumberOfTicketPurchases(); - } - - if (EventDungeonStageId != dungeonRow.StageBegin && - !eventDungeonInfo.IsCleared(EventDungeonStageId - 1)) - { - throw new StageNotClearedException( - ActionTypeText, - addressesHex, - EventDungeonStageId - 1, - eventDungeonInfo.ClearedStageId); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate avatar's event dungeon info. - - // Simulate - sw.Restart(); - var exp = scheduleRow.GetStageExp( - EventDungeonStageId.ToEventDungeonStageNumber(), - PlayCount); - var simulatorSheets = useV100291Sheets - ? sheets.GetSimulatorSheetsV100291() - : sheets.GetSimulatorSheetsV1(); - var simulator = new StageSimulatorV2( - context.Random, - avatarState, - Foods, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards( - context.Random, - stageRow, - sheets.GetSheet(), - PlayCount)); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Simulate: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Simulate - - // Update avatar's event dungeon info. - if (simulator.Log.IsClear) - { - sw.Restart(); - eventDungeonInfo.ClearStage(EventDungeonStageId); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Update event dungeon info: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - } - // ~Update avatar's event dungeon info. - - // Apply player to avatar state - sw.Restart(); - avatarState.Apply(simulator.Player, context.BlockIndex); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Apply player to avatar state: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Apply player to avatar state - - // Set states - sw.Restart(); - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - else - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Set states: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Set states - - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Total elapsed: {Elapsed}", - ActionTypeText, - addressesHex, - DateTimeOffset.UtcNow - started); - return states; - } - } -} diff --git a/Lib9c/Action/EventDungeonBattleV3.cs b/Lib9c/Action/EventDungeonBattleV3.cs deleted file mode 100644 index 47e72fc95d..0000000000 --- a/Lib9c/Action/EventDungeonBattleV3.cs +++ /dev/null @@ -1,435 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Exceptions; -using Nekoyume.Extensions; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Event; -using Nekoyume.Model.Rune; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Event; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/ - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType(ActionTypeText)] - public class EventDungeonBattleV3 : GameAction, IEventDungeonBattleV2 - { - private const string ActionTypeText = "event_dungeon_battle3"; - public const int PlayCount = 1; - - public Address AvatarAddress; - public int EventScheduleId; - public int EventDungeonId; - public int EventDungeonStageId; - public List Equipments; - public List Costumes; - public List Foods; - public bool BuyTicketIfNeeded; - public List RuneInfos; - - Address IEventDungeonBattleV2.AvatarAddress => AvatarAddress; - int IEventDungeonBattleV2.EventScheduleId => EventScheduleId; - int IEventDungeonBattleV2.EventDungeonId => EventDungeonId; - int IEventDungeonBattleV2.EventDungeonStageId => EventDungeonStageId; - IEnumerable IEventDungeonBattleV2.Equipments => Equipments; - IEnumerable IEventDungeonBattleV2.Costumes => Costumes; - IEnumerable IEventDungeonBattleV2.Foods => Foods; - IEnumerable IEventDungeonBattleV2.RuneSlotInfos => - RuneInfos.Select(x => x.Serialize()); - bool IEventDungeonBattleV2.BuyTicketIfNeeded => BuyTicketIfNeeded; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var list = Bencodex.Types.List.Empty - .Add(AvatarAddress.Serialize()) - .Add(EventScheduleId.Serialize()) - .Add(EventDungeonId.Serialize()) - .Add(EventDungeonStageId.Serialize()) - .Add(new Bencodex.Types.List( - Equipments - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Costumes - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Foods - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(BuyTicketIfNeeded.Serialize()) - .Add(RuneInfos.OrderBy(x => x.SlotIndex).Select(x => x.Serialize()) - .Serialize()); - - return new Dictionary - { - { "l", list }, - }.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - if (!plainValue.TryGetValue("l", out var serialized)) - { - throw new ArgumentException("plainValue must contain 'l'"); - } - - if (!(serialized is Bencodex.Types.List list)) - { - throw new ArgumentException("'l' must be a bencodex list"); - } - - if (list.Count < 9) - { - throw new ArgumentException("'l' must contain at least 9 items"); - } - - AvatarAddress = list[0].ToAddress(); - EventScheduleId = list[1].ToInteger(); - EventDungeonId = list[2].ToInteger(); - EventDungeonStageId = list[3].ToInteger(); - Equipments = ((List)list[4]).ToList(StateExtensions.ToGuid); - Costumes = ((List)list[5]).ToList(StateExtensions.ToGuid); - Foods = ((List)list[6]).ToList(StateExtensions.ToGuid); - BuyTicketIfNeeded = list[7].ToBoolean(); - RuneInfos = list[8].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Execute() start", - ActionTypeText, - addressesHex); - - var sw = new Stopwatch(); - // Get AvatarState - sw.Start(); - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(AvatarState), - AvatarAddress); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] TryGetAvatarStateV2: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get AvatarState - - // Get sheets - sw.Restart(); - var sheets = states.GetSheets( - containSimulatorSheets: true, - containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Get sheets: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get sheets - - // Validate fields. - sw.Restart(); - var scheduleSheet = sheets.GetSheet(); - var scheduleRow = scheduleSheet.ValidateFromActionForDungeon( - context.BlockIndex, - EventScheduleId, - EventDungeonId, - ActionTypeText, - addressesHex); - - var dungeonSheet = sheets.GetSheet(); - var dungeonRow = dungeonSheet.ValidateFromAction( - EventDungeonId, - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var stageSheet = sheets.GetSheet(); - var stageRow = stageSheet.ValidateFromAction( - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var equipmentAndCostumes = Equipments.Concat(Costumes); - avatarState.EquipItems(equipmentAndCostumes); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate fields. - - // Validate avatar's event dungeon info. - sw.Restart(); - var eventDungeonInfoAddr = EventDungeonInfo.DeriveAddress( - AvatarAddress, - EventDungeonId); - var eventDungeonInfo = states.GetState(eventDungeonInfoAddr) - is Bencodex.Types.List serializedEventDungeonInfoList - ? new EventDungeonInfo(serializedEventDungeonInfoList) - : new EventDungeonInfo(remainingTickets: scheduleRow.DungeonTicketsMax); - - // Update tickets. - { - var blockRange = context.BlockIndex - scheduleRow.StartBlockIndex; - if (blockRange > 0) - { - var interval = - (int)(blockRange / scheduleRow.DungeonTicketsResetIntervalBlockRange); - if (interval > eventDungeonInfo.ResetTicketsInterval) - { - eventDungeonInfo.ResetTickets( - interval, - scheduleRow.DungeonTicketsMax); - } - } - } - // ~Update tickets. - - if (!eventDungeonInfo.TryUseTickets(PlayCount)) - { - if (!BuyTicketIfNeeded) - { - throw new NotEnoughEventDungeonTicketsException( - ActionTypeText, - addressesHex, - PlayCount, - eventDungeonInfo.RemainingTickets); - } - - var currency = states.GetGoldCurrency(); - var cost = scheduleRow.GetDungeonTicketCost( - eventDungeonInfo.NumberOfTicketPurchases, - currency); - if (cost.Sign > 0) - { - states = states.TransferAsset( - context, - context.Signer, - Addresses.EventDungeon, - cost); - } - - // NOTE: The number of ticket purchases should be increased - // even if [`cost`] is 0. - eventDungeonInfo.IncreaseNumberOfTicketPurchases(); - } - - if (EventDungeonStageId != dungeonRow.StageBegin && - !eventDungeonInfo.IsCleared(EventDungeonStageId - 1)) - { - throw new StageNotClearedException( - ActionTypeText, - addressesHex, - EventDungeonStageId - 1, - eventDungeonInfo.ClearedStageId); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate avatar's event dungeon info. - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlotV2(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - // Simulate - sw.Restart(); - var exp = scheduleRow.GetStageExp( - EventDungeonStageId.ToEventDungeonStageNumber(), - PlayCount); - var simulatorSheets = sheets.GetSimulatorSheets(); - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - var simulator = new StageSimulatorV3( - context.Random, - avatarState, - Foods, - runeStates, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards( - context.Random, - stageRow, - sheets.GetSheet(), - PlayCount)); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Simulate: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Simulate - - // Update avatar's event dungeon info. - if (simulator.Log.IsClear) - { - sw.Restart(); - eventDungeonInfo.ClearStage(EventDungeonStageId); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Update event dungeon info: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - } - // ~Update avatar's event dungeon info. - - // Apply player to avatar state - sw.Restart(); - avatarState.Apply(simulator.Player, context.BlockIndex); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Apply player to avatar state: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Apply player to avatar state - - // Set states - sw.Restart(); - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - else - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Set states: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Set states - - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Total elapsed: {Elapsed}", - ActionTypeText, - addressesHex, - DateTimeOffset.UtcNow - started); - return states; - } - } -} diff --git a/Lib9c/Action/EventDungeonBattleV4.cs b/Lib9c/Action/EventDungeonBattleV4.cs deleted file mode 100644 index f709865810..0000000000 --- a/Lib9c/Action/EventDungeonBattleV4.cs +++ /dev/null @@ -1,432 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Exceptions; -using Nekoyume.Extensions; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Event; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Event; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType(ActionTypeText)] - public class EventDungeonBattleV4 : GameAction, IEventDungeonBattleV2 - { - private const string ActionTypeText = "event_dungeon_battle4"; - public const int PlayCount = 1; - - public Address AvatarAddress; - public int EventScheduleId; - public int EventDungeonId; - public int EventDungeonStageId; - public List Equipments; - public List Costumes; - public List Foods; - public bool BuyTicketIfNeeded; - public List RuneInfos; - - Address IEventDungeonBattleV2.AvatarAddress => AvatarAddress; - int IEventDungeonBattleV2.EventScheduleId => EventScheduleId; - int IEventDungeonBattleV2.EventDungeonId => EventDungeonId; - int IEventDungeonBattleV2.EventDungeonStageId => EventDungeonStageId; - IEnumerable IEventDungeonBattleV2.Equipments => Equipments; - IEnumerable IEventDungeonBattleV2.Costumes => Costumes; - IEnumerable IEventDungeonBattleV2.Foods => Foods; - IEnumerable IEventDungeonBattleV2.RuneSlotInfos => - RuneInfos.Select(x => x.Serialize()); - bool IEventDungeonBattleV2.BuyTicketIfNeeded => BuyTicketIfNeeded; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var list = Bencodex.Types.List.Empty - .Add(AvatarAddress.Serialize()) - .Add(EventScheduleId.Serialize()) - .Add(EventDungeonId.Serialize()) - .Add(EventDungeonStageId.Serialize()) - .Add(new Bencodex.Types.List( - Equipments - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Costumes - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(new Bencodex.Types.List( - Foods - .OrderBy(e => e) - .Select(e => e.Serialize()))) - .Add(BuyTicketIfNeeded.Serialize()) - .Add(RuneInfos.OrderBy(x => x.SlotIndex).Select(x => x.Serialize()) - .Serialize()); - - return new Dictionary - { - { "l", list }, - }.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - if (!plainValue.TryGetValue("l", out var serialized)) - { - throw new ArgumentException("plainValue must contain 'l'"); - } - - if (!(serialized is Bencodex.Types.List list)) - { - throw new ArgumentException("'l' must be a bencodex list"); - } - - if (list.Count < 9) - { - throw new ArgumentException("'l' must contain at least 9 items"); - } - - AvatarAddress = list[0].ToAddress(); - EventScheduleId = list[1].ToInteger(); - EventDungeonId = list[2].ToInteger(); - EventDungeonStageId = list[3].ToInteger(); - Equipments = ((List)list[4]).ToList(StateExtensions.ToGuid); - Costumes = ((List)list[5]).ToList(StateExtensions.ToGuid); - Foods = ((List)list[6]).ToList(StateExtensions.ToGuid); - BuyTicketIfNeeded = list[7].ToBoolean(); - RuneInfos = list[8].ToList(x => new RuneSlotInfo((List)x)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Execute() start", - ActionTypeText, - addressesHex); - - var sw = new Stopwatch(); - // Get AvatarState - sw.Start(); - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - ActionTypeText, - addressesHex, - typeof(AvatarState), - AvatarAddress); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] TryGetAvatarStateV2: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get AvatarState - - // Get sheets - sw.Restart(); - var sheets = states.GetSheets( - containSimulatorSheets: true, - containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Get sheets: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Get sheets - - // Validate fields. - sw.Restart(); - var scheduleSheet = sheets.GetSheet(); - var scheduleRow = scheduleSheet.ValidateFromActionForDungeon( - context.BlockIndex, - EventScheduleId, - EventDungeonId, - ActionTypeText, - addressesHex); - - var dungeonSheet = sheets.GetSheet(); - var dungeonRow = dungeonSheet.ValidateFromAction( - EventDungeonId, - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var stageSheet = sheets.GetSheet(); - var stageRow = stageSheet.ValidateFromAction( - EventDungeonStageId, - ActionTypeText, - addressesHex); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var equipmentAndCostumes = Equipments.Concat(Costumes); - avatarState.EquipItems(equipmentAndCostumes); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate fields. - - // Validate avatar's event dungeon info. - sw.Restart(); - var eventDungeonInfoAddr = EventDungeonInfo.DeriveAddress( - AvatarAddress, - EventDungeonId); - var eventDungeonInfo = states.GetState(eventDungeonInfoAddr) - is Bencodex.Types.List serializedEventDungeonInfoList - ? new EventDungeonInfo(serializedEventDungeonInfoList) - : new EventDungeonInfo(remainingTickets: scheduleRow.DungeonTicketsMax); - - // Update tickets. - { - var blockRange = context.BlockIndex - scheduleRow.StartBlockIndex; - if (blockRange > 0) - { - var interval = - (int)(blockRange / scheduleRow.DungeonTicketsResetIntervalBlockRange); - if (interval > eventDungeonInfo.ResetTicketsInterval) - { - eventDungeonInfo.ResetTickets( - interval, - scheduleRow.DungeonTicketsMax); - } - } - } - // ~Update tickets. - - if (!eventDungeonInfo.TryUseTickets(PlayCount)) - { - if (!BuyTicketIfNeeded) - { - throw new NotEnoughEventDungeonTicketsException( - ActionTypeText, - addressesHex, - PlayCount, - eventDungeonInfo.RemainingTickets); - } - - var currency = states.GetGoldCurrency(); - var cost = scheduleRow.GetDungeonTicketCost( - eventDungeonInfo.NumberOfTicketPurchases, - currency); - if (cost.Sign > 0) - { - states = states.TransferAsset( - context, - context.Signer, - Addresses.EventDungeon, - cost); - } - - // NOTE: The number of ticket purchases should be increased - // even if [`cost`] is 0. - eventDungeonInfo.IncreaseNumberOfTicketPurchases(); - } - - if (EventDungeonStageId != dungeonRow.StageBegin && - !eventDungeonInfo.IsCleared(EventDungeonStageId - 1)) - { - throw new StageNotClearedException( - ActionTypeText, - addressesHex, - EventDungeonStageId - 1, - eventDungeonInfo.ClearedStageId); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Validate fields: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Validate avatar's event dungeon info. - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - // Simulate - sw.Restart(); - var exp = scheduleRow.GetStageExp( - EventDungeonStageId.ToEventDungeonStageNumber(), - PlayCount); - var simulatorSheets = sheets.GetSimulatorSheets(); - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - var simulator = new StageSimulatorV3( - context.Random, - avatarState, - Foods, - runeStates, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards( - context.Random, - stageRow, - sheets.GetSheet(), - PlayCount)); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Simulate: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Simulate - - // Update avatar's event dungeon info. - if (simulator.Log.IsClear) - { - sw.Restart(); - eventDungeonInfo.ClearStage(EventDungeonStageId); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Update event dungeon info: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - } - // ~Update avatar's event dungeon info. - - // Apply player to avatar state - sw.Restart(); - avatarState.Apply(simulator.Player, context.BlockIndex); - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Apply player to avatar state: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Apply player to avatar state - - // Set states - sw.Restart(); - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - AvatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - else - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState( - AvatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState(eventDungeonInfoAddr, eventDungeonInfo.Serialize()); - } - - sw.Stop(); - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Set states: {Elapsed}", - ActionTypeText, - addressesHex, - sw.Elapsed); - // ~Set states - - Log.Verbose( - "[{ActionTypeString}][{AddressesHex}] Total elapsed: {Elapsed}", - ActionTypeText, - addressesHex, - DateTimeOffset.UtcNow - started); - return states; - } - } -} diff --git a/Lib9c/Action/Factory/ClaimStakeRewardFactory.cs b/Lib9c/Action/Factory/ClaimStakeRewardFactory.cs deleted file mode 100644 index ad15a4ed57..0000000000 --- a/Lib9c/Action/Factory/ClaimStakeRewardFactory.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Libplanet.Crypto; - -namespace Nekoyume.Action.Factory -{ - public static class ClaimStakeRewardFactory - { - // NOTE: This method does not return a type of `ClaimStakeReward1`. - // Because it is not obsoleted yet. - public static IClaimStakeReward CreateByBlockIndex( - long blockIndex, - Address avatarAddress) - { - if (blockIndex > ClaimStakeReward4.ObsoleteBlockIndex) - { - return new ClaimStakeReward(avatarAddress); - } - - if (blockIndex > ClaimStakeReward3.ObsoleteBlockIndex) - { - return new ClaimStakeReward4(avatarAddress); - } - - if (blockIndex > ClaimStakeReward2.ObsoletedIndex) - { - return new ClaimStakeReward3(avatarAddress); - } - - // FIXME: This method should consider the starting block index of - // `claim_stake_reward2`. And if the `blockIndex` is less than - // the starting block index, it should throw an exception. - // default: Version 2 - return new ClaimStakeReward2(avatarAddress); - } - - public static IClaimStakeReward CreateByVersion( - int version, - Address avatarAddress) => version switch - { - 1 => new ClaimStakeReward1(avatarAddress), - 2 => new ClaimStakeReward2(avatarAddress), - 3 => new ClaimStakeReward3(avatarAddress), - 4 => new ClaimStakeReward4(avatarAddress), - 5 => new ClaimStakeReward(avatarAddress), - _ => throw new ArgumentOutOfRangeException( - $"Invalid version: {version}"), - }; - } -} diff --git a/Lib9c/Action/HackAndSlash0.cs b/Lib9c/Action/HackAndSlash0.cs deleted file mode 100644 index 13eaa4c497..0000000000 --- a/Lib9c/Action/HackAndSlash0.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash")] - public class HackAndSlash0 : GameAction, IHackAndSlashV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - - IEnumerable IHackAndSlashV1.Costumes => costumes; - IEnumerable IHackAndSlashV1.Equipments => equipments; - IEnumerable IHackAndSlashV1.Foods => foods; - int IHackAndSlashV1.WorldId => worldId; - int IHackAndSlashV1.StageId => stageId; - Address IHackAndSlashV1.AvatarAddress => avatarAddress; - Address IHackAndSlashV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV1.RankingMapAddress => RankingMapAddress; - - public BattleLog Result { get; private set; } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToInteger()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - 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(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - Log.Warning("{AddressesHex}hack_and_slash is deprecated. Please use hack_and_slash2", addressesHex); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates( - ctx.Signer, - avatarAddress, - out AgentState agentState, - 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(); - - 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(); - 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); - - 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(costumes)); - - avatarState.EquipEquipments(equipments); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1() - ); - - 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(); - 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(); - avatarState.UpdateQuestRewards2(materialSheet); - - 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.UpdateV1(avatarState, characterSheet); - weekly.Update(info); - } - else - { - weekly.Set(avatarState, characterSheet); - } - - 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.SetState(ctx.Signer, agentState.Serialize()); - } - } -} diff --git a/Lib9c/Action/HackAndSlash10.cs b/Lib9c/Action/HackAndSlash10.cs deleted file mode 100644 index 853895efcd..0000000000 --- a/Lib9c/Action/HackAndSlash10.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash10")] - public class HackAndSlash10 : GameAction, IHackAndSlashV4 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - public Address rankingMapAddress; - - IEnumerable IHackAndSlashV4.Costumes => costumes; - IEnumerable IHackAndSlashV4.Equipments => equipments; - IEnumerable IHackAndSlashV4.Foods => foods; - int IHackAndSlashV4.WorldId => worldId; - int IHackAndSlashV4.StageId => stageId; - int IHackAndSlashV4.PlayCount => playCount; - Address IHackAndSlashV4.AvatarAddress => avatarAddress; - Address IHackAndSlashV4.RankingMapAddress => rankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["rankingMapAddress"] = rankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - rankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(rankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100170ObsoleteIndex, 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.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - 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"); - } - - var worldSheet = states.GetSheet(); - 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(); - 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}" - ); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException($"{addressesHex}{worldId} can't execute HackAndSlash action."); - } - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - - avatarState.actionPoint -= totalCostActionPoint; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - var questSheet = states.GetQuestSheet(); - if (questList.Count() < questSheet.Count) - { - questList.UpdateList( - questSheet, - states.GetSheet(), - states.GetSheet(), - states.GetSheet()); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (simulator.Log.IsClear && states.TryGetState(rankingMapAddress, out Dictionary d)) - { - 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(); - - TimeSpan totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash11.cs b/Lib9c/Action/HackAndSlash11.cs deleted file mode 100644 index 5649d653a2..0000000000 --- a/Lib9c/Action/HackAndSlash11.cs +++ /dev/null @@ -1,276 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash11")] - public class HackAndSlash11 : GameAction, IHackAndSlashV5 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - - IEnumerable IHackAndSlashV5.Costumes => costumes; - IEnumerable IHackAndSlashV5.Equipments => equipments; - IEnumerable IHackAndSlashV5.Foods => foods; - int IHackAndSlashV5.WorldId => worldId; - int IHackAndSlashV5.StageId => stageId; - int IHackAndSlashV5.PlayCount => playCount; - Address IHackAndSlashV5.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100190ObsoleteIndex, 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.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - 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(); - - var worldSheet = states.GetSheet(); - 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(); - 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}" - ); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException($"{addressesHex}{worldId} can't execute HackAndSlash action."); - } - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - - avatarState.actionPoint -= totalCostActionPoint; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - var questSheet = states.GetQuestSheet(); - if (questList.Count() < questSheet.Count) - { - questList.UpdateList( - questSheet, - states.GetSheet(), - states.GetSheet(), - states.GetSheet()); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - TimeSpan totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash12.cs b/Lib9c/Action/HackAndSlash12.cs deleted file mode 100644 index 61cadd734b..0000000000 --- a/Lib9c/Action/HackAndSlash12.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash12")] - public class HackAndSlash12 : GameAction, IHackAndSlashV5 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - - IEnumerable IHackAndSlashV5.Costumes => costumes; - IEnumerable IHackAndSlashV5.Equipments => equipments; - IEnumerable IHackAndSlashV5.Foods => foods; - int IHackAndSlashV5.WorldId => worldId; - int IHackAndSlashV5.StageId => stageId; - int IHackAndSlashV5.PlayCount => playCount; - Address IHackAndSlashV5.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100260ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containQuestSheet: true, - containStageSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - sw.Restart(); - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get StageSheet: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - 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}" - ); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException($"{addressesHex}{worldId} can't execute HackAndSlash action."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate World: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate Items: {Elapsed}", addressesHex, sw.Elapsed); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= totalCostActionPoint; - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - sheets.GetStageSimulatorSheetsV100291(), - sheets.GetSheet(), - StageSimulatorV1.ConstructorVersionV100080, - playCount); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(playCount); - 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 - ); - - if (simulator.Log.IsClear) - { - sw.Restart(); - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - sheets.GetSheet() - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(sheets.GetSheet()); - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash13.cs b/Lib9c/Action/HackAndSlash13.cs deleted file mode 100644 index 044e8e4216..0000000000 --- a/Lib9c/Action/HackAndSlash13.cs +++ /dev/null @@ -1,317 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/921 - /// Updated at https://github.com/planetarium/lib9c/pull/1176 - /// Obsoleted at https://github.com/planetarium/lib9c/pull/1241 - /// - [Serializable] - [ActionType("hack_and_slash13")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class HackAndSlash13 : GameAction, IHackAndSlashV6 - { - private const long ObsoletedBlockIndex = - ActionObsoleteConfig.V100270ObsoleteIndex; - - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - - IEnumerable IHackAndSlashV6.Costumes => costumes; - IEnumerable IHackAndSlashV6.Equipments => equipments; - IEnumerable IHackAndSlashV6.Foods => foods; - int IHackAndSlashV6.WorldId => worldId; - int IHackAndSlashV6.StageId => stageId; - Address IHackAndSlashV6.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ObsoletedBlockIndex, context); - - var arenaSheetAddress = Addresses.GetSheetAddress(); - var arenaSheetState = states.GetState(arenaSheetAddress); - if (arenaSheetState != null) - { - // exception handling for v100240. - if (context.BlockIndex > 4374125 && context.BlockIndex < 4374158) - { - } - else - { - throw new ActionObsoletedException(nameof(HackAndSlash13)); - } - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containQuestSheet: true, - containStageSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - sw.Restart(); - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get StageSheet: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - 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}" - ); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException($"{addressesHex}{worldId} can't execute HackAndSlash action."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate World: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate Items: {Elapsed}", addressesHex, sw.Elapsed); - - if (avatarState.actionPoint < stageRow.CostAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < cost({stageRow.CostAP}))" - ); - } - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= stageRow.CostAP; - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - sheets.GetStageSimulatorSheetsV100291(), - sheets.GetSheet(), - StageSimulatorV1.ConstructorVersionV100080); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(1); - 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 - ); - - if (simulator.Log.IsClear) - { - sw.Restart(); - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - sheets.GetSheet() - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(sheets.GetSheet()); - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash14.cs b/Lib9c/Action/HackAndSlash14.cs deleted file mode 100644 index 6216e5efea..0000000000 --- a/Lib9c/Action/HackAndSlash14.cs +++ /dev/null @@ -1,381 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/967 - /// Updated at https://github.com/planetarium/lib9c/pull/1167 - /// Obsoleted at https://github.com/planetarium/lib9c/pull/1241 - /// - [Serializable] - [ActionType("hack_and_slash14")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class HackAndSlash14 : GameAction, IHackAndSlashV7 - { - private const long ObsoletedBlockIndex = - ActionObsoleteConfig.V100270ObsoleteIndex; - - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int? stageBuffId; - public Address avatarAddress; - - IEnumerable IHackAndSlashV7.Costumes => costumes; - IEnumerable IHackAndSlashV7.Equipments => equipments; - IEnumerable IHackAndSlashV7.Foods => foods; - int IHackAndSlashV7.WorldId => worldId; - int IHackAndSlashV7.StageId => stageId; - int? IHackAndSlashV7.StageBuffId => stageBuffId; - Address IHackAndSlashV7.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }; - if (stageBuffId.HasValue) - { - dict["stageBuffId"] = stageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - stageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states; - } - - CheckObsolete(ObsoletedBlockIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - states.ValidateWorldId(avatarAddress, worldId); - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containQuestSheet: true, - containStageSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - sw.Restart(); - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get StageSheet: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - 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}" - ); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate World: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate Items: {Elapsed}", addressesHex, sw.Elapsed); - - if (avatarState.actionPoint < stageRow.CostAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < cost({stageRow.CostAP}))" - ); - } - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= stageRow.CostAP; - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(avatarAddress); - CrystalRandomSkillState skillState = null; - var isNotClearedStage = !worldInformation.IsStageCleared(stageId); - var skillsOnWaveStart = new List(); - if (isNotClearedStage) - { - // It has state, get CrystalRandomSkillState. If not, newly make. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, stageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (stageBuffId.HasValue && skillState.SkillIds.Contains(stageBuffId.Value)) - { - selectedId = stageBuffId.Value; - } - else - { - selectedId = skillState.SkillIds - .OrderBy(id => crystalRandomBuffSheet[id].Rank) - .ThenBy(id => id) - .First(); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - skillsOnWaveStart, - worldId, - stageId, - sheets.GetStageSimulatorSheetsV100291(), - sheets.GetSheet(), - StageSimulatorV1.ConstructorVersionV100080); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(1); - 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 - ); - - if (simulator.Log.IsClear) - { - sw.Restart(); - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - sheets.GetSheet() - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - if (isNotClearedStage) - { - // Make new CrystalRandomSkillState by next stage Id. - var nextStageSkillState = new CrystalRandomSkillState(skillStateAddress, stageId + 1); - states = states.SetState(skillStateAddress, nextStageSkillState.Serialize()); - } - } - else - { - if (isNotClearedStage) - { - if (skillsOnWaveStart.Any()) - { - // clear current star count, skill id. - skillState = new CrystalRandomSkillState(skillStateAddress, stageId); - } - - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState!.Update(simulator.Log.clearedWaveNumber, - sheets.GetSheet()); - states = states.SetState(skillStateAddress, skillState!.Serialize()); - } - } - - sw.Restart(); - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(sheets.GetSheet()); - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash15.cs b/Lib9c/Action/HackAndSlash15.cs deleted file mode 100644 index 85ff5463e9..0000000000 --- a/Lib9c/Action/HackAndSlash15.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1222 - /// Updated at https://github.com/planetarium/lib9c/pull/1225 - /// - [Serializable] - [ActionType("hack_and_slash15")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class HackAndSlash15 : GameAction, IHackAndSlashV7 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int? stageBuffId; - public Address avatarAddress; - - IEnumerable IHackAndSlashV7.Costumes => costumes; - IEnumerable IHackAndSlashV7.Equipments => equipments; - IEnumerable IHackAndSlashV7.Foods => foods; - int IHackAndSlashV7.WorldId => worldId; - int IHackAndSlashV7.StageId => stageId; - int? IHackAndSlashV7.StageBuffId => stageBuffId; - Address IHackAndSlashV7.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }; - if (stageBuffId.HasValue) - { - dict["stageBuffId"] = stageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - stageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - return Execute(context.PreviousState, - context.Signer, - context.BlockIndex, - context.Random); - } - - public IAccountStateDelta Execute(IAccountStateDelta states, - Address signer, - long blockIndex, - IRandom random) - { - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - var addressesHex = $"[{signer.ToHex()}, {avatarAddress.ToHex()}]"; - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - states.ValidateWorldId(avatarAddress, worldId); - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containQuestSheet: true, - containStageSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - // Validate about avatar state. - Validator.ValidateForHackAndSlashV1(avatarState, - sheets, - worldId, - stageId, - equipments, - costumes, - foods, - sw, - blockIndex, - addressesHex); - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.actionPoint -= sheets.GetSheet()[stageId].CostAP; - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(avatarAddress); - CrystalRandomSkillState skillState = null; - var isNotClearedStage = !avatarState.worldInformation.IsStageCleared(stageId); - var skillsOnWaveStart = new List(); - if (isNotClearedStage) - { - // It has state, get CrystalRandomSkillState. If not, newly make. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, stageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (stageBuffId.HasValue && skillState.SkillIds.Contains(stageBuffId.Value)) - { - selectedId = stageBuffId.Value; - } - else - { - selectedId = skillState.SkillIds - .OrderBy(id => crystalRandomBuffSheet[id].Rank) - .ThenBy(id => id) - .First(); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var simulator = new StageSimulatorV1( - random, - avatarState, - foods, - skillsOnWaveStart, - worldId, - stageId, - sheets.GetStageSimulatorSheetsV100291(), - sheets.GetSheet(), - StageSimulatorV1.ConstructorVersionV100080); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(1); - 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 - ); - - if (simulator.Log.IsClear) - { - sw.Restart(); - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - blockIndex, - sheets.GetSheet(), - sheets.GetSheet() - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - if (isNotClearedStage) - { - // Make new CrystalRandomSkillState by next stage Id. - var nextStageSkillState = new CrystalRandomSkillState(skillStateAddress, stageId + 1); - states = states.SetState(skillStateAddress, nextStageSkillState.Serialize()); - } - } - else - { - if (isNotClearedStage) - { - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState!.Update(simulator.Log.clearedWaveNumber, - sheets.GetSheet()); - // clear current skill id. - skillState!.Update(new List()); - states = states.SetState(skillStateAddress, skillState!.Serialize()); - } - } - - sw.Restart(); - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(sheets.GetSheet()); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash16.cs b/Lib9c/Action/HackAndSlash16.cs deleted file mode 100644 index 06b46a0215..0000000000 --- a/Lib9c/Action/HackAndSlash16.cs +++ /dev/null @@ -1,355 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1229 - /// Updated at https://github.com/planetarium/lib9c/pull/1241 - /// Updated at https://github.com/planetarium/lib9c/pull/1244 - /// - [Serializable] - [ActionType("hack_and_slash16")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class HackAndSlash16 : GameAction, IHackAndSlashV8 - { - public List Costumes; - public List Equipments; - public List Foods; - public int WorldId; - public int StageId; - public int? StageBuffId; - public Address AvatarAddress; - public int PlayCount = 1; - - IEnumerable IHackAndSlashV8.Costumes => Costumes; - IEnumerable IHackAndSlashV8.Equipments => Equipments; - IEnumerable IHackAndSlashV8.Foods => Foods; - int IHackAndSlashV8.WorldId => WorldId; - int IHackAndSlashV8.StageId => StageId; - int IHackAndSlashV8.PlayCount => PlayCount; - int? IHackAndSlashV8.StageBuffId => StageBuffId; - Address IHackAndSlashV8.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - ["playCount"] = PlayCount.Serialize(), - }; - if (StageBuffId.HasValue) - { - dict["stageBuffId"] = StageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - StageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - PlayCount = plainValue["playCount"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - context.Random); - } - - public IAccountStateDelta Execute( - IAccountStateDelta states, - Address signer, - long blockIndex, - IRandom random) - { - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - var addressesHex = $"[{signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - states.ValidateWorldId(AvatarAddress, WorldId); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {PlayCount}"); - } - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containQuestSheet: true, - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - // Validate about avatar state. - Validator.ValidateForHackAndSlashV1(avatarState, - sheets, - WorldId, - StageId, - Equipments, - Costumes, - Foods, - sw, - blockIndex, - addressesHex, - PlayCount); - - var items = Equipments.Concat(Costumes); - avatarState.EquipItems(items); - avatarState.actionPoint -= sheets.GetSheet()[StageId].CostAP * PlayCount; - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(AvatarAddress); - var isNotClearedStage = !avatarState.worldInformation.IsStageCleared(StageId); - var skillsOnWaveStart = new List(); - CrystalRandomSkillState skillState = null; - if (isNotClearedStage) - { - // It has state, get CrystalRandomSkillState. If not, newly make. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, StageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (StageBuffId.HasValue && skillState.SkillIds.Contains(StageBuffId.Value)) - { - selectedId = StageBuffId.Value; - } - else - { - selectedId = skillState.SkillIds - .OrderBy(id => crystalRandomBuffSheet[id].Rank) - .ThenBy(id => id) - .First(); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - var worldUnlockSheet = sheets.GetSheet(); - var crystalStageBuffSheet = sheets.GetSheet(); - var stageRow = sheets.GetSheet()[StageId]; - var materialItemSheet = sheets.GetSheet(); - sw.Restart(); - // if PlayCount > 1, it is Multi-HAS. - for (var i = 0; i < PlayCount; i++) - { - sw.Restart(); - // First simulating will use Foods and Random Skills. - // Remainder simulating will not use Foods. - var simulator = new StageSimulatorV2( - random, - avatarState, - i == 0 ? Foods : new List(), - i == 0 ? skillsOnWaveStart : new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - StageRewardExpHelper.GetExp(avatarState.level, StageId), - sheets.GetSimulatorSheetsV100291(), - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards(random, stageRow, materialItemSheet)); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - avatarState.Update(simulator); - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Update avatar by simulator({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.Stop(); - Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}", - addressesHex, sw.Elapsed, PlayCount); - - sw.Restart(); - avatarState.UpdateQuestRewards(materialItemSheet); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (isNotClearedStage) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var lastClearedStageId); - if (lastClearedStageId >= StageId) - { - // Make new CrystalRandomSkillState by next stage Id. - skillState = new CrystalRandomSkillState(skillStateAddress, StageId + 1); - } - - skillState.Update(new List()); - states = states.SetState(skillStateAddress, skillState.Serialize()); - } - - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash17.cs b/Lib9c/Action/HackAndSlash17.cs deleted file mode 100644 index bd4a61c714..0000000000 --- a/Lib9c/Action/HackAndSlash17.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1229 - /// - [Serializable] - [ActionType("hack_and_slash17")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class HackAndSlash17 : GameAction, IHackAndSlashV8 - { - public List Costumes; - public List Equipments; - public List Foods; - public int WorldId; - public int StageId; - public int? StageBuffId; - public Address AvatarAddress; - public int PlayCount = 1; - - IEnumerable IHackAndSlashV8.Costumes => Costumes; - IEnumerable IHackAndSlashV8.Equipments => Equipments; - IEnumerable IHackAndSlashV8.Foods => Foods; - int IHackAndSlashV8.WorldId => WorldId; - int IHackAndSlashV8.StageId => StageId; - int IHackAndSlashV8.PlayCount => PlayCount; - int? IHackAndSlashV8.StageBuffId => StageBuffId; - Address IHackAndSlashV8.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - ["playCount"] = PlayCount.Serialize(), - }; - if (StageBuffId.HasValue) - { - dict["stageBuffId"] = StageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - StageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - PlayCount = plainValue["playCount"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - context.Random); - } - - public IAccountStateDelta Execute( - IAccountStateDelta states, - Address signer, - long blockIndex, - IRandom random) - { - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - var addressesHex = $"[{signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - states.ValidateWorldId(AvatarAddress, WorldId); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {PlayCount}"); - } - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containQuestSheet: true, - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - // Validate about avatar state. - Validator.ValidateForHackAndSlash(avatarState, - sheets, - WorldId, - StageId, - Equipments, - Costumes, - Foods, - sw, - blockIndex, - addressesHex, - PlayCount); - - var items = Equipments.Concat(Costumes); - avatarState.EquipItems(items); - avatarState.actionPoint -= sheets.GetSheet()[StageId].CostAP * PlayCount; - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(AvatarAddress); - var isNotClearedStage = !avatarState.worldInformation.IsStageCleared(StageId); - var skillsOnWaveStart = new List(); - CrystalRandomSkillState skillState = null; - if (isNotClearedStage) - { - // It has state, get CrystalRandomSkillState. If not, newly make. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, StageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (StageBuffId.HasValue && skillState.SkillIds.Contains(StageBuffId.Value)) - { - selectedId = StageBuffId.Value; - } - else - { - selectedId = skillState.SkillIds - .OrderBy(id => crystalRandomBuffSheet[id].Rank) - .ThenBy(id => id) - .First(); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - var worldUnlockSheet = sheets.GetSheet(); - var crystalStageBuffSheet = sheets.GetSheet(); - var stageRow = sheets.GetSheet()[StageId]; - var materialItemSheet = sheets.GetSheet(); - sw.Restart(); - // if PlayCount > 1, it is Multi-HAS. - for (var i = 0; i < PlayCount; i++) - { - sw.Restart(); - // First simulating will use Foods and Random Skills. - // Remainder simulating will not use Foods. - var simulator = new StageSimulatorV2( - random, - avatarState, - i == 0 ? Foods : new List(), - i == 0 ? skillsOnWaveStart : new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - StageRewardExpHelper.GetExp(avatarState.level, StageId), - sheets.GetSimulatorSheetsV100291(), - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards(random, stageRow, materialItemSheet)); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - avatarState.Update(simulator); - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Update avatar by simulator({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.Stop(); - Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}", - addressesHex, sw.Elapsed, PlayCount); - - sw.Restart(); - avatarState.UpdateQuestRewards(materialItemSheet); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (isNotClearedStage) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var lastClearedStageId); - if (lastClearedStageId >= StageId) - { - // Make new CrystalRandomSkillState by next stage Id. - skillState = new CrystalRandomSkillState(skillStateAddress, StageId + 1); - } - - skillState.Update(new List()); - states = states.SetState(skillStateAddress, skillState.Serialize()); - } - - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash18.cs b/Lib9c/Action/HackAndSlash18.cs deleted file mode 100644 index dbabaa1fab..0000000000 --- a/Lib9c/Action/HackAndSlash18.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1338 - /// - [Serializable] - [ActionType("hack_and_slash18")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class HackAndSlash18 : GameAction, IHackAndSlashV8 - { - public List Costumes; - public List Equipments; - public List Foods; - public int WorldId; - public int StageId; - public int? StageBuffId; - public Address AvatarAddress; - public int PlayCount = 1; - - IEnumerable IHackAndSlashV8.Costumes => Costumes; - IEnumerable IHackAndSlashV8.Equipments => Equipments; - IEnumerable IHackAndSlashV8.Foods => Foods; - int IHackAndSlashV8.WorldId => WorldId; - int IHackAndSlashV8.StageId => StageId; - int IHackAndSlashV8.PlayCount => PlayCount; - int? IHackAndSlashV8.StageBuffId => StageBuffId; - Address IHackAndSlashV8.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - ["playCount"] = PlayCount.Serialize(), - }; - if (StageBuffId.HasValue) - { - dict["stageBuffId"] = StageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - StageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - PlayCount = plainValue["playCount"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - context.Random); - } - - public IAccountStateDelta Execute( - IAccountStateDelta states, - Address signer, - long blockIndex, - IRandom random) - { - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - var addressesHex = $"[{signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HAS exec started", addressesHex); - - states.ValidateWorldId(AvatarAddress, WorldId); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {PlayCount}"); - } - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - // FIXME Delete this check next hard fork. - bool useV100291Sheets = UseV100291Sheets(blockIndex); - var sheets = useV100291Sheets - ? states.GetSheetsV100291( - containQuestSheet: true, - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - typeof(StakeActionPointCoefficientSheet), - }) - : states.GetSheetsV1( - containQuestSheet: true, - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - typeof(StakeActionPointCoefficientSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var stakingLevel = 0; - StakeActionPointCoefficientSheet actionPointCoefficientSheet = null; - if (states.TryGetStakeState(signer, out var stakeState) && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - stakingLevel = actionPointCoefficientSheet.FindLevelByStakedAmount(signer, stakedAmount); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Check StakeState: {Elapsed}", addressesHex, sw.Elapsed); - - // Validate about avatar state. - Validator.ValidateForHackAndSlash(avatarState, - sheets, - WorldId, - StageId, - Equipments, - Costumes, - Foods, - sw, - blockIndex, - addressesHex, - PlayCount, - stakingLevel); - var costAp = sheets.GetSheet()[StageId].CostAP; - if (actionPointCoefficientSheet != null && stakingLevel > 0) - { - costAp = actionPointCoefficientSheet.GetActionPointByStaking( - costAp, - PlayCount, - stakingLevel); - } - else - { - costAp *= PlayCount; - } - - avatarState.actionPoint -= costAp; - - var items = Equipments.Concat(Costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(AvatarAddress); - var isNotClearedStage = !avatarState.worldInformation.IsStageCleared(StageId); - var skillsOnWaveStart = new List(); - CrystalRandomSkillState skillState = null; - if (isNotClearedStage) - { - // If state exists, get CrystalRandomSkillState. If not, create new state. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, StageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (StageBuffId.HasValue && skillState.SkillIds.Contains(StageBuffId.Value)) - { - selectedId = StageBuffId.Value; - } - else - { - selectedId = skillState.GetHighestRankSkill(crystalRandomBuffSheet); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - var worldUnlockSheet = sheets.GetSheet(); - var crystalStageBuffSheet = sheets.GetSheet(); - var stageRow = sheets.GetSheet()[StageId]; - var materialItemSheet = sheets.GetSheet(); - sw.Restart(); - // if PlayCount > 1, it is Multi-HAS. - var simulatorSheets = useV100291Sheets - ? sheets.GetSimulatorSheetsV100291() - : sheets.GetSimulatorSheetsV1(); - for (var i = 0; i < PlayCount; i++) - { - sw.Restart(); - // First simulating will use Foods and Random Skills. - // Remainder simulating will not use Foods. - var simulator = new StageSimulatorV2( - random, - avatarState, - i == 0 ? Foods : new List(), - i == 0 ? skillsOnWaveStart : new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - StageRewardExpHelper.GetExp(avatarState.level, StageId), - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards(random, stageRow, materialItemSheet)); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - avatarState.Update(simulator); - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Update avatar by simulator({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.Stop(); - Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}", - addressesHex, sw.Elapsed, PlayCount); - - sw.Restart(); - avatarState.UpdateQuestRewards(materialItemSheet); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (isNotClearedStage) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var lastClearedStageId); - if (lastClearedStageId >= StageId) - { - // Make new CrystalRandomSkillState by next stage Id. - skillState = new CrystalRandomSkillState(skillStateAddress, StageId + 1); - } - - skillState.Update(new List()); - states = states.SetState(skillStateAddress, skillState.Serialize()); - } - - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Debug("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - - } -} diff --git a/Lib9c/Action/HackAndSlash19.cs b/Lib9c/Action/HackAndSlash19.cs deleted file mode 100644 index 287029fed6..0000000000 --- a/Lib9c/Action/HackAndSlash19.cs +++ /dev/null @@ -1,436 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; -using Skill = Nekoyume.Model.Skill.Skill; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1495 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash19")] - public class HackAndSlash19 : GameAction, IHackAndSlashV9 - { - public List Costumes; - public List Equipments; - public List Foods; - public List RuneInfos; - public int WorldId; - public int StageId; - public int? StageBuffId; - public Address AvatarAddress; - public int PlayCount = 1; - - IEnumerable IHackAndSlashV9.Costumes => Costumes; - IEnumerable IHackAndSlashV9.Equipments => Equipments; - IEnumerable IHackAndSlashV9.Foods => Foods; - IEnumerable IHackAndSlashV9.RuneSlotInfos => RuneInfos.Select(x => x.Serialize()); - int IHackAndSlashV9.WorldId => WorldId; - int IHackAndSlashV9.StageId => StageId; - int IHackAndSlashV9.PlayCount => PlayCount; - int? IHackAndSlashV9.StageBuffId => StageBuffId; - Address IHackAndSlashV9.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - ["playCount"] = PlayCount.Serialize(), - }; - if (StageBuffId.HasValue) - { - dict["stageBuffId"] = StageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - StageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - PlayCount = plainValue["playCount"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - context.Random); - } - - public IAccountStateDelta Execute( - IAccountStateDelta states, - Address signer, - long blockIndex, - IRandom random) - { - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - var addressesHex = $"[{signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HAS exec started", addressesHex); - - states.ValidateWorldId(AvatarAddress, WorldId); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {PlayCount}"); - } - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheets( - containQuestSheet: true, - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - typeof(StakeActionPointCoefficientSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var stakingLevel = 0; - StakeActionPointCoefficientSheet actionPointCoefficientSheet = null; - if (states.TryGetStakeState(signer, out var stakeState) && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - stakingLevel = actionPointCoefficientSheet.FindLevelByStakedAmount(signer, stakedAmount); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Check StakeState: {Elapsed}", addressesHex, sw.Elapsed); - - // Validate about avatar state. - Validator.ValidateForHackAndSlash(avatarState, - sheets, - WorldId, - StageId, - Equipments, - Costumes, - Foods, - sw, - blockIndex, - addressesHex, - PlayCount, - stakingLevel); - var costAp = sheets.GetSheet()[StageId].CostAP; - if (actionPointCoefficientSheet != null && stakingLevel > 0) - { - costAp = actionPointCoefficientSheet.GetActionPointByStaking( - costAp, - PlayCount, - stakingLevel); - } - else - { - costAp *= PlayCount; - } - - avatarState.actionPoint -= costAp; - - var items = Equipments.Concat(Costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(AvatarAddress); - var isNotClearedStage = !avatarState.worldInformation.IsStageCleared(StageId); - var skillsOnWaveStart = new List(); - CrystalRandomSkillState skillState = null; - if (isNotClearedStage) - { - // If state exists, get CrystalRandomSkillState. If not, create new state. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, StageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (StageBuffId.HasValue && skillState.SkillIds.Contains(StageBuffId.Value)) - { - selectedId = StageBuffId.Value; - } - else - { - selectedId = skillState.GetHighestRankSkill(crystalRandomBuffSheet); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - var worldUnlockSheet = sheets.GetSheet(); - var crystalStageBuffSheet = sheets.GetSheet(); - var stageRow = sheets.GetSheet()[StageId]; - var materialItemSheet = sheets.GetSheet(); - sw.Restart(); - // if PlayCount > 1, it is Multi-HAS. - var simulatorSheets = sheets.GetSimulatorSheets(); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlotV2(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - for (var i = 0; i < PlayCount; i++) - { - sw.Restart(); - // First simulating will use Foods and Random Skills. - // Remainder simulating will not use Foods. - var simulator = new StageSimulatorV3( - random, - avatarState, - i == 0 ? Foods : new List(), - runeStates, - i == 0 ? skillsOnWaveStart : new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - StageRewardExpHelper.GetExp(avatarState.level, StageId), - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards(random, stageRow, materialItemSheet)); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - // This conditional logic is same as written in the - // MimisbrunnrBattle("mimisbrunnr_battle10") action. - if (blockIndex < ActionObsoleteConfig.V100310ExecutedBlockIndex) - { - var player = simulator.Player; - foreach (var key in player.monsterMapForBeforeV100310.Keys) - { - player.monsterMap.Add(key, player.monsterMapForBeforeV100310[key]); - } - - player.monsterMapForBeforeV100310.Clear(); - - foreach (var key in player.eventMapForBeforeV100310.Keys) - { - player.eventMap.Add(key, player.eventMapForBeforeV100310[key]); - } - - player.eventMapForBeforeV100310.Clear(); - } - - avatarState.Update(simulator); - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Update avatar by simulator({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.Stop(); - Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}", - addressesHex, sw.Elapsed, PlayCount); - - sw.Restart(); - avatarState.UpdateQuestRewards(materialItemSheet); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (isNotClearedStage) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var lastClearedStageId); - if (lastClearedStageId >= StageId) - { - // Make new CrystalRandomSkillState by next stage Id. - skillState = new CrystalRandomSkillState(skillStateAddress, StageId + 1); - } - - skillState.Update(new List()); - states = states.SetState(skillStateAddress, skillState.Serialize()); - } - - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Debug("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - - } -} diff --git a/Lib9c/Action/HackAndSlash2.cs b/Lib9c/Action/HackAndSlash2.cs deleted file mode 100644 index 14eb192771..0000000000 --- a/Lib9c/Action/HackAndSlash2.cs +++ /dev/null @@ -1,306 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash2")] - public class HackAndSlash2 : GameAction, IHackAndSlashV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - public BattleLog Result { get; private set; } - - IEnumerable IHackAndSlashV1.Costumes => costumes; - IEnumerable IHackAndSlashV1.Equipments => equipments; - IEnumerable IHackAndSlashV1.Foods => foods; - int IHackAndSlashV1.WorldId => worldId; - int IHackAndSlashV1.StageId => stageId; - Address IHackAndSlashV1.AvatarAddress => avatarAddress; - Address IHackAndSlashV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV1.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToInteger()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - 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(ActionObsoleteConfig.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(); - - 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(); - 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(costumes)); - - var costumeStatSheet = states.GetSheet(); - - 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(costumes)); - - avatarState.EquipEquipments(equipments); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - 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(); - 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(); - 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; - } - } -} diff --git a/Lib9c/Action/HackAndSlash20.cs b/Lib9c/Action/HackAndSlash20.cs deleted file mode 100644 index 32ed1addb2..0000000000 --- a/Lib9c/Action/HackAndSlash20.cs +++ /dev/null @@ -1,567 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; - -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; -using Skill = Nekoyume.Model.Skill.Skill; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash20")] - public class HackAndSlash20 : GameAction, IHackAndSlashV10 - { - public const int UsableApStoneCount = 10; - - public List Costumes; - public List Equipments; - public List Foods; - public List RuneInfos; - public int WorldId; - public int StageId; - public int? StageBuffId; - public Address AvatarAddress; - public int TotalPlayCount = 1; - public int ApStoneCount = 0; - - IEnumerable IHackAndSlashV10.Costumes => Costumes; - IEnumerable IHackAndSlashV10.Equipments => Equipments; - IEnumerable IHackAndSlashV10.Foods => Foods; - IEnumerable IHackAndSlashV10.RuneSlotInfos => RuneInfos.Select(x => x.Serialize()); - int IHackAndSlashV10.WorldId => WorldId; - int IHackAndSlashV10.StageId => StageId; - int IHackAndSlashV10.TotalPlayCount => TotalPlayCount; - int IHackAndSlashV10.ApStoneCount => ApStoneCount; - int? IHackAndSlashV10.StageBuffId => StageBuffId; - Address IHackAndSlashV10.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = - new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - ["totalPlayCount"] = TotalPlayCount.Serialize(), - ["apStoneCount"] = ApStoneCount.Serialize(), - }; - if (StageBuffId.HasValue) - { - dict["stageBuffId"] = StageBuffId.Serialize(); - } - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - if (plainValue.ContainsKey("stageBuffId")) - { - StageBuffId = plainValue["stageBuffId"].ToNullableInteger(); - } - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - TotalPlayCount = plainValue["totalPlayCount"].ToInteger(); - ApStoneCount = plainValue["apStoneCount"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - if (context.Rehearsal) - { - return context.PreviousState; - } - - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - context.Random); - } - - public IAccountStateDelta Execute( - IAccountStateDelta states, - Address signer, - long blockIndex, - IRandom random) - { - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - var addressesHex = $"[{signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HAS exec started", addressesHex); - - if (ApStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException( - "Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {ApStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - if (ApStoneCount < 0) - { - throw new InvalidItemCountException( - "ApStone count must not be negative. " + - $"Ap stone count: {ApStoneCount}"); - } - - if (TotalPlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must not be zero or negative. " + - $"Total play count : {TotalPlayCount}"); - } - - states.ValidateWorldId(AvatarAddress, WorldId); - - var sw = new Stopwatch(); - sw.Start(); - if (!states.TryGetAvatarStateV2(signer, AvatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheets( - containQuestSheet: true, - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - typeof(StakeActionPointCoefficientSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var stakingLevel = 0; - StakeActionPointCoefficientSheet actionPointCoefficientSheet = null; - if (states.TryGetStakeState(signer, out var stakeState) && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - stakingLevel = actionPointCoefficientSheet.FindLevelByStakedAmount(signer, stakedAmount); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Check StakeState: {Elapsed}", addressesHex, sw.Elapsed); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - sw.Restart(); - if (!sheets.GetSheet().TryGetValue(StageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), StageId); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get StageSheet: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(WorldId, out var world)) - { - // NOTE: Add new World from WorldSheet - worldInformation.AddAndUnlockNewWorld(worldRow, blockIndex, worldSheet); - worldInformation.TryGetWorld(WorldId, out world); - } - - 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.StageBegin) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage ({WorldId}/{StageId - 1}) is not cleared; " + - $"clear the stage ({world.Id}/{world.StageBegin}) first" - ); - } - - if (world.IsStageCleared && StageId - 1 > world.StageClearedId) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage ({WorldId}/{StageId - 1}) is not cleared; " + - $"cleared stage is ({world.Id}/{world.StageClearedId}), so you can play stage " + - $"({world.Id}/{world.StageClearedId + 1})" - ); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate World: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, blockIndex); - var foodIds = avatarState.ValidateConsumable(Foods, blockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate Items: {Elapsed}", addressesHex, sw.Elapsed); - - var materialItemSheet = sheets.GetSheet(); - var apPlayCount = TotalPlayCount; - var minimumCostAp = stageRow.CostAP; - if (actionPointCoefficientSheet != null && stakingLevel > 0) - { - minimumCostAp = actionPointCoefficientSheet.GetActionPointByStaking( - minimumCostAp, - 1, - stakingLevel); - } - - if (ApStoneCount > 0) - { - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, blockIndex, - count: ApStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - - var apStonePlayCount = - ApStoneCount * (gameConfigState.ActionPointMax / minimumCostAp); - apPlayCount = TotalPlayCount - apStonePlayCount; - if (apPlayCount < 0) - { - throw new InvalidRepeatPlayException( - $"{addressesHex}Invalid TotalPlayCount({TotalPlayCount}) and ApStoneCount({ApStoneCount}). " + - $"TotalPlayCount must be at least calculated apStonePlayCount({apStonePlayCount}). " + - $"Calculated ap play count: {apPlayCount}"); - } - - Log.Verbose( - "{AddressesHex}TotalPlayCount: {TotalPlayCount}, " + - "ApStoneCount: {ApStoneCount}, PlayCount by Ap stone: {ApStonePlayCount}, " + - "Ap cost per 1 play: {MinimumCostAp}, " + - "PlayCount by action point: {ApPlayCount}, Used AP: {UsedAp}", - addressesHex, - TotalPlayCount, - ApStoneCount, - apStonePlayCount, - minimumCostAp, - apPlayCount, - apPlayCount * minimumCostAp); - } - - if (avatarState.actionPoint < minimumCostAp * apPlayCount) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < cost({minimumCostAp * apPlayCount}))" - ); - } - - avatarState.actionPoint -= minimumCostAp * apPlayCount; - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var items = Equipments.Concat(Costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS GetQuestSheet: {Elapsed}", addressesHex, sw.Elapsed); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - if (questList.Count() < questSheet.Count) - { - sw.Restart(); - questList.UpdateList( - questSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - var skillStateAddress = Addresses.GetSkillStateAddressFromAvatarAddress(AvatarAddress); - var isNotClearedStage = !avatarState.worldInformation.IsStageCleared(StageId); - var skillsOnWaveStart = new List(); - CrystalRandomSkillState skillState = null; - if (isNotClearedStage) - { - // If state exists, get CrystalRandomSkillState. If not, create new state. - skillState = states.TryGetState(skillStateAddress, out var serialized) - ? new CrystalRandomSkillState(skillStateAddress, serialized) - : new CrystalRandomSkillState(skillStateAddress, StageId); - - if (skillState.SkillIds.Any()) - { - var crystalRandomBuffSheet = sheets.GetSheet(); - var skillSheet = sheets.GetSheet(); - int selectedId; - if (StageBuffId.HasValue && skillState.SkillIds.Contains(StageBuffId.Value)) - { - selectedId = StageBuffId.Value; - } - else - { - selectedId = skillState.GetHighestRankSkill(crystalRandomBuffSheet); - } - - var skill = CrystalRandomSkillState.GetSkill( - selectedId, - crystalRandomBuffSheet, - skillSheet); - skillsOnWaveStart.Add(skill); - } - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get skillState : {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var worldUnlockSheet = sheets.GetSheet(); - var crystalStageBuffSheet = sheets.GetSheet(); - sw.Restart(); - // if PlayCount > 1, it is Multi-HAS. - var simulatorSheets = sheets.GetSimulatorSheets(); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - for (var i = 0; i < TotalPlayCount; i++) - { - sw.Restart(); - // First simulating will use Foods and Random Skills. - // Remainder simulating will not use Foods. - var simulator = new StageSimulatorV3( - random, - avatarState, - i == 0 ? Foods : new List(), - runeStates, - i == 0 ? skillsOnWaveStart : new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - StageRewardExpHelper.GetExp(avatarState.level, StageId), - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards(random, stageRow, materialItemSheet)); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - - // This conditional logic is same as written in the - // MimisbrunnrBattle("mimisbrunnr_battle10") action. - if (blockIndex < ActionObsoleteConfig.V100310ExecutedBlockIndex) - { - var player = simulator.Player; - foreach (var key in player.monsterMapForBeforeV100310.Keys) - { - player.monsterMap.Add(key, player.monsterMapForBeforeV100310[key]); - } - - player.monsterMapForBeforeV100310.Clear(); - - foreach (var key in player.eventMapForBeforeV100310.Keys) - { - player.eventMap.Add(key, player.eventMapForBeforeV100310[key]); - } - - player.eventMapForBeforeV100310.Clear(); - } - - avatarState.Update(simulator); - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Update avatar by simulator({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.Stop(); - Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}", - addressesHex, sw.Elapsed, TotalPlayCount); - - sw.Restart(); - avatarState.UpdateQuestRewards(materialItemSheet); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (isNotClearedStage) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var lastClearedStageId); - if (lastClearedStageId >= StageId) - { - // Make new CrystalRandomSkillState by next stage Id. - skillState = new CrystalRandomSkillState(skillStateAddress, StageId + 1); - } - - skillState.Update(new List()); - states = states.SetState(skillStateAddress, skillState.Serialize()); - } - - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set States: {Elapsed}", addressesHex, sw.Elapsed); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Debug("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - - } -} diff --git a/Lib9c/Action/HackAndSlash3.cs b/Lib9c/Action/HackAndSlash3.cs deleted file mode 100644 index 8c669ba881..0000000000 --- a/Lib9c/Action/HackAndSlash3.cs +++ /dev/null @@ -1,300 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash3")] - public class HackAndSlash3 : GameAction, IHackAndSlashV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - public BattleLog Result { get; private set; } - - IEnumerable IHackAndSlashV1.Costumes => costumes; - IEnumerable IHackAndSlashV1.Equipments => equipments; - IEnumerable IHackAndSlashV1.Foods => foods; - int IHackAndSlashV1.WorldId => worldId; - int IHackAndSlashV1.StageId => stageId; - Address IHackAndSlashV1.AvatarAddress => avatarAddress; - Address IHackAndSlashV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV1.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToInteger()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - 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(ActionObsoleteConfig.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(); - - 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(); - 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(costumes)); - - var costumeStatSheet = states.GetSheet(); - - 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(costumes)); - - avatarState.EquipEquipments(equipments); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - 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(); - 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(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUpV1(); - 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; - } - } -} diff --git a/Lib9c/Action/HackAndSlash4.cs b/Lib9c/Action/HackAndSlash4.cs deleted file mode 100644 index da49d32330..0000000000 --- a/Lib9c/Action/HackAndSlash4.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash4")] - public class HackAndSlash4 : GameAction, IHackAndSlashV2 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - public BattleLog Result { get; private set; } - - IEnumerable IHackAndSlashV2.Costumes => costumes; - IEnumerable IHackAndSlashV2.Equipments => equipments; - IEnumerable IHackAndSlashV2.Foods => foods; - int IHackAndSlashV2.WorldId => worldId; - int IHackAndSlashV2.StageId => stageId; - Address IHackAndSlashV2.AvatarAddress => avatarAddress; - Address IHackAndSlashV2.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV2.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - 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(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("030b3528-f8b2-4375-8d58-0eccaa6f7fc9"))) - { - return states; - } - - 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(); - - 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(); - 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.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - var costumeStatSheet = states.GetSheet(); - - 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; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV2(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - 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; - } - } -} diff --git a/Lib9c/Action/HackAndSlash5.cs b/Lib9c/Action/HackAndSlash5.cs deleted file mode 100644 index 01e03b9b42..0000000000 --- a/Lib9c/Action/HackAndSlash5.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash5")] - public class HackAndSlash5 : GameAction, IHackAndSlashV2 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - public BattleLog Result { get; private set; } - - IEnumerable IHackAndSlashV2.Costumes => costumes; - IEnumerable IHackAndSlashV2.Equipments => equipments; - IEnumerable IHackAndSlashV2.Foods => foods; - int IHackAndSlashV2.WorldId => worldId; - int IHackAndSlashV2.StageId => stageId; - Address IHackAndSlashV2.AvatarAddress => avatarAddress; - Address IHackAndSlashV2.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV2.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - 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(ActionObsoleteConfig.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(); - - 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(); - 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.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - var costumeStatSheet = states.GetSheet(); - - 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; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV2(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - 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; - } - } -} diff --git a/Lib9c/Action/HackAndSlash6.cs b/Lib9c/Action/HackAndSlash6.cs deleted file mode 100644 index 228cbc75e2..0000000000 --- a/Lib9c/Action/HackAndSlash6.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash6")] - public class HackAndSlash6 : GameAction, IHackAndSlashV2 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - public BattleLog Result { get; private set; } - - IEnumerable IHackAndSlashV2.Costumes => costumes; - IEnumerable IHackAndSlashV2.Equipments => equipments; - IEnumerable IHackAndSlashV2.Foods => foods; - int IHackAndSlashV2.WorldId => worldId; - int IHackAndSlashV2.StageId => stageId; - Address IHackAndSlashV2.AvatarAddress => avatarAddress; - Address IHackAndSlashV2.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV2.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(RankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states.SetState(WeeklyArenaAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.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.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - 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(); - - 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(); - 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.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - var costumeStatSheet = states.GetSheet(); - - 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; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV3(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute HackAndSlash6({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(); - 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(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.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; - } - } -} diff --git a/Lib9c/Action/HackAndSlash7.cs b/Lib9c/Action/HackAndSlash7.cs deleted file mode 100644 index ce36c8ddd6..0000000000 --- a/Lib9c/Action/HackAndSlash7.cs +++ /dev/null @@ -1,324 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash7")] - public class HackAndSlash7 : GameAction, IHackAndSlashV2 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - - IEnumerable IHackAndSlashV2.Costumes => costumes; - IEnumerable IHackAndSlashV2.Equipments => equipments; - IEnumerable IHackAndSlashV2.Foods => foods; - int IHackAndSlashV2.WorldId => worldId; - int IHackAndSlashV2.StageId => stageId; - Address IHackAndSlashV2.AvatarAddress => avatarAddress; - Address IHackAndSlashV2.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV2.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(RankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states.SetState(WeeklyArenaAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.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.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - 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(); - - 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(); - 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.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - var costumeStatSheet = states.GetSheet(); - - 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; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - var questSheet = states.GetQuestSheet(); - if (questList.Count() < questSheet.Count) - { - questList.UpdateListV1( - 2, - questSheet, - states.GetSheet(), - states.GetSheet(), - states.GetSheet()); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV3(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.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); - } - } - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash8.cs b/Lib9c/Action/HackAndSlash8.cs deleted file mode 100644 index dbf4e617fa..0000000000 --- a/Lib9c/Action/HackAndSlash8.cs +++ /dev/null @@ -1,297 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash8")] - public class HackAndSlash8 : GameAction, IHackAndSlashV3 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address rankingMapAddress; - - IEnumerable IHackAndSlashV3.Costumes => costumes; - IEnumerable IHackAndSlashV3.Equipments => equipments; - IEnumerable IHackAndSlashV3.Foods => foods; - int IHackAndSlashV3.WorldId => worldId; - int IHackAndSlashV3.StageId => stageId; - Address IHackAndSlashV3.AvatarAddress => avatarAddress; - Address IHackAndSlashV3.RankingMapAddress => rankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["rankingMapAddress"] = rankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - rankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(rankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100081ObsoleteIndex, 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.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - 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"); - } - - var worldSheet = states.GetSheet(); - 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(); - 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}" - ); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException($"{addressesHex}{worldId} can't execute HackAndSlash action."); - } - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - 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; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - var questSheet = states.GetQuestSheet(); - if (questList.Count() < questSheet.Count) - { - questList.UpdateListV1( - 2, - questSheet, - states.GetSheet(), - states.GetSheet(), - states.GetSheet()); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var characterSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV4(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (simulator.Log.IsClear && states.TryGetState(rankingMapAddress, out Dictionary d)) - { - 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(); - - TimeSpan totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlash9.cs b/Lib9c/Action/HackAndSlash9.cs deleted file mode 100644 index 6f9719fd43..0000000000 --- a/Lib9c/Action/HackAndSlash9.cs +++ /dev/null @@ -1,308 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash9")] - public class HackAndSlash9 : GameAction, IHackAndSlashV4 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - public Address rankingMapAddress; - - IEnumerable IHackAndSlashV4.Costumes => costumes; - IEnumerable IHackAndSlashV4.Equipments => equipments; - IEnumerable IHackAndSlashV4.Foods => foods; - int IHackAndSlashV4.WorldId => worldId; - int IHackAndSlashV4.StageId => stageId; - int IHackAndSlashV4.PlayCount => playCount; - Address IHackAndSlashV4.AvatarAddress => avatarAddress; - Address IHackAndSlashV4.RankingMapAddress => rankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["rankingMapAddress"] = rankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - rankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(rankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100086ObsoleteIndex, 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.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - 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"); - } - - var worldSheet = states.GetSheet(); - 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(); - 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}" - ); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException($"{addressesHex}{worldId} can't execute HackAndSlash action."); - } - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Validate: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - - avatarState.actionPoint -= totalCostActionPoint; - - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Update QuestList only when QuestSheet.Count is greater than QuestList.Count - var questList = avatarState.questList; - var questSheet = states.GetQuestSheet(); - if (questList.Count() < questSheet.Count) - { - questList.UpdateListV1( - 2, - questSheet, - states.GetSheet(), - states.GetSheet(), - states.GetSheet()); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update QuestList: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV5(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.SimulateV2(): {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(); - 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(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (simulator.Log.IsClear && states.TryGetState(rankingMapAddress, out Dictionary d)) - { - 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(); - - TimeSpan totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, totalElapsed); - return states; - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep1.cs b/Lib9c/Action/HackAndSlashSweep1.cs deleted file mode 100644 index b3f5474214..0000000000 --- a/Lib9c/Action/HackAndSlashSweep1.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash_sweep")] - public class HackAndSlashSweep1 : GameAction, IHackAndSlashSweepV1 - { - public const int UsableApStoneCount = 10; - - public Address avatarAddress; - public int apStoneCount = 0; - public int worldId; - public int stageId; - - Address IHackAndSlashSweepV1.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV1.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV1.WorldId => worldId; - int IHackAndSlashSweepV1.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(inventoryAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(avatarAddress, MarkChanged) - .SetState(context.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100193ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException($"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException( - $"{addressesHex} [{worldId}] can't execute HackAndSlashSweep action."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, avatarAddress, out var avatarState, out var migrationRequired)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheetsV1( - containQuestSheet: false, - containStageSimulatorSheets: false, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(worldId, out var world)) - { - throw new SheetRowColumnException($"{addressesHex}world is not contains in world information: {worldId}"); - } - - if (!world.IsStageCleared && stageId > world.StageClearedId) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage ({worldId}/{stageId}) is not cleared; " + - $"cleared stage: {world.StageClearedId}" - ); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - var apMaxPlayCount = stageRow.CostAP > 0 ? gameConfigState.ActionPointMax / stageRow.CostAP : 0; - var apStonePlayCount = apMaxPlayCount * apStoneCount; - var apPlayCount = stageRow.CostAP > 0 ? avatarState.actionPoint / stageRow.CostAP : 0; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - var ap = avatarState.actionPoint + gameConfigState.ActionPointMax * apStoneCount; - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {ap} < required cost : {stageRow.CostAP})" - ); - } - - // burn ap - var remainActionPoint = Math.Max(0, avatarState.actionPoint - stageRow.CostAP * apPlayCount); - avatarState.actionPoint = remainActionPoint; - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = GetRewardItems(context.Random, playCount, stageRow, materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExpV1(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - public static List GetRewardItems(IRandom random, - int playCount, - StageSheet.Row stageRow, - MaterialItemSheet materialItemSheet) - { - var rewardItems = new List(); - var maxCount = random.Next(stageRow.DropItemMin, stageRow.DropItemMax + 1); - for (var i = 0; i < playCount; i++) - { - var selector = StageSimulatorV1.SetItemSelector(stageRow, random); - var rewards = Simulator.SetRewardV2(selector, maxCount, random, - materialItemSheet); - rewardItems.AddRange(rewards); - } - - rewardItems = rewardItems.OrderBy(x => x.Id).ToList(); - return rewardItems; - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep2.cs b/Lib9c/Action/HackAndSlashSweep2.cs deleted file mode 100644 index c6eaae9ef0..0000000000 --- a/Lib9c/Action/HackAndSlashSweep2.cs +++ /dev/null @@ -1,211 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash_sweep2")] - public class HackAndSlashSweep2 : GameAction, IHackAndSlashSweepV1 - { - public const int UsableApStoneCount = 10; - - public Address avatarAddress; - public int apStoneCount = 0; - public int worldId; - public int stageId; - - Address IHackAndSlashSweepV1.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV1.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV1.WorldId => worldId; - int IHackAndSlashSweepV1.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(inventoryAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(avatarAddress, MarkChanged) - .SetState(context.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100200ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException($"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException( - $"{addressesHex} [{worldId}] can't execute HackAndSlashSweep action."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, avatarAddress, out var avatarState, out var migrationRequired)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheetsV1( - containQuestSheet: false, - containStageSimulatorSheets: false, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(worldId, out var world)) - { - throw new SheetRowColumnException($"{addressesHex}world is not contains in world information: {worldId}"); - } - - if (!world.IsStageCleared) - { - throw new StageNotClearedException($"{addressesHex}There is no stage cleared in that world (worldId:{worldId})"); - } - - if (stageId > world.StageClearedId) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage ({worldId}/{stageId}) is not cleared; " + - $"cleared stage: {world.StageClearedId}" - ); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - var apStonePlayCount = gameConfigState.ActionPointMax / stageRow.CostAP * apStoneCount; - var apPlayCount = avatarState.actionPoint / stageRow.CostAP; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - var ap = avatarState.actionPoint + gameConfigState.ActionPointMax * apStoneCount; - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {ap} < required cost : {stageRow.CostAP})" - ); - } - - // burn ap - var remainActionPoint = Math.Max(0, avatarState.actionPoint - stageRow.CostAP * apPlayCount); - avatarState.actionPoint = remainActionPoint; - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = GetRewardItems(context.Random, playCount, stageRow, materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExpV1(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - public static List GetRewardItems(IRandom random, - int playCount, - StageSheet.Row stageRow, - MaterialItemSheet materialItemSheet) - { - var rewardItems = new List(); - var maxCount = random.Next(stageRow.DropItemMin, stageRow.DropItemMax + 1); - for (var i = 0; i < playCount; i++) - { - var selector = StageSimulatorV1.SetItemSelector(stageRow, random); - var rewards = Simulator.SetRewardV2(selector, maxCount, random, - materialItemSheet); - rewardItems.AddRange(rewards); - } - - rewardItems = rewardItems.OrderBy(x => x.Id).ToList(); - return rewardItems; - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep3.cs b/Lib9c/Action/HackAndSlashSweep3.cs deleted file mode 100644 index b487b05167..0000000000 --- a/Lib9c/Action/HackAndSlashSweep3.cs +++ /dev/null @@ -1,274 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1017 - /// Updated at https://github.com/planetarium/lib9c/pull/1176 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash_sweep3")] - public class HackAndSlashSweep3 : GameAction, IHackAndSlashSweepV2 - { - public const int UsableApStoneCount = 10; - - public List costumes; - public List equipments; - public Address avatarAddress; - public int apStoneCount = 0; - public int actionPoint = 0; - public int worldId; - public int stageId; - - IEnumerable IHackAndSlashSweepV2.Costumes => costumes; - IEnumerable IHackAndSlashSweepV2.Equipments => equipments; - Address IHackAndSlashSweepV2.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV2.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV2.ActionPoint => actionPoint; - int IHackAndSlashSweepV2.WorldId => worldId; - int IHackAndSlashSweepV2.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["actionPoint"] = actionPoint.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - actionPoint = plainValue["actionPoint"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(inventoryAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(avatarAddress, MarkChanged) - .SetState(context.Signer, MarkChanged); - } - - var arenaSheetAddress = Addresses.GetSheetAddress(); - var arenaSheetState = states.GetState(arenaSheetAddress); - if (arenaSheetState != null) - { - throw new ActionObsoletedException(nameof(HackAndSlashSweep3)); - } - - CheckObsolete(ActionObsoleteConfig.V100210ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException($"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - if (worldId == GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException( - $"{addressesHex} [{worldId}] can't execute HackAndSlashSweep action."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, avatarAddress, out var avatarState, out var migrationRequired)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheetsV1( - containQuestSheet: false, - containStageSimulatorSheets: false, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(SweepRequiredCPSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(worldId, out var world)) - { - throw new SheetRowColumnException($"{addressesHex}world is not contains in world information: {worldId}"); - } - - if (!world.IsStageCleared) - { - throw new StageNotClearedException($"{addressesHex}There is no stage cleared in that world (worldId:{worldId})"); - } - - if (stageId > world.StageClearedId) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage ({worldId}/{stageId}) is not cleared; " + - $"cleared stage: {world.StageClearedId}" - ); - } - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds, - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var sweepRequiredCpSheet = sheets.GetSheet(); - if (!sweepRequiredCpSheet.TryGetValue(stageId, out var cpRow)) - { - throw new SheetRowColumnException($"{addressesHex}There is no row in SweepRequiredCPSheet: {stageId}"); - } - - var characterSheet = sheets.GetSheet(); - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.GetCPV2(avatarState, characterSheet, costumeStatSheet); - if (cp < cpRow.RequiredCP) - { - throw new NotEnoughCombatPointException($"{addressesHex}Aborted due to lack of player cp ({cp} < {cpRow.RequiredCP})"); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - if (actionPoint > avatarState.actionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"use AP({actionPoint}) > current AP({avatarState.actionPoint})" - ); - } - - // burn ap - avatarState.actionPoint -= actionPoint; - - var apMaxPlayCount = stageRow.CostAP > 0 ? gameConfigState.ActionPointMax / stageRow.CostAP : 0; - var apStonePlayCount = apMaxPlayCount * apStoneCount; - var apPlayCount = stageRow.CostAP > 0 ? actionPoint / stageRow.CostAP : 0; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = GetRewardItems(context.Random, playCount, stageRow, materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExpV1(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - public static List GetRewardItems(IRandom random, - int playCount, - StageSheet.Row stageRow, - MaterialItemSheet materialItemSheet) - { - var rewardItems = new List(); - var maxCount = random.Next(stageRow.DropItemMin, stageRow.DropItemMax + 1); - for (var i = 0; i < playCount; i++) - { - var selector = StageSimulatorV1.SetItemSelector(stageRow, random); - var rewards = Simulator.SetRewardV2(selector, maxCount, random, - materialItemSheet); - rewardItems.AddRange(rewards); - } - - rewardItems = rewardItems.OrderBy(x => x.Id).ToList(); - return rewardItems; - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep4.cs b/Lib9c/Action/HackAndSlashSweep4.cs deleted file mode 100644 index 3e30874ef9..0000000000 --- a/Lib9c/Action/HackAndSlashSweep4.cs +++ /dev/null @@ -1,281 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1017 - /// Updated at https://github.com/planetarium/lib9c/pull/1176 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash_sweep4")] - public class HackAndSlashSweep4 : GameAction, IHackAndSlashSweepV2 - { - public const int UsableApStoneCount = 10; - - public List costumes; - public List equipments; - public Address avatarAddress; - public int apStoneCount = 0; - public int actionPoint = 0; - public int worldId; - public int stageId; - - IEnumerable IHackAndSlashSweepV2.Costumes => costumes; - IEnumerable IHackAndSlashSweepV2.Equipments => equipments; - Address IHackAndSlashSweepV2.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV2.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV2.ActionPoint => actionPoint; - int IHackAndSlashSweepV2.WorldId => worldId; - int IHackAndSlashSweepV2.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["actionPoint"] = actionPoint.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - actionPoint = plainValue["actionPoint"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(inventoryAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(avatarAddress, MarkChanged) - .SetState(context.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100300ObsoleteIndex, context); - - var arenaSheetAddress = Addresses.GetSheetAddress(); - var arenaSheetState = states.GetState(arenaSheetAddress); - if (arenaSheetState != null) - { - // exception handling for v100240. - if (context.BlockIndex > 4374125 && context.BlockIndex < 4374249) - { - } - else - { - throw new ActionObsoletedException(nameof(HackAndSlashSweep4)); - } - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException($"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - if (worldId >= GameConfig.MimisbrunnrWorldId) - { - throw new InvalidWorldException( - $"{addressesHex} [{worldId}] can't execute HackAndSlashSweep action."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, avatarAddress, out var avatarState, out var migrationRequired)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheetsV1( - containQuestSheet: false, - containStageSimulatorSheets: false, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(SweepRequiredCPSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(worldId, out world)) - { - // Do nothing. - } - } - - if (!world.IsPlayable(stageId)) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage isn't playable;" + - $"StageClearedId: {world.StageClearedId}" - ); - } - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds, - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var sweepRequiredCpSheet = sheets.GetSheet(); - if (!sweepRequiredCpSheet.TryGetValue(stageId, out var cpRow)) - { - throw new SheetRowColumnException($"{addressesHex}There is no row in SweepRequiredCPSheet: {stageId}"); - } - - var characterSheet = sheets.GetSheet(); - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.GetCPV2(avatarState, characterSheet, costumeStatSheet); - if (cp < cpRow.RequiredCP) - { - throw new NotEnoughCombatPointException($"{addressesHex}Aborted due to lack of player cp ({cp} < {cpRow.RequiredCP})"); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - if (actionPoint > avatarState.actionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"use AP({actionPoint}) > current AP({avatarState.actionPoint})" - ); - } - - // burn ap - avatarState.actionPoint -= actionPoint; - - var apMaxPlayCount = stageRow.CostAP > 0 ? gameConfigState.ActionPointMax / stageRow.CostAP : 0; - var apStonePlayCount = apMaxPlayCount * apStoneCount; - var apPlayCount = stageRow.CostAP > 0 ? actionPoint / stageRow.CostAP : 0; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = GetRewardItems(context.Random, playCount, stageRow, materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExp(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - public static List GetRewardItems(IRandom random, - int playCount, - StageSheet.Row stageRow, - MaterialItemSheet materialItemSheet) - { - var rewardItems = new List(); - var maxCount = random.Next(stageRow.DropItemMin, stageRow.DropItemMax + 1); - for (var i = 0; i < playCount; i++) - { - var selector = StageSimulatorV1.SetItemSelector(stageRow, random); - var rewards = Simulator.SetRewardV2(selector, maxCount, random, - materialItemSheet); - rewardItems.AddRange(rewards); - } - - rewardItems = rewardItems.OrderBy(x => x.Id).ToList(); - return rewardItems; - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep5.cs b/Lib9c/Action/HackAndSlashSweep5.cs deleted file mode 100644 index b01b323017..0000000000 --- a/Lib9c/Action/HackAndSlashSweep5.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1173 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash_sweep5")] - public class HackAndSlashSweep5 : GameAction, IHackAndSlashSweepV2 - { - public const int UsableApStoneCount = 10; - - public List costumes; - public List equipments; - public Address avatarAddress; - public int apStoneCount = 0; - public int actionPoint = 0; - public int worldId; - public int stageId; - - IEnumerable IHackAndSlashSweepV2.Costumes => costumes; - IEnumerable IHackAndSlashSweepV2.Equipments => equipments; - Address IHackAndSlashSweepV2.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV2.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV2.ActionPoint => actionPoint; - int IHackAndSlashSweepV2.WorldId => worldId; - int IHackAndSlashSweepV2.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["actionPoint"] = actionPoint.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - actionPoint = plainValue["actionPoint"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100300ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException($"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - states.ValidateWorldId(avatarAddress, worldId); - - if (!states.TryGetAvatarStateV2(context.Signer, avatarAddress, out var avatarState, out var migrationRequired)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheetsV1( - containQuestSheet: false, - containStageSimulatorSheets: false, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(SweepRequiredCPSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(worldId, out world)) - { - // Do nothing. - } - } - - if (!world.IsPlayable(stageId)) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage isn't playable;" + - $"StageClearedId: {world.StageClearedId}" - ); - } - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds, - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var sweepRequiredCpSheet = sheets.GetSheet(); - if (!sweepRequiredCpSheet.TryGetValue(stageId, out var cpRow)) - { - throw new SheetRowColumnException($"{addressesHex}There is no row in SweepRequiredCPSheet: {stageId}"); - } - - var characterSheet = sheets.GetSheet(); - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.GetCPV2(avatarState, characterSheet, costumeStatSheet); - if (cp < cpRow.RequiredCP) - { - throw new NotEnoughCombatPointException($"{addressesHex}Aborted due to lack of player cp ({cp} < {cpRow.RequiredCP})"); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - if (actionPoint > avatarState.actionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"use AP({actionPoint}) > current AP({avatarState.actionPoint})" - ); - } - - // burn ap - avatarState.actionPoint -= actionPoint; - - var apMaxPlayCount = stageRow.CostAP > 0 ? gameConfigState.ActionPointMax / stageRow.CostAP : 0; - var apStonePlayCount = apMaxPlayCount * apStoneCount; - var apPlayCount = stageRow.CostAP > 0 ? actionPoint / stageRow.CostAP : 0; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = GetRewardItems(context.Random, playCount, stageRow, materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExp(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - public static List GetRewardItems(IRandom random, - int playCount, - StageSheet.Row stageRow, - MaterialItemSheet materialItemSheet) - { - var rewardItems = new List(); - var maxCount = random.Next(stageRow.DropItemMin, stageRow.DropItemMax + 1); - for (var i = 0; i < playCount; i++) - { - var selector = StageSimulatorV1.SetItemSelector(stageRow, random); - var rewards = Simulator.SetRewardV2(selector, maxCount, random, - materialItemSheet); - rewardItems.AddRange(rewards); - } - - rewardItems = rewardItems.OrderBy(x => x.Id).ToList(); - return rewardItems; - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep7.cs b/Lib9c/Action/HackAndSlashSweep7.cs deleted file mode 100644 index f7b2914e22..0000000000 --- a/Lib9c/Action/HackAndSlashSweep7.cs +++ /dev/null @@ -1,281 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1374 - /// - [Serializable] - [ActionType("hack_and_slash_sweep7")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class HackAndSlashSweep7 : GameAction, IHackAndSlashSweepV2 - { - public const int UsableApStoneCount = 10; - - public List costumes; - public List equipments; - public Address avatarAddress; - public int apStoneCount; - public int actionPoint; - public int worldId; - public int stageId; - - IEnumerable IHackAndSlashSweepV2.Costumes => costumes; - IEnumerable IHackAndSlashSweepV2.Equipments => equipments; - Address IHackAndSlashSweepV2.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV2.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV2.ActionPoint => actionPoint; - int IHackAndSlashSweepV2.WorldId => worldId; - int IHackAndSlashSweepV2.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["actionPoint"] = actionPoint.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - actionPoint = plainValue["actionPoint"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HackAndSlashSweep exec started", addressesHex); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException( - $"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - states.ValidateWorldId(avatarAddress, worldId); - - if (!states.TryGetAvatarStateV2( - context.Signer, - avatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheets( - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(SweepRequiredCPSheet), - typeof(StakeActionPointCoefficientSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(worldId, out world)) - { - // Do nothing. - } - } - - if (!world.IsPlayable(stageId)) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage isn't playable;" + - $"StageClearedId: {world.StageClearedId}" - ); - } - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds, - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var sweepRequiredCpSheet = sheets.GetSheet(); - if (!sweepRequiredCpSheet.TryGetValue(stageId, out var cpRow)) - { - throw new SheetRowColumnException( - $"{addressesHex}There is no row in SweepRequiredCPSheet: {stageId}"); - } - - var characterSheet = sheets.GetSheet(); - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.GetCPV2(avatarState, characterSheet, costumeStatSheet); - if (cp < cpRow.RequiredCP) - { - throw new NotEnoughCombatPointException( - $"{addressesHex}Aborted due to lack of player cp ({cp} < {cpRow.RequiredCP})"); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, - count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - if (actionPoint > avatarState.actionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"use AP({actionPoint}) > current AP({avatarState.actionPoint})" - ); - } - - // burn ap - avatarState.actionPoint -= actionPoint; - var costAp = sheets.GetSheet()[stageId].CostAP; - if (states.TryGetStakeState(context.Signer, out var stakeState)) - { - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - var actionPointCoefficientSheet = - sheets.GetSheet(); - var stakingLevel = - actionPointCoefficientSheet.FindLevelByStakedAmount(context.Signer, - stakedAmount); - costAp = actionPointCoefficientSheet.GetActionPointByStaking( - costAp, - 1, - stakingLevel); - } - - var apMaxPlayCount = costAp > 0 ? gameConfigState.ActionPointMax / costAp : 0; - var apStonePlayCount = apMaxPlayCount * apStoneCount; - var apPlayCount = costAp > 0 ? actionPoint / costAp : 0; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = HackAndSlashSweep6.GetRewardItems( - context.Random, - playCount, - stageRow, - materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExp(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - if (migrationRequired) - { - states = states.SetState( - avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HackAndSlashSweep Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState( - avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - } -} diff --git a/Lib9c/Action/HackAndSlashSweep8.cs b/Lib9c/Action/HackAndSlashSweep8.cs deleted file mode 100644 index d5f33babd2..0000000000 --- a/Lib9c/Action/HackAndSlashSweep8.cs +++ /dev/null @@ -1,350 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1495 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash_sweep8")] - public class HackAndSlashSweep8 : GameAction, IHackAndSlashSweepV3 - { - public const int UsableApStoneCount = 10; - - public List costumes; - public List equipments; - public List runeInfos; - public Address avatarAddress; - public int apStoneCount; - public int actionPoint; - public int worldId; - public int stageId; - - IEnumerable IHackAndSlashSweepV3.Costumes => costumes; - IEnumerable IHackAndSlashSweepV3.Equipments => equipments; - IEnumerable IHackAndSlashSweepV3.RuneSlotInfos => - runeInfos.Select(x => x.Serialize()); - Address IHackAndSlashSweepV3.AvatarAddress => avatarAddress; - int IHackAndSlashSweepV3.ApStoneCount => apStoneCount; - int IHackAndSlashSweepV3.ActionPoint => actionPoint; - int IHackAndSlashSweepV3.WorldId => worldId; - int IHackAndSlashSweepV3.StageId => stageId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["runeInfos"] = runeInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["apStoneCount"] = apStoneCount.Serialize(), - ["actionPoint"] = actionPoint.Serialize(), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - runeInfos = plainValue["runeInfos"].ToList(x => new RuneSlotInfo((List)x)); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - apStoneCount = plainValue["apStoneCount"].ToInteger(); - actionPoint = plainValue["actionPoint"].ToInteger(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HackAndSlashSweep exec started", addressesHex); - - if (apStoneCount > UsableApStoneCount) - { - throw new UsageLimitExceedException( - $"Exceeded the amount of ap stones that can be used " + - $"apStoneCount : {apStoneCount} > UsableApStoneCount : {UsableApStoneCount}"); - } - - states.ValidateWorldId(avatarAddress, worldId); - - if (!states.TryGetAvatarStateV2( - context.Signer, - avatarAddress, - out var avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheets( - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(SweepRequiredCPSheet), - typeof(StakeActionPointCoefficientSheet), - typeof(RuneListSheet), - typeof(RuneOptionSheet), - }); - - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(worldId, out world)) - { - // Do nothing. - } - } - - if (!world.IsPlayable(stageId)) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage isn't playable;" + - $"StageClearedId: {world.StageClearedId}" - ); - } - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - var items = equipments.Concat(costumes); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds, - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var sweepRequiredCpSheet = sheets.GetSheet(); - if (!sweepRequiredCpSheet.TryGetValue(stageId, out var cpRow)) - { - throw new SheetRowColumnException( - $"{addressesHex}There is no row in SweepRequiredCPSheet: {stageId}"); - } - - var costumeList = new List(); - foreach (var guid in costumes) - { - var costume = avatarState.inventory.Costumes.FirstOrDefault(x => x.ItemId == guid); - if (costume != null) - { - costumeList.Add(costume); - } - } - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(avatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlotV2(runeInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(avatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(equipments); - itemSlotState.UpdateCostumes(costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var runeStates = new List(); - foreach (var address in runeInfos.Select(info => RuneState.DeriveAddress(avatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - var runeOptionSheet = sheets.GetSheet(); - var runeOptions = new List(); - foreach (var runeState in runeStates) - { - if (!runeOptionSheet.TryGetValue(runeState.RuneId, out var optionRow)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.RuneId); - } - - if (!optionRow.LevelOptionMap.TryGetValue(runeState.Level, out var option)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.Level); - } - - runeOptions.Add(option); - } - - var characterSheet = sheets.GetSheet(); - if (!characterSheet.TryGetValue(avatarState.characterId, out var characterRow)) - { - throw new SheetRowNotFoundException("CharacterSheet", avatarState.characterId); - } - - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.TotalCP( - equipmentList, costumeList, - runeOptions, avatarState.level, - characterRow, costumeStatSheet); - if (cp < cpRow.RequiredCP) - { - throw new NotEnoughCombatPointException( - $"{addressesHex}Aborted due to lack of player cp ({cp} < {cpRow.RequiredCP})"); - } - - var materialItemSheet = sheets.GetSheet(); - if (apStoneCount > 0) - { - // use apStone - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex, - count: apStoneCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id})"); - } - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the game config state was failed to load."); - } - - if (actionPoint > avatarState.actionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"use AP({actionPoint}) > current AP({avatarState.actionPoint})" - ); - } - - // burn ap - avatarState.actionPoint -= actionPoint; - var costAp = sheets.GetSheet()[stageId].CostAP; - if (states.TryGetStakeState(context.Signer, out var stakeState)) - { - var currency = states.GetGoldCurrency(); - var stakedAmount = states.GetBalance(stakeState.address, currency); - var actionPointCoefficientSheet = - sheets.GetSheet(); - var stakingLevel = - actionPointCoefficientSheet.FindLevelByStakedAmount(context.Signer, - stakedAmount); - costAp = actionPointCoefficientSheet.GetActionPointByStaking( - costAp, - 1, - stakingLevel); - } - - var apMaxPlayCount = costAp > 0 ? gameConfigState.ActionPointMax / costAp : 0; - var apStonePlayCount = apMaxPlayCount * apStoneCount; - var apPlayCount = costAp > 0 ? actionPoint / costAp : 0; - var playCount = apStonePlayCount + apPlayCount; - if (playCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var stageWaveSheet = sheets.GetSheet(); - avatarState.UpdateMonsterMap(stageWaveSheet, stageId); - - var rewardItems = HackAndSlashSweep6.GetRewardItems( - context.Random, - playCount, - stageRow, - materialItemSheet); - avatarState.UpdateInventory(rewardItems); - - var levelSheet = sheets.GetSheet(); - var (level, exp) = avatarState.GetLevelAndExp(levelSheet, stageId, playCount); - avatarState.UpdateExp(level, exp); - - if (migrationRequired) - { - states = states.SetState( - avatarAddress.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}HackAndSlashSweep Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState( - avatarAddress.Derive(LegacyInventoryKey), - avatarState.inventory.Serialize()) - .SetState( - avatarAddress.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - } - } -} diff --git a/Lib9c/Action/ItemEnhancement0.cs b/Lib9c/Action/ItemEnhancement0.cs deleted file mode 100644 index bb20b85a6c..0000000000 --- a/Lib9c/Action/ItemEnhancement0.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement")] - public class ItemEnhancement0 : GameAction, IItemEnhancementV1 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public IEnumerable materialIds; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV1.ItemId => itemId; - IEnumerable IItemEnhancementV1.MaterialIds => materialIds; - Address IItemEnhancementV1.AvatarAddress => avatarAddress; - int IItemEnhancementV1.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - Log.Warning("{AddressesHex}item_enhancement is deprecated. Please use item_enhancement2", addressesHex); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if(enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = materialIds - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = ItemEnhancement7.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - var materials = new List(); - foreach (var materialId in materialIds.OrderBy(guid => guid)) - { - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (materials.Contains(materialEquipment)) - { - throw new DuplicateMaterialException( - $"{addressesHex}Aborted as the same material was used more than once: {materialEquipment}" - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - materials.Add(materialEquipment); - } - - enhancementEquipment.Unequip(); - - enhancementEquipment = ItemEnhancement7.UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = 0; - - foreach (var material in materials) - { - avatarState.inventory.RemoveNonFungibleItem(material); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update2(mail); - avatarState.UpdateFromItemEnhancement2(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialIds"] = materialIds - .OrderBy(i => i) - .Select(g => g.Serialize()) - .Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }; - - // slotIndex가 포함되지 않은채 나간 버전과 호환을 위해, 0번째 슬롯을 쓰는 경우엔 보내지 않습니다. - if (slotIndex != 0) - { - dict["slotIndex"] = slotIndex.Serialize(); - } - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialIds = plainValue["materialIds"].ToList(StateExtensions.ToGuid); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - } -} diff --git a/Lib9c/Action/ItemEnhancement2.cs b/Lib9c/Action/ItemEnhancement2.cs deleted file mode 100644 index 4b36feb7d6..0000000000 --- a/Lib9c/Action/ItemEnhancement2.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement2")] - public class ItemEnhancement2 : GameAction, IItemEnhancementV2 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public Guid materialId; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if(enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = new []{materialId} - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = ItemEnhancement7.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - - enhancementEquipment.Unequip(); - - enhancementEquipment = ItemEnhancement7.UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = 0; - - avatarState.inventory.RemoveNonFungibleItem(materialId); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update2(mail); - avatarState.UpdateFromItemEnhancement2(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }; - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/ItemEnhancement3.cs b/Lib9c/Action/ItemEnhancement3.cs deleted file mode 100644 index a8164fc3d3..0000000000 --- a/Lib9c/Action/ItemEnhancement3.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement3")] - public class ItemEnhancement3 : GameAction, IItemEnhancementV2 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public Guid materialId; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if(enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = new []{materialId} - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = ItemEnhancement7.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - - enhancementEquipment.Unequip(); - - enhancementEquipment = ItemEnhancement7.UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = 0; - - avatarState.inventory.RemoveNonFungibleItem(materialId); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update3(mail); - avatarState.UpdateFromItemEnhancement2(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }; - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/ItemEnhancement4.cs b/Lib9c/Action/ItemEnhancement4.cs deleted file mode 100644 index ecac56ec54..0000000000 --- a/Lib9c/Action/ItemEnhancement4.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement4")] - public class ItemEnhancement4 : GameAction, IItemEnhancementV2 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public Guid materialId; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex , sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = new[] { materialId } - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = ItemEnhancement7.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - - enhancementEquipment.Unequip(); - - enhancementEquipment = ItemEnhancement7.UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = requiredNCG; - - avatarState.inventory.RemoveNonFungibleItem(materialId); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update3(mail); - avatarState.UpdateFromItemEnhancement2(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }; - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/ItemEnhancement5.cs b/Lib9c/Action/ItemEnhancement5.cs deleted file mode 100644 index c60e948f04..0000000000 --- a/Lib9c/Action/ItemEnhancement5.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement5")] - public class ItemEnhancement5 : GameAction, IItemEnhancementV2 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public Guid materialId; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = new[] { materialId } - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = ItemEnhancement7.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - - enhancementEquipment.Unequip(); - - enhancementEquipment = ItemEnhancement7.UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = requiredNCG; - - avatarState.inventory.RemoveNonFungibleItem(materialId); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update(mail); - avatarState.UpdateFromItemEnhancement2(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }; - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/ItemEnhancement6.cs b/Lib9c/Action/ItemEnhancement6.cs deleted file mode 100644 index 18e05c3e18..0000000000 --- a/Lib9c/Action/ItemEnhancement6.cs +++ /dev/null @@ -1,296 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement6")] - public class ItemEnhancement6 : GameAction, IItemEnhancementV2 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public Guid materialId; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = new[] { materialId } - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = ItemEnhancement7.GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - - enhancementEquipment.Unequip(); - - enhancementEquipment = ItemEnhancement7.UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = requiredNCG; - - avatarState.inventory.RemoveNonFungibleItem(materialId); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update(mail); - avatarState.UpdateFromItemEnhancement2(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }; - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public static BigInteger GetRequiredNCG(EnhancementCostSheet costSheet, int grade, int level) - { - var row = costSheet - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == level); - - return row?.Cost ?? 0; - } - - public static Equipment UpgradeEquipment(Equipment equipment) - { - equipment.LevelUpV1(); - return equipment; - } - - public static int GetRequiredAp() - { - return GameConfig.EnhanceEquipmentCostAP; - } - } -} diff --git a/Lib9c/Action/ItemEnhancement8.cs b/Lib9c/Action/ItemEnhancement8.cs deleted file mode 100644 index f1b5377647..0000000000 --- a/Lib9c/Action/ItemEnhancement8.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("item_enhancement8")] - public class ItemEnhancement8 : GameAction, IItemEnhancementV2 - { - public const int RequiredBlockCount = 1; - - public static readonly Address BlacksmithAddress = Addresses.Blacksmith; - - public Guid itemId; - public Guid materialId; - public Address avatarAddress; - public int slotIndex; - - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, BlacksmithAddress) - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement exec started", addressesHex); - - if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out AgentState agentState, - out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (enhancementItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{enhancementItem.RequiredBlockIndex}." - ); - } - - if (!(enhancementItem is Equipment enhancementEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - - ); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); - } - - if (!slotState.Validate(avatarState, ctx.BlockIndex)) - { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (enhancementEquipment.level > 9) - { - // Maximum level exceeded. - throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < 9" - ); - } - - var result = new ItemEnhancement7.ResultModel - { - itemUsable = enhancementEquipment, - materialItemIdList = new[] { materialId } - }; - - var requiredAP = ItemEnhancement.GetRequiredAp(); - if (avatarState.actionPoint < requiredAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredAP}" - ); - } - - var enhancementCostSheet = states.GetSheet(); - var requiredNCG = GetRequiredNCG(enhancementCostSheet, enhancementEquipment.Grade, enhancementEquipment.level + 1); - - avatarState.actionPoint -= requiredAP; - result.actionPoint = requiredAP; - - if (requiredNCG > 0) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - BlacksmithAddress, - states.GetGoldCurrency() * requiredNCG - ); - } - - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); - } - - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet; it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } - - if (!(materialItem is Equipment materialEquipment)) - { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } - - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } - - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - // Invalid ItemSubType - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}, but {materialEquipment.ItemSubType}." - ); - } - - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - // Invalid Grade - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade}) and a material ({materialEquipment.Grade}) does not match." - ); - } - - if (materialEquipment.level != enhancementEquipment.level) - { - // Invalid level - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level}) and a material ({materialEquipment.level}) does not match." - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - materialEquipment.Unequip(); - - enhancementEquipment.Unequip(); - - enhancementEquipment = UpgradeEquipment(enhancementEquipment); - - var requiredBlockIndex = ctx.BlockIndex + RequiredBlockCount; - enhancementEquipment.Update(requiredBlockIndex); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - result.gold = requiredNCG; - - avatarState.inventory.RemoveNonFungibleItem(materialId); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Remove Materials: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); - result.id = mail.id; - - avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); - avatarState.Update(mail); - avatarState.UpdateFromItemEnhancement(enhancementEquipment); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal - { - get - { - var dict = new Dictionary - { - ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }; - - return dict.ToImmutableDictionary(); - } - } - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - if (plainValue.TryGetValue((Text) "slotIndex", out var value)) - { - slotIndex = value.ToInteger(); - } - } - - public static BigInteger GetRequiredNCG(EnhancementCostSheet costSheet, int grade, int level) - { - var row = costSheet - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == level); - - return row?.Cost ?? 0; - } - - public static Equipment UpgradeEquipment(Equipment equipment) - { - equipment.LevelUpV1(); - return equipment; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle.cs b/Lib9c/Action/MimisbrunnrBattle.cs deleted file mode 100644 index 1ba8c9418e..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle.cs +++ /dev/null @@ -1,446 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Model; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.Item; -using Nekoyume.Model.Quest; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Skill = Nekoyume.Model.Skill.Skill; -using static Lib9c.SerializeKeys; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Rune; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - [ActionType("mimisbrunnr_battle13")] - public class MimisbrunnrBattle : GameAction, IMimisbrunnrBattleV5 - { - public List Costumes; - public List Equipments; - public List Foods; - public List RuneInfos; - public int WorldId; - public int StageId; - public int PlayCount = 1; - public Address AvatarAddress; - - IEnumerable IMimisbrunnrBattleV5.Costumes => Costumes; - IEnumerable IMimisbrunnrBattleV5.Equipments => Equipments; - IEnumerable IMimisbrunnrBattleV5.Foods => Foods; - IEnumerable IMimisbrunnrBattleV5.RuneSlotInfos => - RuneInfos.Select(x => x.Serialize()); - int IMimisbrunnrBattleV5.WorldId => WorldId; - int IMimisbrunnrBattleV5.StageId => StageId; - int IMimisbrunnrBattleV5.PlayCount => PlayCount; - Address IMimisbrunnrBattleV5.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["playCount"] = PlayCount.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - PlayCount = plainValue["playCount"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr exec started", - addressesHex); - - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - "Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheets( - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MimisbrunnrSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Get Sheets: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(StageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), StageId); - } - - var worldUnlockSheet = sheets.GetSheet(); - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(WorldId, out var world)) - { - // NOTE: Add new World from WorldSheet - worldInformation.AddAndUnlockMimisbrunnrWorld( - worldRow, - context.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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(WorldId, out world)) - { - // Do nothing. - } - } - } - - 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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = sheets.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(StageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - "MimisbrunnrSheet", - StageId); - } - - foreach (var equipmentId in Equipments) - { - if (!avatarState.inventory.TryGetNonFungibleItem( - equipmentId, - out ItemUsable itemUsable)) - { - continue; - } - - var elementalType = ((Equipment)itemUsable).ElementalType; - if (!mimisbrunnrSheetRow.ElementalTypes.Exists(x => - x == elementalType)) - { - throw new InvalidElementalException( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", - addressesHex, - sw.Elapsed); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - - sw.Restart(); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0." + - $" current playCount : {PlayCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * PlayCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point:" + - $" current({avatarState.actionPoint}) < required({totalCostActionPoint})" - ); - } - - var equippableItem = Costumes.Concat(Equipments); - avatarState.EquipItems(equippableItem); - var requirementSheet = sheets.GetSheet(); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - requirementSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= totalCostActionPoint; - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var materialSheet = sheets.GetSheet(); - - // update rune slot - if (RuneInfos is null) - { - throw new RuneInfosIsEmptyException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - if (RuneInfos.GroupBy(x => x.SlotIndex).Count() != RuneInfos.Count) - { - throw new DuplicatedRuneSlotIndexException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - - if (RuneInfos.Exists(x => x.SlotIndex >= runeSlotState.GetRuneSlot().Count)) - { - throw new SlotNotFoundException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var simulator = new StageSimulator( - context.Random, - avatarState, - Foods, - runeStates, - new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - 0, - sheets.GetSimulatorSheets(), - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulator.GetWaveRewards(context.Random, stageRow, materialSheet, PlayCount)); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", - addressesHex, - sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - context.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - // This conditional logic is same as written in the - // HackAndSlash("hack_and_slash18") action. - if (context.BlockIndex < ActionObsoleteConfig.V100310ExecutedBlockIndex) - { - var player = simulator.Player; - foreach (var key in player.monsterMapForBeforeV100310.Keys) - { - player.monsterMap.Add(key, player.monsterMapForBeforeV100310[key]); - } - - player.monsterMapForBeforeV100310.Clear(); - - foreach (var key in player.eventMapForBeforeV100310.Keys) - { - player.eventMap.Add(key, player.eventMapForBeforeV100310[key]); - } - - player.eventMapForBeforeV100310.Clear(); - } - - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = context.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(AvatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle0.cs b/Lib9c/Action/MimisbrunnrBattle0.cs deleted file mode 100644 index 6ff09d4107..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle0.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle")] - public class MimisbrunnrBattle0 : GameAction, IMimisbrunnrBattleV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - - IEnumerable IMimisbrunnrBattleV1.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV1.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV1.Foods => foods; - int IMimisbrunnrBattleV1.WorldId => worldId; - int IMimisbrunnrBattleV1.StageId => stageId; - Address IMimisbrunnrBattleV1.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IMimisbrunnrBattleV1.RankingMapAddress => RankingMapAddress; - - private const int AlfheimId = 2; - public BattleLog Result { get; private set; } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(RankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - return states.SetState(WeeklyArenaAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr 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}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (avatarState.RankingMapAddress != RankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - var worldUnlockSheet = states.GetSheet(); - if (!worldSheet.TryGetValue(worldId, out var worldRow, false)) - { - throw new SheetRowNotFoundException(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(); - 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.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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException(addressesHex, "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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, sw.Elapsed); - - avatarState.ValidateEquipments(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - 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; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet - ); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV1(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUpV1(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(RankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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) - { - var characterSheet = states.GetSheet(); - 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}Mimisbrunnr Update WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var weeklySerialized = weekly.Serialize(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - - states = states.SetState(weekly.address, weeklySerialized); - } - } - - Result = simulator.Log; - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle10.cs b/Lib9c/Action/MimisbrunnrBattle10.cs deleted file mode 100644 index edfecdd0c2..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle10.cs +++ /dev/null @@ -1,393 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Model.Item; -using Nekoyume.Model.Skill; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1241 - /// Updated at https://github.com/planetarium/lib9c/pull/1244 - /// - [Serializable] - [ActionType("mimisbrunnr_battle10")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class MimisbrunnrBattle10 : GameAction, IMimisbrunnrBattleV4 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - - IEnumerable IMimisbrunnrBattleV4.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV4.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV4.Foods => foods; - int IMimisbrunnrBattleV4.WorldId => worldId; - int IMimisbrunnrBattleV4.StageId => stageId; - int IMimisbrunnrBattleV4.PlayCount => playCount; - Address IMimisbrunnrBattleV4.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr exec started", - addressesHex); - - if (!states.TryGetAvatarStateV2( - context.Signer, - avatarAddress, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - "Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - - // FIXME Delete this check next hard fork. - bool useV100291Sheets = UseV100291Sheets(context.BlockIndex); - var sheets = useV100291Sheets - ? states.GetSheetsV100291( - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MimisbrunnrSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }) - : states.GetSheetsV1( - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MimisbrunnrSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - }); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Get Sheets: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldUnlockSheet = sheets.GetSheet(); - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(worldId, out var world)) - { - // NOTE: Add new World from WorldSheet - worldInformation.AddAndUnlockMimisbrunnrWorld( - worldRow, - context.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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(worldId, out world)) - { - // Do nothing. - } - } - } - - 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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = sheets.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - "MimisbrunnrSheet", - stageId); - } - - foreach (var equipmentId in equipments) - { - if (!avatarState.inventory.TryGetNonFungibleItem( - equipmentId, - out ItemUsable itemUsable)) - { - continue; - } - - var elementalType = ((Equipment)itemUsable).ElementalType; - if (!mimisbrunnrSheetRow.ElementalTypes.Exists(x => - x == elementalType)) - { - throw new InvalidElementalException( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", - addressesHex, - sw.Elapsed); - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0." + - $" current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point:" + - $" current({avatarState.actionPoint}) < required({totalCostActionPoint})" - ); - } - - var equippableItem = costumes.Concat(equipments); - avatarState.EquipItems(equippableItem); - var requirementSheet = sheets.GetSheet(); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - requirementSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= totalCostActionPoint; - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var simulatorSheets = useV100291Sheets - ? sheets.GetSimulatorSheetsV100291() - : sheets.GetSimulatorSheetsV1(); - var materialSheet = sheets.GetSheet(); - var simulator = new StageSimulatorV2( - context.Random, - avatarState, - foods, - new List(), - worldId, - stageId, - stageRow, - sheets.GetSheet()[stageId], - avatarState.worldInformation.IsStageCleared(stageId), - 0, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards(context.Random, stageRow, materialSheet, playCount)); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", - addressesHex, - sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - context.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = context.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle11.cs b/Lib9c/Action/MimisbrunnrBattle11.cs deleted file mode 100644 index 7019246556..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle11.cs +++ /dev/null @@ -1,447 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Model; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.Item; -using Nekoyume.Model.Quest; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Skill = Nekoyume.Model.Skill.Skill; -using static Lib9c.SerializeKeys; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Rune; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1495 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle11")] - public class MimisbrunnrBattle11 : GameAction, IMimisbrunnrBattleV5 - { - public List Costumes; - public List Equipments; - public List Foods; - public List RuneInfos; - public int WorldId; - public int StageId; - public int PlayCount = 1; - public Address AvatarAddress; - - IEnumerable IMimisbrunnrBattleV5.Costumes => Costumes; - IEnumerable IMimisbrunnrBattleV5.Equipments => Equipments; - IEnumerable IMimisbrunnrBattleV5.Foods => Foods; - IEnumerable IMimisbrunnrBattleV5.RuneSlotInfos => - RuneInfos.Select(x => x.Serialize()); - int IMimisbrunnrBattleV5.WorldId => WorldId; - int IMimisbrunnrBattleV5.StageId => StageId; - int IMimisbrunnrBattleV5.PlayCount => PlayCount; - Address IMimisbrunnrBattleV5.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["playCount"] = PlayCount.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - PlayCount = plainValue["playCount"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr exec started", - addressesHex); - - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - "Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheets( - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MimisbrunnrSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Get Sheets: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(StageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), StageId); - } - - var worldUnlockSheet = sheets.GetSheet(); - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(WorldId, out var world)) - { - // NOTE: Add new World from WorldSheet - worldInformation.AddAndUnlockMimisbrunnrWorld( - worldRow, - context.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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(WorldId, out world)) - { - // Do nothing. - } - } - } - - 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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = sheets.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(StageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - "MimisbrunnrSheet", - StageId); - } - - foreach (var equipmentId in Equipments) - { - if (!avatarState.inventory.TryGetNonFungibleItem( - equipmentId, - out ItemUsable itemUsable)) - { - continue; - } - - var elementalType = ((Equipment)itemUsable).ElementalType; - if (!mimisbrunnrSheetRow.ElementalTypes.Exists(x => - x == elementalType)) - { - throw new InvalidElementalException( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", - addressesHex, - sw.Elapsed); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - - sw.Restart(); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0." + - $" current playCount : {PlayCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * PlayCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point:" + - $" current({avatarState.actionPoint}) < required({totalCostActionPoint})" - ); - } - - var equippableItem = Costumes.Concat(Equipments); - avatarState.EquipItems(equippableItem); - var requirementSheet = sheets.GetSheet(); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - requirementSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= totalCostActionPoint; - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var materialSheet = sheets.GetSheet(); - - // update rune slot - if (RuneInfos is null) - { - throw new RuneInfosIsEmptyException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - if (RuneInfos.GroupBy(x => x.SlotIndex).Count() != RuneInfos.Count) - { - throw new DuplicatedRuneSlotIndexException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - - if (RuneInfos.Exists(x => x.SlotIndex >= runeSlotState.GetRuneSlot().Count)) - { - throw new SlotNotFoundException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlotV2(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var simulator = new StageSimulatorV3( - context.Random, - avatarState, - Foods, - runeStates, - new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - 0, - sheets.GetSimulatorSheets(), - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards(context.Random, stageRow, materialSheet, PlayCount)); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", - addressesHex, - sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - context.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - // This conditional logic is same as written in the - // HackAndSlash("hack_and_slash18") action. - if (context.BlockIndex < ActionObsoleteConfig.V100310ExecutedBlockIndex) - { - var player = simulator.Player; - foreach (var key in player.monsterMapForBeforeV100310.Keys) - { - player.monsterMap.Add(key, player.monsterMapForBeforeV100310[key]); - } - - player.monsterMapForBeforeV100310.Clear(); - - foreach (var key in player.eventMapForBeforeV100310.Keys) - { - player.eventMap.Add(key, player.eventMapForBeforeV100310[key]); - } - - player.eventMapForBeforeV100310.Clear(); - } - - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = context.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(AvatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle12.cs b/Lib9c/Action/MimisbrunnrBattle12.cs deleted file mode 100644 index c7185e9337..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle12.cs +++ /dev/null @@ -1,446 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; - -using Nekoyume.Extensions; -using Nekoyume.Model; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.Item; -using Nekoyume.Model.Quest; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using Skill = Nekoyume.Model.Skill.Skill; -using static Lib9c.SerializeKeys; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Rune; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle12")] - public class MimisbrunnrBattle12 : GameAction, IMimisbrunnrBattleV5 - { - public List Costumes; - public List Equipments; - public List Foods; - public List RuneInfos; - public int WorldId; - public int StageId; - public int PlayCount = 1; - public Address AvatarAddress; - - IEnumerable IMimisbrunnrBattleV5.Costumes => Costumes; - IEnumerable IMimisbrunnrBattleV5.Equipments => Equipments; - IEnumerable IMimisbrunnrBattleV5.Foods => Foods; - IEnumerable IMimisbrunnrBattleV5.RuneSlotInfos => - RuneInfos.Select(x => x.Serialize()); - int IMimisbrunnrBattleV5.WorldId => WorldId; - int IMimisbrunnrBattleV5.StageId => StageId; - int IMimisbrunnrBattleV5.PlayCount => PlayCount; - Address IMimisbrunnrBattleV5.AvatarAddress => AvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(Costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(Equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(Foods.OrderBy(i => i).Select(e => e.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["worldId"] = WorldId.Serialize(), - ["stageId"] = StageId.Serialize(), - ["playCount"] = PlayCount.Serialize(), - ["avatarAddress"] = AvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - Costumes = ((List)plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - Equipments = ((List)plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - Foods = ((List)plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - WorldId = plainValue["worldId"].ToInteger(); - StageId = plainValue["stageId"].ToInteger(); - PlayCount = plainValue["playCount"].ToInteger(); - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr exec started", - addressesHex); - - if (!states.TryGetAvatarStateV2( - context.Signer, - AvatarAddress, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - "Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheets( - containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(WorldUnlockSheet), - typeof(MimisbrunnrSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Get Sheets: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var worldSheet = sheets.GetSheet(); - 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}"); - } - - if (!sheets.GetSheet().TryGetValue(StageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), StageId); - } - - var worldUnlockSheet = sheets.GetSheet(); - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(WorldId, out var world)) - { - // NOTE: Add new World from WorldSheet - worldInformation.AddAndUnlockMimisbrunnrWorld( - worldRow, - context.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, context.BlockIndex, worldSheet); - if (!worldInformation.TryGetWorld(WorldId, out world)) - { - // Do nothing. - } - } - } - - 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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = sheets.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(StageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - "MimisbrunnrSheet", - StageId); - } - - foreach (var equipmentId in Equipments) - { - if (!avatarState.inventory.TryGetNonFungibleItem( - equipmentId, - out ItemUsable itemUsable)) - { - continue; - } - - var elementalType = ((Equipment)itemUsable).ElementalType; - if (!mimisbrunnrSheetRow.ElementalTypes.Exists(x => - x == elementalType)) - { - throw new InvalidElementalException( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", - addressesHex, - sw.Elapsed); - - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(Foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - - sw.Restart(); - - if (PlayCount <= 0) - { - throw new PlayCountIsZeroException( - $"{addressesHex}playCount must be greater than 0." + - $" current playCount : {PlayCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * PlayCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point:" + - $" current({avatarState.actionPoint}) < required({totalCostActionPoint})" - ); - } - - var equippableItem = Costumes.Concat(Equipments); - avatarState.EquipItems(equippableItem); - var requirementSheet = sheets.GetSheet(); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - requirementSheet, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - avatarState.actionPoint -= totalCostActionPoint; - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - var materialSheet = sheets.GetSheet(); - - // update rune slot - if (RuneInfos is null) - { - throw new RuneInfosIsEmptyException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - if (RuneInfos.GroupBy(x => x.SlotIndex).Count() != RuneInfos.Count) - { - throw new DuplicatedRuneSlotIndexException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Adventure); - - if (RuneInfos.Exists(x => x.SlotIndex >= runeSlotState.GetRuneSlot().Count)) - { - throw new SlotNotFoundException( - $"[{nameof(MimisbrunnrBattle)}] my avatar address : {AvatarAddress}"); - } - - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Adventure); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Adventure); - itemSlotState.UpdateEquipment(Equipments); - itemSlotState.UpdateCostumes(Costumes); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - var simulator = new StageSimulatorV3( - context.Random, - avatarState, - Foods, - runeStates, - new List(), - WorldId, - StageId, - stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), - 0, - sheets.GetSimulatorSheets(), - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards(context.Random, stageRow, materialSheet, PlayCount)); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", - addressesHex, - sw.Elapsed); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", - addressesHex, - sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - context.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - // This conditional logic is same as written in the - // HackAndSlash("hack_and_slash18") action. - if (context.BlockIndex < ActionObsoleteConfig.V100310ExecutedBlockIndex) - { - var player = simulator.Player; - foreach (var key in player.monsterMapForBeforeV100310.Keys) - { - player.monsterMap.Add(key, player.monsterMapForBeforeV100310[key]); - } - - player.monsterMapForBeforeV100310.Clear(); - - foreach (var key in player.eventMapForBeforeV100310.Keys) - { - player.eventMap.Add(key, player.eventMapForBeforeV100310[key]); - } - - player.eventMapForBeforeV100310.Clear(); - } - - avatarState.Update(simulator); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = context.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(AvatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Debug( - "{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle2.cs b/Lib9c/Action/MimisbrunnrBattle2.cs deleted file mode 100644 index 9d72aca7da..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle2.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle2")] - public class MimisbrunnrBattle2 : GameAction, IMimisbrunnrBattleV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - - IEnumerable IMimisbrunnrBattleV1.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV1.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV1.Foods => foods; - int IMimisbrunnrBattleV1.WorldId => worldId; - int IMimisbrunnrBattleV1.StageId => stageId; - Address IMimisbrunnrBattleV1.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IMimisbrunnrBattleV1.RankingMapAddress => RankingMapAddress; - - private const int AlfheimId = 2; - public BattleLog Result { get; private set; } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(RankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - return states.SetState(WeeklyArenaAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - 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.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (avatarState.RankingMapAddress != RankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - var worldUnlockSheet = states.GetSheet(); - 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(); - 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.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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, 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( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < {stageRow.CostAP}" - ); - } - avatarState.actionPoint -= stageRow.CostAP; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV2(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(RankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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) - { - var characterSheet = states.GetSheet(); - 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}Mimisbrunnr Update WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var weeklySerialized = weekly.Serialize(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - - states = states.SetState(weekly.address, weeklySerialized); - } - } - - Result = simulator.Log; - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle3.cs b/Lib9c/Action/MimisbrunnrBattle3.cs deleted file mode 100644 index 5d41dc1b7a..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle3.cs +++ /dev/null @@ -1,335 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle3")] - public class MimisbrunnrBattle3 : GameAction, IMimisbrunnrBattleV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - - IEnumerable IMimisbrunnrBattleV1.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV1.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV1.Foods => foods; - int IMimisbrunnrBattleV1.WorldId => worldId; - int IMimisbrunnrBattleV1.StageId => stageId; - Address IMimisbrunnrBattleV1.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IMimisbrunnrBattleV1.RankingMapAddress => RankingMapAddress; - - private const int AlfheimId = 2; - public BattleLog Result { get; private set; } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(RankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - return states.SetState(WeeklyArenaAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - 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.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (avatarState.RankingMapAddress != RankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - var worldUnlockSheet = states.GetSheet(); - 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(); - 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.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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, 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( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < {stageRow.CostAP}" - ); - } - avatarState.actionPoint -= stageRow.CostAP; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV2(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states.SetState(avatarAddress, avatarState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(RankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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) - { - var characterSheet = states.GetSheet(); - 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}Mimisbrunnr Update WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var weeklySerialized = weekly.Serialize(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - - states = states.SetState(weekly.address, weeklySerialized); - } - } - - Result = simulator.Log; - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle4.cs b/Lib9c/Action/MimisbrunnrBattle4.cs deleted file mode 100644 index 00a8ff7133..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle4.cs +++ /dev/null @@ -1,343 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle4")] - public class MimisbrunnrBattle4 : GameAction, IMimisbrunnrBattleV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - - IEnumerable IMimisbrunnrBattleV1.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV1.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV1.Foods => foods; - int IMimisbrunnrBattleV1.WorldId => worldId; - int IMimisbrunnrBattleV1.StageId => stageId; - Address IMimisbrunnrBattleV1.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IMimisbrunnrBattleV1.RankingMapAddress => RankingMapAddress; - - private const int AlfheimId = 2; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(RankingMapAddress, MarkChanged); - states = states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(WeeklyArenaAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (avatarState.RankingMapAddress != RankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - var worldUnlockSheet = states.GetSheet(); - 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(); - 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.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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, 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( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < {stageRow.CostAP}" - ); - } - avatarState.actionPoint -= stageRow.CostAP; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV2(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(RankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr 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) - { - var characterSheet = states.GetSheet(); - 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}Mimisbrunnr Update WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var weeklySerialized = weekly.Serialize(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - - states = states.SetState(weekly.address, weeklySerialized); - } - } - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle5.cs b/Lib9c/Action/MimisbrunnrBattle5.cs deleted file mode 100644 index 2ce529fa6e..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle5.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle5")] - public class MimisbrunnrBattle5 : GameAction, IMimisbrunnrBattleV2 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address rankingMapAddress; - - IEnumerable IMimisbrunnrBattleV2.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV2.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV2.Foods => foods; - int IMimisbrunnrBattleV2.WorldId => worldId; - int IMimisbrunnrBattleV2.StageId => stageId; - Address IMimisbrunnrBattleV2.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV2.RankingMapAddress => rankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["rankingMapAddress"] = rankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - rankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(rankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100083ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (avatarState.RankingMapAddress != rankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - 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(); - if (!stageSheet.TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldUnlockSheet = states.GetSheet(); - 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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, 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( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < {stageRow.CostAP}" - ); - } - avatarState.actionPoint -= stageRow.CostAP; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100025); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV4(); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear && states.TryGetState(rankingMapAddress, out Dictionary d)) - { - var ranking = new RankingMapState(d); - ranking.Update(avatarState); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(rankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle6.cs b/Lib9c/Action/MimisbrunnrBattle6.cs deleted file mode 100644 index 9f53cac15d..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle6.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle6")] - public class MimisbrunnrBattle6 : GameAction, IMimisbrunnrBattleV3 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - public Address rankingMapAddress; - - IEnumerable IMimisbrunnrBattleV3.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV3.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV3.Foods => foods; - int IMimisbrunnrBattleV3.WorldId => worldId; - int IMimisbrunnrBattleV3.StageId => stageId; - int IMimisbrunnrBattleV3.PlayCount => playCount; - Address IMimisbrunnrBattleV3.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV3.RankingMapAddress => rankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["rankingMapAddress"] = rankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - rankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(rankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100086ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (avatarState.RankingMapAddress != rankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - 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(); - if (!stageSheet.TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldUnlockSheet = states.GetSheet(); - 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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, sw.Elapsed); - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - avatarState.actionPoint -= totalCostActionPoint; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV5(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear && states.TryGetState(rankingMapAddress, out Dictionary d)) - { - var ranking = new RankingMapState(d); - ranking.Update(avatarState); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(rankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle7.cs b/Lib9c/Action/MimisbrunnrBattle7.cs deleted file mode 100644 index 03a9a5027d..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle7.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle7")] - public class MimisbrunnrBattle7 : GameAction, IMimisbrunnrBattleV3 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - public Address rankingMapAddress; - - IEnumerable IMimisbrunnrBattleV3.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV3.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV3.Foods => foods; - int IMimisbrunnrBattleV3.WorldId => worldId; - int IMimisbrunnrBattleV3.StageId => stageId; - int IMimisbrunnrBattleV3.PlayCount => playCount; - Address IMimisbrunnrBattleV3.AvatarAddress => avatarAddress; - Address IMimisbrunnrBattleV3.RankingMapAddress => rankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["rankingMapAddress"] = rankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - rankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(rankingMapAddress, MarkChanged); - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100170ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (avatarState.RankingMapAddress != rankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - var worldSheet = states.GetSheet(); - 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(); - if (!stageSheet.TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldUnlockSheet = states.GetSheet(); - 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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, sw.Elapsed); - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - avatarState.actionPoint -= totalCostActionPoint; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.IsClear && states.TryGetState(rankingMapAddress, out Dictionary d)) - { - var ranking = new RankingMapState(d); - ranking.Update(avatarState); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(rankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle8.cs b/Lib9c/Action/MimisbrunnrBattle8.cs deleted file mode 100644 index 77a6c777de..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle8.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Obsoleted at https://github.com/planetarium/lib9c/pull/1241 - /// - [Serializable] - [ActionType("mimisbrunnr_battle8")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class MimisbrunnrBattle8 : GameAction, IMimisbrunnrBattleV4 - { - private const long ObsoletedBlockIndex = - ActionObsoleteConfig.V100270ObsoleteIndex; - - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - - IEnumerable IMimisbrunnrBattleV4.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV4.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV4.Foods => foods; - int IMimisbrunnrBattleV4.WorldId => worldId; - int IMimisbrunnrBattleV4.StageId => stageId; - int IMimisbrunnrBattleV4.PlayCount => playCount; - Address IMimisbrunnrBattleV4.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ObsoletedBlockIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var worldSheet = states.GetSheet(); - 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(); - if (!stageSheet.TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldUnlockSheet = states.GetSheet(); - 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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, sw.Elapsed); - - avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - avatarState.actionPoint -= totalCostActionPoint; - var equippableItem = new List(); - equippableItem.AddRange(costumes); - equippableItem.AddRange(equipments); - avatarState.EquipItems(equippableItem); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle9.cs b/Lib9c/Action/MimisbrunnrBattle9.cs deleted file mode 100644 index 91ee92fabd..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle9.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/884 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// Updated at https://github.com/planetarium/lib9c/pull/1241 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("mimisbrunnr_battle9")] - public class MimisbrunnrBattle9 : GameAction, IMimisbrunnrBattleV4 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public int playCount = 1; - public Address avatarAddress; - - IEnumerable IMimisbrunnrBattleV4.Costumes => costumes; - IEnumerable IMimisbrunnrBattleV4.Equipments => equipments; - IEnumerable IMimisbrunnrBattleV4.Foods => foods; - int IMimisbrunnrBattleV4.WorldId => worldId; - int IMimisbrunnrBattleV4.StageId => stageId; - int IMimisbrunnrBattleV4.PlayCount => playCount; - Address IMimisbrunnrBattleV4.AvatarAddress => avatarAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["playCount"] = playCount.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToGuid()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - playCount = plainValue["playCount"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - states = states.SetState(avatarAddress, MarkChanged); - states = states - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out AvatarState avatarState, out _)) - { - throw new FailedLoadStateException("Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var worldSheet = states.GetSheet(); - 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(); - if (!stageSheet.TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldUnlockSheet = states.GetSheet(); - 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($"{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}" - ); - } - - sw.Restart(); - var mimisbrunnrSheet = states.GetSheet(); - if (!mimisbrunnrSheet.TryGetValue(stageId, out var mimisbrunnrSheetRow)) - { - throw new SheetRowNotFoundException("MimisbrunnrSheet", addressesHex, 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( - $"{addressesHex}ElementalType of {equipmentId} does not match."); - } - } - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Check Equipments ElementalType: {Elapsed}", addressesHex, sw.Elapsed); - - var equipmentList = avatarState.ValidateEquipmentsV2(equipments, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(foods, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(costumes); - - sw.Restart(); - - if (playCount <= 0) - { - throw new PlayCountIsZeroException($"{addressesHex}playCount must be greater than 0. " + - $"current playCount : {playCount}"); - } - - var totalCostActionPoint = stageRow.CostAP * playCount; - if (avatarState.actionPoint < totalCostActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < totalAP({totalCostActionPoint}) = cost({stageRow.CostAP}) * boostCount({playCount})" - ); - } - - var equippableItem = costumes.Concat(equipments); - avatarState.EquipItems(equippableItem); - var requirementSheet = states.GetSheet(); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - requirementSheet, - states.GetSheet(), - states.GetSheet(), - states.GetSheet(), - addressesHex); - - avatarState.actionPoint -= totalCostActionPoint; - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - ctx.Random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet, - StageSimulatorV1.ConstructorVersionV100080, - playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.Simulate(playCount); - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute Mimisbrunnr({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) - { - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUp(); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}Mimisbrunnr Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Mimisbrunnr Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/MonsterCollect.cs b/Lib9c/Action/MonsterCollect.cs deleted file mode 100644 index 1c59fcc16c..0000000000 --- a/Lib9c/Action/MonsterCollect.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/500 - /// Updated at https://github.com/planetarium/lib9c/pull/958 - /// - [Serializable] - [ActionType("monster_collect3")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class MonsterCollect : GameAction, IMonsterCollectV2 - { - public int level; - - int IMonsterCollectV2.Level => level; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - 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(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 0)) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 1)) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 2)) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 3)); - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, context.Signer); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}MonsterCollect exec started", addressesHex); - if (states.TryGetStakeState(context.Signer, out StakeState _)) - { - throw new InvalidOperationException( - "The user has staked. You cannot stake and monster-collect at the same time."); - } - - MonsterCollectionSheet monsterCollectionSheet = states.GetSheet(); - - 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(context, 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( - $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}", - context.Signer, - requiredGold - ); - } - states = states.TransferAsset(context, context.Signer, monsterCollectionAddress, requiredGold); - states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize()); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}MonsterCollect Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [LevelKey] = level.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - level = plainValue[LevelKey].ToInteger(); - } - } -} diff --git a/Lib9c/Action/MonsterCollect0.cs b/Lib9c/Action/MonsterCollect0.cs deleted file mode 100644 index 026cb0bac9..0000000000 --- a/Lib9c/Action/MonsterCollect0.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("monster_collect")] - public class MonsterCollect0 : GameAction, IMonsterCollectV1 - { - public int level; - public int collectionRound; - - int IMonsterCollectV1.Level => level; - int IMonsterCollectV1.CollectionRound => collectionRound; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - Address monsterCollectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectionRound); - if (context.Rehearsal) - { - return states - .SetState(monsterCollectionAddress, MarkChanged) - .SetState(context.Signer, MarkChanged) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, monsterCollectionAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - MonsterCollectionSheet monsterCollectionSheet = states.GetSheet(); - - 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(); - 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( - $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}", - context.Signer, - requiredGold); - } - states = states.TransferAsset(context, context.Signer, monsterCollectionAddress, requiredGold); - states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize()); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [LevelKey] = level.Serialize(), - [MonsterCollectionRoundKey] = collectionRound.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - level = plainValue[LevelKey].ToInteger(); - collectionRound = plainValue[MonsterCollectionRoundKey].ToInteger(); - } - } -} diff --git a/Lib9c/Action/MonsterCollect2.cs b/Lib9c/Action/MonsterCollect2.cs deleted file mode 100644 index cfc1298c3e..0000000000 --- a/Lib9c/Action/MonsterCollect2.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("monster_collect2")] - public class MonsterCollect2 : GameAction, IMonsterCollectV2 - { - public int level; - - int IMonsterCollectV2.Level => level; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - 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(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 0)) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 1)) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 2)) - .MarkBalanceChanged(context, GoldCurrencyMock, context.Signer, MonsterCollectionState.DeriveAddress(context.Signer, 3)); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - MonsterCollectionSheet monsterCollectionSheet = states.GetSheet(); - - 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; - FungibleAssetValue balance = states.GetBalance(context.Signer, currency); - 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(context, monsterCollectionAddress, context.Signer, gold); - } - - if (level == 0) - { - return states.SetState(monsterCollectionAddress, new Null()); - } - - 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( - $"There is no sufficient balance for {context.Signer}: {balance} < {requiredGold}", - context.Signer, - requiredGold); - } - states = states.TransferAsset(context, context.Signer, monsterCollectionAddress, requiredGold); - states = states.SetState(monsterCollectionAddress, monsterCollectionState.Serialize()); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [LevelKey] = level.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - level = plainValue[LevelKey].ToInteger(); - } - } -} diff --git a/Lib9c/Action/Raid1.cs b/Lib9c/Action/Raid1.cs deleted file mode 100644 index aa26de3e54..0000000000 --- a/Lib9c/Action/Raid1.cs +++ /dev/null @@ -1,288 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Arena; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("raid")] - public class Raid1 : GameAction, IRaidV1 - { - public Address AvatarAddress; - public List EquipmentIds; - public List CostumeIds; - public List FoodIds; - public bool PayNcg; - - Address IRaidV1.AvatarAddress => AvatarAddress; - IEnumerable IRaidV1.EquipmentIds => EquipmentIds; - IEnumerable IRaidV1.CostumeIds => CostumeIds; - IEnumerable IRaidV1.FoodIds => FoodIds; - bool IRaidV1.PayNcg => PayNcg; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - if (!states.TryGetAvatarStateV2(context.Signer, AvatarAddress, - out AvatarState avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"Aborted as the avatar state of the signer was failed to load."); - } - // Check stage level. - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInRaid)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out int current); - throw new NotEnoughClearedStageLevelException(AvatarAddress.ToHex(), - GameConfig.RequireClearedStageLevel.ActionsInRaid, current); - } - - Dictionary sheets = states.GetSheetsV1( - containRaidSimulatorSheets: true, - sheetTypes: new [] { - typeof(MaterialItemSheet), - typeof(SkillSheet), - typeof(SkillBuffSheet), - typeof(StatBuffSheet), - typeof(CharacterLevelSheet), - typeof(EquipmentItemSetEffectSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(WorldBossCharacterSheet), - typeof(WorldBossListSheet), - typeof(WorldBossGlobalHpSheet), - typeof(WorldBossActionPatternSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(RuneWeightSheet), - typeof(WorldBossKillRewardSheet), - typeof(RuneSheet), - }); - var worldBossListSheet = sheets.GetSheet(); - var row = worldBossListSheet.FindRowByBlockIndex(context.BlockIndex); - int raidId = row.Id; - if (raidId > 1) - { - throw new ActionObsoletedException("raid action is obsoleted. please use new action."); - } - Address worldBossAddress = Addresses.GetWorldBossAddress(raidId); - Address raiderAddress = Addresses.GetRaiderAddress(AvatarAddress, raidId); - // Check challenge count. - RaiderState raiderState; - if (states.TryGetState(raiderAddress, out List rawState)) - { - raiderState = new RaiderState(rawState); - } - else - { - raiderState = new RaiderState(); - FungibleAssetValue crystalCost = CrystalCalculator.CalculateEntranceFee(avatarState.level, row.EntranceFee); - states = states.TransferAsset(context, context.Signer, worldBossAddress, crystalCost); - } - - if (context.BlockIndex - raiderState.UpdatedBlockIndex < Raid4.RequiredInterval) - { - throw new RequiredBlockIntervalException($"wait for interval. {context.BlockIndex - raiderState.UpdatedBlockIndex}"); - } - - if (WorldBossHelper.CanRefillTicketV1(context.BlockIndex, raiderState.RefillBlockIndex, row.StartedBlockIndex)) - { - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.RefillBlockIndex = context.BlockIndex; - } - - if (raiderState.RemainChallengeCount < 1) - { - if (PayNcg) - { - if (raiderState.PurchaseCount >= row.MaxPurchaseCount) - { - throw new ExceedTicketPurchaseLimitException(""); - } - var goldCurrency = states.GetGoldCurrency(); - states = states.TransferAsset(context, context.Signer, worldBossAddress, - WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); - raiderState.PurchaseCount++; - } - else - { - throw new ExceedPlayCountException(""); - } - } - - // Validate equipment, costume. - var equipmentList = avatarState.ValidateEquipmentsV2(EquipmentIds, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(FoodIds, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(CostumeIds); - int previousHighScore = raiderState.HighScore; - WorldBossState bossState; - WorldBossGlobalHpSheet hpSheet = sheets.GetSheet(); - if (states.TryGetState(worldBossAddress, out List rawBossState)) - { - bossState = new WorldBossState(rawBossState); - } - else - { - bossState = new WorldBossState(row, hpSheet[1]); - } - - var addressesHex = $"[{context.Signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var items = EquipmentIds.Concat(CostumeIds); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var raidSimulatorSheets = sheets.GetRaidSimulatorSheetsV1(); - - // Simulate. - var simulator = new RaidSimulatorV1( - row.BossId, - context.Random, - avatarState, - FoodIds, - raidSimulatorSheets, - sheets.GetSheet()); - simulator.Simulate(); - avatarState.inventory = simulator.Player.Inventory; - - int score = simulator.DamageDealt; - int cp = CPHelper.GetCPV2(avatarState, sheets.GetSheet(), - sheets.GetSheet()); - raiderState.Update(avatarState, cp, score, PayNcg, context.BlockIndex); - - // Reward. - bossState.CurrentHp -= score; - if (bossState.CurrentHp <= 0) - { - if (bossState.Level < hpSheet.OrderedList.Last().Level) - { - bossState.Level++; - } - bossState.CurrentHp = hpSheet[bossState.Level].Hp; - } - - // Update State. - - // battle reward - foreach (var battleReward in simulator.AssetReward) - { - if (battleReward.Currency.Equals(CrystalCalculator.CRYSTAL)) - { - states = states.MintAsset(context, context.Signer, battleReward); - } - else - { - states = states.MintAsset(context, AvatarAddress, battleReward); - } - } - - if (raiderState.LatestBossLevel < bossState.Level) - { - // kill reward - var worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(AvatarAddress, raidId); - WorldBossKillRewardRecord rewardRecord; - if (states.TryGetState(worldBossKillRewardRecordAddress, out List rawList)) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[row.BossId]; - rewardRecord = new WorldBossKillRewardRecord(rawList); - // calculate with previous high score. - int rank = WorldBossHelper.CalculateRank(bossRow, previousHighScore); - states = states.SetWorldBossKillReward( - context, - worldBossKillRewardRecordAddress, - rewardRecord, - rank, - bossState, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.Random, - AvatarAddress, - context.Signer - ); - } - else - { - rewardRecord = new WorldBossKillRewardRecord(); - } - - // Save level infos; - raiderState.LatestBossLevel = bossState.Level; - if (!rewardRecord.ContainsKey(raiderState.LatestBossLevel)) - { - rewardRecord.Add(raiderState.LatestBossLevel, false); - } - states = states.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInfoAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInfoAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - } - - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldBossAddress, bossState.Serialize()) - .SetState(raiderAddress, raiderState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["e"] = new List(EquipmentIds.Select(e => e.Serialize())), - ["c"] = new List(CostumeIds.Select(c => c.Serialize())), - ["f"] = new List(FoodIds.Select(f => f.Serialize())), - ["p"] = PayNcg.Serialize(), - } - .ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - EquipmentIds = plainValue["e"].ToList(StateExtensions.ToGuid); - CostumeIds = plainValue["c"].ToList(StateExtensions.ToGuid); - FoodIds = plainValue["f"].ToList(StateExtensions.ToGuid); - PayNcg = plainValue["p"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/Raid2.cs b/Lib9c/Action/Raid2.cs deleted file mode 100644 index 0bf006f6d5..0000000000 --- a/Lib9c/Action/Raid2.cs +++ /dev/null @@ -1,301 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Arena; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1419 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("raid2")] - public class Raid2 : GameAction, IRaidV1 - { - public Address AvatarAddress; - public List EquipmentIds; - public List CostumeIds; - public List FoodIds; - public bool PayNcg; - - Address IRaidV1.AvatarAddress => AvatarAddress; - IEnumerable IRaidV1.EquipmentIds => EquipmentIds; - IEnumerable IRaidV1.CostumeIds => CostumeIds; - IEnumerable IRaidV1.FoodIds => FoodIds; - bool IRaidV1.PayNcg => PayNcg; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - var addressHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid exec started", addressHex); - if (!states.TryGetAvatarStateV2(context.Signer, AvatarAddress, - out AvatarState avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"Aborted as the avatar state of the signer was failed to load."); - } - // Check stage level. - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInRaid)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out int current); - throw new NotEnoughClearedStageLevelException(AvatarAddress.ToHex(), - GameConfig.RequireClearedStageLevel.ActionsInRaid, current); - } - - Dictionary sheets = states.GetSheetsV1( - containRaidSimulatorSheets: true, - sheetTypes: new [] { - typeof(MaterialItemSheet), - typeof(SkillSheet), - typeof(SkillBuffSheet), - typeof(StatBuffSheet), - typeof(CharacterLevelSheet), - typeof(EquipmentItemSetEffectSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(WorldBossCharacterSheet), - typeof(WorldBossListSheet), - typeof(WorldBossGlobalHpSheet), - typeof(WorldBossActionPatternSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(RuneWeightSheet), - typeof(WorldBossKillRewardSheet), - typeof(RuneSheet), - }); - var worldBossListSheet = sheets.GetSheet(); - var row = worldBossListSheet.FindRowByBlockIndex(context.BlockIndex); - int raidId = row.Id; - Address worldBossAddress = Addresses.GetWorldBossAddress(raidId); - Address raiderAddress = Addresses.GetRaiderAddress(AvatarAddress, raidId); - // Check challenge count. - RaiderState raiderState; - if (states.TryGetState(raiderAddress, out List rawState)) - { - raiderState = new RaiderState(rawState); - } - else - { - raiderState = new RaiderState(); - FungibleAssetValue crystalCost = CrystalCalculator.CalculateEntranceFee(avatarState.level, row.EntranceFee); - states = states.TransferAsset(context, context.Signer, worldBossAddress, crystalCost); - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - List
raiderList = - states.TryGetState(raiderListAddress, out List rawRaiderList) - ? rawRaiderList.ToList(StateExtensions.ToAddress) - : new List
(); - raiderList.Add(raiderAddress); - states = states.SetState(raiderListAddress, - new List(raiderList.Select(a => a.Serialize()))); - } - - if (context.BlockIndex - raiderState.UpdatedBlockIndex < Raid4.RequiredInterval) - { - throw new RequiredBlockIntervalException($"wait for interval. {context.BlockIndex - raiderState.UpdatedBlockIndex}"); - } - - if (WorldBossHelper.CanRefillTicketV1(context.BlockIndex, raiderState.RefillBlockIndex, row.StartedBlockIndex)) - { - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.RefillBlockIndex = context.BlockIndex; - } - - if (raiderState.RemainChallengeCount < 1) - { - if (PayNcg) - { - if (raiderState.PurchaseCount >= row.MaxPurchaseCount) - { - throw new ExceedTicketPurchaseLimitException(""); - } - var goldCurrency = states.GetGoldCurrency(); - states = states.TransferAsset(context, context.Signer, worldBossAddress, - WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); - raiderState.PurchaseCount++; - } - else - { - throw new ExceedPlayCountException(""); - } - } - - // Validate equipment, costume. - var equipmentList = avatarState.ValidateEquipmentsV2(EquipmentIds, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(FoodIds, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(CostumeIds); - int previousHighScore = raiderState.HighScore; - WorldBossState bossState; - WorldBossGlobalHpSheet hpSheet = sheets.GetSheet(); - if (states.TryGetState(worldBossAddress, out List rawBossState)) - { - bossState = new WorldBossState(rawBossState); - } - else - { - bossState = new WorldBossState(row, hpSheet[1]); - } - - var addressesHex = $"[{context.Signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var items = EquipmentIds.Concat(CostumeIds); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var raidSimulatorSheets = sheets.GetRaidSimulatorSheetsV1(); - - // Simulate. - var simulator = new RaidSimulatorV1( - row.BossId, - context.Random, - avatarState, - FoodIds, - raidSimulatorSheets, - sheets.GetSheet()); - simulator.Simulate(); - avatarState.inventory = simulator.Player.Inventory; - - int score = simulator.DamageDealt; - int cp = CPHelper.GetCPV2(avatarState, sheets.GetSheet(), - sheets.GetSheet()); - raiderState.Update(avatarState, cp, score, PayNcg, context.BlockIndex); - - // Reward. - bossState.CurrentHp -= score; - if (bossState.CurrentHp <= 0) - { - if (bossState.Level < hpSheet.OrderedList.Last().Level) - { - bossState.Level++; - } - bossState.CurrentHp = hpSheet[bossState.Level].Hp; - } - - // Update State. - - // battle reward - foreach (var battleReward in simulator.AssetReward) - { - if (battleReward.Currency.Equals(CrystalCalculator.CRYSTAL)) - { - states = states.MintAsset(context, context.Signer, battleReward); - } - else - { - states = states.MintAsset(context, AvatarAddress, battleReward); - } - } - - if (raiderState.LatestBossLevel < bossState.Level) - { - // kill reward - var worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(AvatarAddress, raidId); - WorldBossKillRewardRecord rewardRecord; - if (states.TryGetState(worldBossKillRewardRecordAddress, out List rawList)) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[row.BossId]; - rewardRecord = new WorldBossKillRewardRecord(rawList); - // calculate with previous high score. - int rank = WorldBossHelper.CalculateRank(bossRow, previousHighScore); - states = states.SetWorldBossKillReward( - context, - worldBossKillRewardRecordAddress, - rewardRecord, - rank, - bossState, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.Random, - AvatarAddress, - context.Signer - ); - } - else - { - rewardRecord = new WorldBossKillRewardRecord(); - } - - // Save level infos; - raiderState.LatestBossLevel = bossState.Level; - if (!rewardRecord.ContainsKey(raiderState.LatestBossLevel)) - { - rewardRecord.Add(raiderState.LatestBossLevel, false); - } - states = states.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInfoAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInfoAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid Total Executed Time: {Elapsed}", addressHex, ended - started); - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldBossAddress, bossState.Serialize()) - .SetState(raiderAddress, raiderState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["e"] = new List(EquipmentIds.Select(e => e.Serialize())), - ["c"] = new List(CostumeIds.Select(c => c.Serialize())), - ["f"] = new List(FoodIds.Select(f => f.Serialize())), - ["p"] = PayNcg.Serialize(), - } - .ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - EquipmentIds = plainValue["e"].ToList(StateExtensions.ToGuid); - CostumeIds = plainValue["c"].ToList(StateExtensions.ToGuid); - FoodIds = plainValue["f"].ToList(StateExtensions.ToGuid); - PayNcg = plainValue["p"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/Raid3.cs b/Lib9c/Action/Raid3.cs deleted file mode 100644 index f9a52e0d53..0000000000 --- a/Lib9c/Action/Raid3.cs +++ /dev/null @@ -1,370 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1495 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("raid3")] - public class Raid3 : GameAction, IRaidV2 - { - public Address AvatarAddress; - public List EquipmentIds; - public List CostumeIds; - public List FoodIds; - public List RuneInfos; - public bool PayNcg; - - Address IRaidV2.AvatarAddress => AvatarAddress; - IEnumerable IRaidV2.EquipmentIds => EquipmentIds; - IEnumerable IRaidV2.CostumeIds => CostumeIds; - IEnumerable IRaidV2.FoodIds => FoodIds; - IEnumerable IRaidV2.RuneSlotInfos => RuneInfos.Select(x => x.Serialize()); - bool IRaidV2.PayNcg => PayNcg; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var addressHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid exec started", addressHex); - if (!states.TryGetAvatarStateV2(context.Signer, AvatarAddress, - out AvatarState avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"Aborted as the avatar state of the signer was failed to load."); - } - // Check stage level. - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInRaid)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out int current); - throw new NotEnoughClearedStageLevelException(AvatarAddress.ToHex(), - GameConfig.RequireClearedStageLevel.ActionsInRaid, current); - } - - Dictionary sheets = states.GetSheets( - containRaidSimulatorSheets: true, - sheetTypes: new [] { - typeof(MaterialItemSheet), - typeof(SkillSheet), - typeof(SkillBuffSheet), - typeof(StatBuffSheet), - typeof(CharacterLevelSheet), - typeof(EquipmentItemSetEffectSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(WorldBossCharacterSheet), - typeof(WorldBossListSheet), - typeof(WorldBossGlobalHpSheet), - typeof(WorldBossActionPatternSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(RuneWeightSheet), - typeof(WorldBossKillRewardSheet), - typeof(RuneSheet), - typeof(RuneListSheet), - }); - var worldBossListSheet = sheets.GetSheet(); - var row = worldBossListSheet.FindRowByBlockIndex(context.BlockIndex); - int raidId = row.Id; - Address worldBossAddress = Addresses.GetWorldBossAddress(raidId); - Address raiderAddress = Addresses.GetRaiderAddress(AvatarAddress, raidId); - // Check challenge count. - RaiderState raiderState; - if (states.TryGetState(raiderAddress, out List rawState)) - { - raiderState = new RaiderState(rawState); - } - else - { - raiderState = new RaiderState(); - FungibleAssetValue crystalCost = CrystalCalculator.CalculateEntranceFee(avatarState.level, row.EntranceFee); - states = states.TransferAsset(context, context.Signer, worldBossAddress, crystalCost); - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - List
raiderList = - states.TryGetState(raiderListAddress, out List rawRaiderList) - ? rawRaiderList.ToList(StateExtensions.ToAddress) - : new List
(); - raiderList.Add(raiderAddress); - states = states.SetState(raiderListAddress, - new List(raiderList.Select(a => a.Serialize()))); - } - - if (context.BlockIndex - raiderState.UpdatedBlockIndex < Raid4.RequiredInterval) - { - throw new RequiredBlockIntervalException($"wait for interval. {context.BlockIndex - raiderState.UpdatedBlockIndex}"); - } - - if (WorldBossHelper.CanRefillTicketV1(context.BlockIndex, raiderState.RefillBlockIndex, row.StartedBlockIndex)) - { - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.RefillBlockIndex = context.BlockIndex; - } - - if (raiderState.RemainChallengeCount < 1) - { - if (PayNcg) - { - if (raiderState.PurchaseCount >= row.MaxPurchaseCount) - { - throw new ExceedTicketPurchaseLimitException(""); - } - var goldCurrency = states.GetGoldCurrency(); - states = states.TransferAsset(context, context.Signer, worldBossAddress, - WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); - raiderState.PurchaseCount++; - } - else - { - throw new ExceedPlayCountException(""); - } - } - - // Validate equipment, costume. - var equipmentList = avatarState.ValidateEquipmentsV2(EquipmentIds, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(FoodIds, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(CostumeIds); - - // Update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Raid); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Raid); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlotV2(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // Update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Raid); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Raid); - itemSlotState.UpdateEquipment(EquipmentIds); - itemSlotState.UpdateCostumes(CostumeIds); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - int previousHighScore = raiderState.HighScore; - WorldBossState bossState; - WorldBossGlobalHpSheet hpSheet = sheets.GetSheet(); - if (states.TryGetState(worldBossAddress, out List rawBossState)) - { - bossState = new WorldBossState(rawBossState); - } - else - { - bossState = new WorldBossState(row, hpSheet[1]); - } - - var addressesHex = $"[{context.Signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var items = EquipmentIds.Concat(CostumeIds); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var raidSimulatorSheets = sheets.GetRaidSimulatorSheets(); - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // Simulate. - var simulator = new RaidSimulatorV2( - row.BossId, - context.Random, - avatarState, - FoodIds, - runeStates, - raidSimulatorSheets, - sheets.GetSheet()); - simulator.Simulate(); - avatarState.inventory = simulator.Player.Inventory; - - var costumeList = new List(); - foreach (var guid in CostumeIds) - { - var costume = avatarState.inventory.Costumes.FirstOrDefault(x => x.ItemId == guid); - if (costume != null) - { - costumeList.Add(costume); - } - } - - var runeOptionSheet = sheets.GetSheet(); - var runeOptions = new List(); - foreach (var runeState in runeStates) - { - if (!runeOptionSheet.TryGetValue(runeState.RuneId, out var optionRow)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.RuneId); - } - - if (!optionRow.LevelOptionMap.TryGetValue(runeState.Level, out var option)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.Level); - } - - runeOptions.Add(option); - } - - var characterSheet = sheets.GetSheet(); - if (!characterSheet.TryGetValue(avatarState.characterId, out var characterRow)) - { - throw new SheetRowNotFoundException("CharacterSheet", avatarState.characterId); - } - - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.TotalCP( - equipmentList, costumeList, - runeOptions, avatarState.level, - characterRow, costumeStatSheet); - int score = simulator.DamageDealt; - raiderState.Update(avatarState, cp, score, PayNcg, context.BlockIndex); - - // Reward. - bossState.CurrentHp -= score; - if (bossState.CurrentHp <= 0) - { - if (bossState.Level < hpSheet.OrderedList.Last().Level) - { - bossState.Level++; - } - bossState.CurrentHp = hpSheet[bossState.Level].Hp; - } - - // battle reward - foreach (var battleReward in simulator.AssetReward) - { - if (battleReward.Currency.Equals(CrystalCalculator.CRYSTAL)) - { - states = states.MintAsset(context, context.Signer, battleReward); - } - else - { - states = states.MintAsset(context, AvatarAddress, battleReward); - } - } - - if (raiderState.LatestBossLevel < bossState.Level) - { - // kill reward - var worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(AvatarAddress, raidId); - WorldBossKillRewardRecord rewardRecord; - if (states.TryGetState(worldBossKillRewardRecordAddress, out List rawList)) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[row.BossId]; - rewardRecord = new WorldBossKillRewardRecord(rawList); - // calculate with previous high score. - int rank = WorldBossHelper.CalculateRank(bossRow, previousHighScore); - states = states.SetWorldBossKillReward( - context, - worldBossKillRewardRecordAddress, - rewardRecord, - rank, - bossState, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.Random, - AvatarAddress, - context.Signer - ); - } - else - { - rewardRecord = new WorldBossKillRewardRecord(); - } - - // Save level infos; - raiderState.LatestBossLevel = bossState.Level; - if (!rewardRecord.ContainsKey(raiderState.LatestBossLevel)) - { - rewardRecord.Add(raiderState.LatestBossLevel, false); - } - states = states.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInfoAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInfoAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid Total Executed Time: {Elapsed}", addressHex, ended - started); - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldBossAddress, bossState.Serialize()) - .SetState(raiderAddress, raiderState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["e"] = new List(EquipmentIds.Select(e => e.Serialize())), - ["c"] = new List(CostumeIds.Select(c => c.Serialize())), - ["f"] = new List(FoodIds.Select(f => f.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["p"] = PayNcg.Serialize(), - } - .ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - EquipmentIds = plainValue["e"].ToList(StateExtensions.ToGuid); - CostumeIds = plainValue["c"].ToList(StateExtensions.ToGuid); - FoodIds = plainValue["f"].ToList(StateExtensions.ToGuid); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - PayNcg = plainValue["p"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/Raid4.cs b/Lib9c/Action/Raid4.cs deleted file mode 100644 index b0928ba165..0000000000 --- a/Lib9c/Action/Raid4.cs +++ /dev/null @@ -1,372 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1663 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("raid4")] - public class Raid4 : GameAction, IRaidV2 - { - public const long RequiredInterval = 5L; - public Address AvatarAddress; - public List EquipmentIds; - public List CostumeIds; - public List FoodIds; - public List RuneInfos; - public bool PayNcg; - - Address IRaidV2.AvatarAddress => AvatarAddress; - IEnumerable IRaidV2.EquipmentIds => EquipmentIds; - IEnumerable IRaidV2.CostumeIds => CostumeIds; - IEnumerable IRaidV2.FoodIds => FoodIds; - IEnumerable IRaidV2.RuneSlotInfos => RuneInfos.Select(x => x.Serialize()); - bool IRaidV2.PayNcg => PayNcg; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - var addressHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid exec started", addressHex); - if (!states.TryGetAvatarStateV2(context.Signer, AvatarAddress, - out AvatarState avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"Aborted as the avatar state of the signer was failed to load."); - } - // Check stage level. - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInRaid)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out int current); - throw new NotEnoughClearedStageLevelException(AvatarAddress.ToHex(), - GameConfig.RequireClearedStageLevel.ActionsInRaid, current); - } - - Dictionary sheets = states.GetSheets( - containRaidSimulatorSheets: true, - sheetTypes: new [] { - typeof(MaterialItemSheet), - typeof(SkillSheet), - typeof(SkillBuffSheet), - typeof(StatBuffSheet), - typeof(CharacterLevelSheet), - typeof(EquipmentItemSetEffectSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(WorldBossCharacterSheet), - typeof(WorldBossListSheet), - typeof(WorldBossGlobalHpSheet), - typeof(WorldBossActionPatternSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(RuneWeightSheet), - typeof(WorldBossKillRewardSheet), - typeof(RuneSheet), - typeof(RuneListSheet), - }); - var worldBossListSheet = sheets.GetSheet(); - var row = worldBossListSheet.FindRowByBlockIndex(context.BlockIndex); - int raidId = row.Id; - Address worldBossAddress = Addresses.GetWorldBossAddress(raidId); - Address raiderAddress = Addresses.GetRaiderAddress(AvatarAddress, raidId); - // Check challenge count. - RaiderState raiderState; - if (states.TryGetState(raiderAddress, out List rawState)) - { - raiderState = new RaiderState(rawState); - } - else - { - raiderState = new RaiderState(); - if (row.EntranceFee > 0) - { - FungibleAssetValue crystalCost = CrystalCalculator.CalculateEntranceFee(avatarState.level, row.EntranceFee); - states = states.TransferAsset(context, context.Signer, worldBossAddress, crystalCost); - } - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - List
raiderList = - states.TryGetState(raiderListAddress, out List rawRaiderList) - ? rawRaiderList.ToList(StateExtensions.ToAddress) - : new List
(); - raiderList.Add(raiderAddress); - states = states.SetState(raiderListAddress, - new List(raiderList.Select(a => a.Serialize()))); - } - - if (context.BlockIndex - raiderState.UpdatedBlockIndex < RequiredInterval) - { - throw new RequiredBlockIntervalException($"wait for interval. {context.BlockIndex - raiderState.UpdatedBlockIndex}"); - } - - if (WorldBossHelper.CanRefillTicketV1(context.BlockIndex, raiderState.RefillBlockIndex, row.StartedBlockIndex)) - { - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.RefillBlockIndex = context.BlockIndex; - } - - if (raiderState.RemainChallengeCount < 1) - { - if (PayNcg) - { - if (raiderState.PurchaseCount >= row.MaxPurchaseCount) - { - throw new ExceedTicketPurchaseLimitException(""); - } - var goldCurrency = states.GetGoldCurrency(); - states = states.TransferAsset(context, context.Signer, worldBossAddress, - WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); - raiderState.PurchaseCount++; - } - else - { - throw new ExceedPlayCountException(""); - } - } - - // Validate equipment, costume. - var equipmentList = avatarState.ValidateEquipmentsV2(EquipmentIds, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(FoodIds, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(CostumeIds); - - // Update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Raid); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Raid); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // Update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Raid); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Raid); - itemSlotState.UpdateEquipment(EquipmentIds); - itemSlotState.UpdateCostumes(CostumeIds); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - int previousHighScore = raiderState.HighScore; - WorldBossState bossState; - WorldBossGlobalHpSheet hpSheet = sheets.GetSheet(); - if (states.TryGetState(worldBossAddress, out List rawBossState)) - { - bossState = new WorldBossState(rawBossState); - } - else - { - bossState = new WorldBossState(row, hpSheet[1]); - } - - var addressesHex = $"[{context.Signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var items = EquipmentIds.Concat(CostumeIds); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var raidSimulatorSheets = sheets.GetRaidSimulatorSheets(); - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // Simulate. - var simulator = new RaidSimulatorV2( - row.BossId, - context.Random, - avatarState, - FoodIds, - runeStates, - raidSimulatorSheets, - sheets.GetSheet()); - simulator.Simulate(); - avatarState.inventory = simulator.Player.Inventory; - - var costumeList = new List(); - foreach (var guid in CostumeIds) - { - var costume = avatarState.inventory.Costumes.FirstOrDefault(x => x.ItemId == guid); - if (costume != null) - { - costumeList.Add(costume); - } - } - - var runeOptionSheet = sheets.GetSheet(); - var runeOptions = new List(); - foreach (var runeState in runeStates) - { - if (!runeOptionSheet.TryGetValue(runeState.RuneId, out var optionRow)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.RuneId); - } - - if (!optionRow.LevelOptionMap.TryGetValue(runeState.Level, out var option)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.Level); - } - - runeOptions.Add(option); - } - - var characterSheet = sheets.GetSheet(); - if (!characterSheet.TryGetValue(avatarState.characterId, out var characterRow)) - { - throw new SheetRowNotFoundException("CharacterSheet", avatarState.characterId); - } - - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.TotalCP( - equipmentList, costumeList, - runeOptions, avatarState.level, - characterRow, costumeStatSheet); - int score = simulator.DamageDealt; - raiderState.Update(avatarState, cp, score, PayNcg, context.BlockIndex); - - // Reward. - bossState.CurrentHp -= score; - if (bossState.CurrentHp <= 0) - { - if (bossState.Level < hpSheet.OrderedList.Last().Level) - { - bossState.Level++; - } - bossState.CurrentHp = hpSheet[bossState.Level].Hp; - } - - // battle reward - foreach (var battleReward in simulator.AssetReward) - { - if (battleReward.Currency.Equals(CrystalCalculator.CRYSTAL)) - { - states = states.MintAsset(context, context.Signer, battleReward); - } - else - { - states = states.MintAsset(context, AvatarAddress, battleReward); - } - } - - if (raiderState.LatestBossLevel < bossState.Level) - { - // kill reward - var worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(AvatarAddress, raidId); - WorldBossKillRewardRecord rewardRecord; - if (states.TryGetState(worldBossKillRewardRecordAddress, out List rawList)) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[row.BossId]; - rewardRecord = new WorldBossKillRewardRecord(rawList); - // calculate with previous high score. - int rank = WorldBossHelper.CalculateRank(bossRow, previousHighScore); - states = states.SetWorldBossKillReward( - context, - worldBossKillRewardRecordAddress, - rewardRecord, - rank, - bossState, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.Random, - AvatarAddress, - context.Signer - ); - } - else - { - rewardRecord = new WorldBossKillRewardRecord(); - } - - // Save level infos; - raiderState.LatestBossLevel = bossState.Level; - if (!rewardRecord.ContainsKey(raiderState.LatestBossLevel)) - { - rewardRecord.Add(raiderState.LatestBossLevel, false); - } - states = states.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInfoAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInfoAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid Total Executed Time: {Elapsed}", addressHex, ended - started); - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldBossAddress, bossState.Serialize()) - .SetState(raiderAddress, raiderState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["e"] = new List(EquipmentIds.Select(e => e.Serialize())), - ["c"] = new List(CostumeIds.Select(c => c.Serialize())), - ["f"] = new List(FoodIds.Select(f => f.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["p"] = PayNcg.Serialize(), - } - .ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - EquipmentIds = plainValue["e"].ToList(StateExtensions.ToGuid); - CostumeIds = plainValue["c"].ToList(StateExtensions.ToGuid); - FoodIds = plainValue["f"].ToList(StateExtensions.ToGuid); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - PayNcg = plainValue["p"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/Raid5.cs b/Lib9c/Action/Raid5.cs deleted file mode 100644 index b7d06c36cb..0000000000 --- a/Lib9c/Action/Raid5.cs +++ /dev/null @@ -1,373 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1858 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("raid5")] - public class Raid5 : GameAction, IRaidV2 - { - public Address AvatarAddress; - public List EquipmentIds; - public List CostumeIds; - public List FoodIds; - public List RuneInfos; - public bool PayNcg; - - Address IRaidV2.AvatarAddress => AvatarAddress; - IEnumerable IRaidV2.EquipmentIds => EquipmentIds; - IEnumerable IRaidV2.CostumeIds => CostumeIds; - IEnumerable IRaidV2.FoodIds => FoodIds; - IEnumerable IRaidV2.RuneSlotInfos => RuneInfos.Select(x => x.Serialize()); - bool IRaidV2.PayNcg => PayNcg; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IAccountStateDelta states = context.PreviousState; - if (context.Rehearsal) - { - return states; - } - - var addressHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid exec started", addressHex); - if (!states.TryGetAvatarStateV2(context.Signer, AvatarAddress, - out AvatarState avatarState, - out var migrationRequired)) - { - throw new FailedLoadStateException( - $"Aborted as the avatar state of the signer was failed to load."); - } - // Check stage level. - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInRaid)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out int current); - throw new NotEnoughClearedStageLevelException(AvatarAddress.ToHex(), - GameConfig.RequireClearedStageLevel.ActionsInRaid, current); - } - - Dictionary sheets = states.GetSheets( - containRaidSimulatorSheets: true, - sheetTypes: new [] { - typeof(MaterialItemSheet), - typeof(SkillSheet), - typeof(SkillBuffSheet), - typeof(StatBuffSheet), - typeof(CharacterLevelSheet), - typeof(EquipmentItemSetEffectSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(WorldBossCharacterSheet), - typeof(WorldBossListSheet), - typeof(WorldBossGlobalHpSheet), - typeof(WorldBossActionPatternSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(RuneWeightSheet), - typeof(WorldBossKillRewardSheet), - typeof(RuneSheet), - typeof(RuneListSheet), - }); - var worldBossListSheet = sheets.GetSheet(); - var row = worldBossListSheet.FindRowByBlockIndex(context.BlockIndex); - int raidId = row.Id; - Address worldBossAddress = Addresses.GetWorldBossAddress(raidId); - Address raiderAddress = Addresses.GetRaiderAddress(AvatarAddress, raidId); - // Check challenge count. - RaiderState raiderState; - if (states.TryGetState(raiderAddress, out List rawState)) - { - raiderState = new RaiderState(rawState); - } - else - { - raiderState = new RaiderState(); - if (row.EntranceFee > 0) - { - FungibleAssetValue crystalCost = CrystalCalculator.CalculateEntranceFee(avatarState.level, row.EntranceFee); - states = states.TransferAsset(context, context.Signer, worldBossAddress, crystalCost); - } - Address raiderListAddress = Addresses.GetRaiderListAddress(raidId); - List
raiderList = - states.TryGetState(raiderListAddress, out List rawRaiderList) - ? rawRaiderList.ToList(StateExtensions.ToAddress) - : new List
(); - raiderList.Add(raiderAddress); - states = states.SetState(raiderListAddress, - new List(raiderList.Select(a => a.Serialize()))); - } - - var gameConfigState = states.GetGameConfigState(); - if (context.BlockIndex - raiderState.UpdatedBlockIndex < gameConfigState.WorldBossRequiredInterval) - { - throw new RequiredBlockIntervalException($"wait for interval. {context.BlockIndex - raiderState.UpdatedBlockIndex}"); - } - - if (WorldBossHelper.CanRefillTicket(context.BlockIndex, raiderState.RefillBlockIndex, - row.StartedBlockIndex, gameConfigState.DailyWorldBossInterval)) - { - raiderState.RemainChallengeCount = WorldBossHelper.MaxChallengeCount; - raiderState.RefillBlockIndex = context.BlockIndex; - } - - if (raiderState.RemainChallengeCount < 1) - { - if (PayNcg) - { - if (raiderState.PurchaseCount >= row.MaxPurchaseCount) - { - throw new ExceedTicketPurchaseLimitException(""); - } - var goldCurrency = states.GetGoldCurrency(); - states = states.TransferAsset(context, context.Signer, worldBossAddress, - WorldBossHelper.CalculateTicketPrice(row, raiderState, goldCurrency)); - raiderState.PurchaseCount++; - } - else - { - throw new ExceedPlayCountException(""); - } - } - - // Validate equipment, costume. - var equipmentList = avatarState.ValidateEquipmentsV2(EquipmentIds, context.BlockIndex); - var foodIds = avatarState.ValidateConsumable(FoodIds, context.BlockIndex); - var costumeIds = avatarState.ValidateCostume(CostumeIds); - - // Update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(AvatarAddress, BattleType.Raid); - var runeSlotState = states.TryGetState(runeSlotStateAddress, out List rawRuneSlotState) - ? new RuneSlotState(rawRuneSlotState) - : new RuneSlotState(BattleType.Raid); - var runeListSheet = sheets.GetSheet(); - runeSlotState.UpdateSlot(RuneInfos, runeListSheet); - states = states.SetState(runeSlotStateAddress, runeSlotState.Serialize()); - - // Update item slot - var itemSlotStateAddress = ItemSlotState.DeriveAddress(AvatarAddress, BattleType.Raid); - var itemSlotState = states.TryGetState(itemSlotStateAddress, out List rawItemSlotState) - ? new ItemSlotState(rawItemSlotState) - : new ItemSlotState(BattleType.Raid); - itemSlotState.UpdateEquipment(EquipmentIds); - itemSlotState.UpdateCostumes(CostumeIds); - states = states.SetState(itemSlotStateAddress, itemSlotState.Serialize()); - - int previousHighScore = raiderState.HighScore; - WorldBossState bossState; - WorldBossGlobalHpSheet hpSheet = sheets.GetSheet(); - if (states.TryGetState(worldBossAddress, out List rawBossState)) - { - bossState = new WorldBossState(rawBossState); - } - else - { - bossState = new WorldBossState(row, hpSheet[1]); - } - - var addressesHex = $"[{context.Signer.ToHex()}, {AvatarAddress.ToHex()}]"; - var items = EquipmentIds.Concat(CostumeIds); - avatarState.EquipItems(items); - avatarState.ValidateItemRequirement( - costumeIds.Concat(foodIds).ToList(), - equipmentList, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - addressesHex); - - var raidSimulatorSheets = sheets.GetRaidSimulatorSheets(); - var runeStates = new List(); - foreach (var address in RuneInfos.Select(info => RuneState.DeriveAddress(AvatarAddress, info.RuneId))) - { - if (states.TryGetState(address, out List rawRuneState)) - { - runeStates.Add(new RuneState(rawRuneState)); - } - } - - // Simulate. - var simulator = new RaidSimulatorV2( - row.BossId, - context.Random, - avatarState, - FoodIds, - runeStates, - raidSimulatorSheets, - sheets.GetSheet()); - simulator.Simulate(); - avatarState.inventory = simulator.Player.Inventory; - - var costumeList = new List(); - foreach (var guid in CostumeIds) - { - var costume = avatarState.inventory.Costumes.FirstOrDefault(x => x.ItemId == guid); - if (costume != null) - { - costumeList.Add(costume); - } - } - - var runeOptionSheet = sheets.GetSheet(); - var runeOptions = new List(); - foreach (var runeState in runeStates) - { - if (!runeOptionSheet.TryGetValue(runeState.RuneId, out var optionRow)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.RuneId); - } - - if (!optionRow.LevelOptionMap.TryGetValue(runeState.Level, out var option)) - { - throw new SheetRowNotFoundException("RuneOptionSheet", runeState.Level); - } - - runeOptions.Add(option); - } - - var characterSheet = sheets.GetSheet(); - if (!characterSheet.TryGetValue(avatarState.characterId, out var characterRow)) - { - throw new SheetRowNotFoundException("CharacterSheet", avatarState.characterId); - } - - var costumeStatSheet = sheets.GetSheet(); - var cp = CPHelper.TotalCP( - equipmentList, costumeList, - runeOptions, avatarState.level, - characterRow, costumeStatSheet); - int score = simulator.DamageDealt; - raiderState.Update(avatarState, cp, score, PayNcg, context.BlockIndex); - - // Reward. - bossState.CurrentHp -= score; - if (bossState.CurrentHp <= 0) - { - if (bossState.Level < hpSheet.OrderedList.Last().Level) - { - bossState.Level++; - } - bossState.CurrentHp = hpSheet[bossState.Level].Hp; - } - - // battle reward - foreach (var battleReward in simulator.AssetReward) - { - if (battleReward.Currency.Equals(CrystalCalculator.CRYSTAL)) - { - states = states.MintAsset(context, context.Signer, battleReward); - } - else - { - states = states.MintAsset(context, AvatarAddress, battleReward); - } - } - - if (raiderState.LatestBossLevel < bossState.Level) - { - // kill reward - var worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(AvatarAddress, raidId); - WorldBossKillRewardRecord rewardRecord; - if (states.TryGetState(worldBossKillRewardRecordAddress, out List rawList)) - { - var bossRow = raidSimulatorSheets.WorldBossCharacterSheet[row.BossId]; - rewardRecord = new WorldBossKillRewardRecord(rawList); - // calculate with previous high score. - int rank = WorldBossHelper.CalculateRank(bossRow, previousHighScore); - states = states.SetWorldBossKillReward( - context, - worldBossKillRewardRecordAddress, - rewardRecord, - rank, - bossState, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.Random, - AvatarAddress, - context.Signer - ); - } - else - { - rewardRecord = new WorldBossKillRewardRecord(); - } - - // Save level infos; - raiderState.LatestBossLevel = bossState.Level; - if (!rewardRecord.ContainsKey(raiderState.LatestBossLevel)) - { - rewardRecord.Add(raiderState.LatestBossLevel, false); - } - states = states.SetState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); - } - - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInfoAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - - if (migrationRequired) - { - states = states - .SetState(AvatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInfoAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressHex}Raid Total Executed Time: {Elapsed}", addressHex, ended - started); - return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldBossAddress, bossState.Serialize()) - .SetState(raiderAddress, raiderState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["e"] = new List(EquipmentIds.Select(e => e.Serialize())), - ["c"] = new List(CostumeIds.Select(c => c.Serialize())), - ["f"] = new List(FoodIds.Select(f => f.Serialize())), - ["r"] = RuneInfos.OrderBy(x => x.SlotIndex).Select(x=> x.Serialize()).Serialize(), - ["p"] = PayNcg.Serialize(), - } - .ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - EquipmentIds = plainValue["e"].ToList(StateExtensions.ToGuid); - CostumeIds = plainValue["c"].ToList(StateExtensions.ToGuid); - FoodIds = plainValue["f"].ToList(StateExtensions.ToGuid); - RuneInfos = plainValue["r"].ToList(x => new RuneSlotInfo((List)x)); - PayNcg = plainValue["p"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/RankingBattle0.cs b/Lib9c/Action/RankingBattle0.cs deleted file mode 100644 index cbacc7b544..0000000000 --- a/Lib9c/Action/RankingBattle0.cs +++ /dev/null @@ -1,217 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle")] - public class RankingBattle0 : GameAction, IRankingBattleV1 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address AvatarAddress; - public Address EnemyAddress; - public Address WeeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV1.AvatarAddress => AvatarAddress; - Address IRankingBattleV1.EnemyAddress => EnemyAddress; - Address IRankingBattleV1.WeeklyArenaAddress => WeeklyArenaAddress; - IEnumerable IRankingBattleV1.CostumeIds => costumeIds; - IEnumerable IRankingBattleV1.EquipmentIds => equipmentIds; - IEnumerable IRankingBattleV1.ConsumableIds => consumableIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - return states.SetState(ctx.Signer, MarkChanged) - .SetState(AvatarAddress, MarkChanged) - .SetState(WeeklyArenaAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, WeeklyArenaAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress, EnemyAddress); - - Log.Warning("ranking_battle is deprecated. Please use ranking_battle2"); - if (AvatarAddress.Equals(EnemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAgentAvatarStates( - ctx.Signer, - AvatarAddress, - out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var costumes = new HashSet(costumeIds); - - avatarState.ValidateEquipments(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumes); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - avatarState.EquipCostumes(costumes); - avatarState.EquipEquipments(equipmentIds); - - var enemyAvatarState = states.GetAvatarState(EnemyAddress); - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({EnemyAddress}) was failed to load."); - } - - var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException( - addressesHex + WeeklyArenaStateAlreadyEndedException.BaseMessage); - } - - if (!weeklyArenaState.ContainsKey(AvatarAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, AvatarAddress); - } - - var arenaInfo = weeklyArenaState[AvatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - FungibleAssetValue agentBalance = default; - try - { - agentBalance = states.GetBalance(ctx.Signer, states.GetGoldCurrency()); - } - catch (InvalidOperationException) - { - throw new NotEnoughFungibleAssetValueException(addressesHex, EntranceFee, agentBalance); - } - - if (agentBalance >= new FungibleAssetValue(agentBalance.Currency, EntranceFee, 0)) - { - states = states.TransferAsset( - ctx, - ctx.Signer, - WeeklyArenaAddress, - new FungibleAssetValue( - states.GetGoldCurrency(), - EntranceFee, - 0 - ) - ); - arenaInfo.Activate(); - } - else - { - throw new NotEnoughFungibleAssetValueException(addressesHex, EntranceFee, agentBalance); - } - } - - if (!weeklyArenaState.ContainsKey(EnemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, EnemyAddress); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - weeklyArenaState[EnemyAddress]); - - simulator.SimulateV1(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - avatarState.inventory.AddItem2(itemBase); - } - - return states - .SetState(ctx.Signer, agentState.Serialize()) - .SetState(WeeklyArenaAddress, weeklyArenaState.Serialize()) - .SetState(AvatarAddress, avatarState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["enemyAddress"] = EnemyAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["costume_ids"] = new Bencodex.Types.List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new Bencodex.Types.List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new Bencodex.Types.List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - EnemyAddress = plainValue["enemyAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((Bencodex.Types.List) plainValue["costume_ids"]) - .Select(e => e.ToInteger()) - .ToList(); - equipmentIds = ((Bencodex.Types.List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((Bencodex.Types.List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle10.cs b/Lib9c/Action/RankingBattle10.cs deleted file mode 100644 index 0446370e1e..0000000000 --- a/Lib9c/Action/RankingBattle10.cs +++ /dev/null @@ -1,342 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model; -using Nekoyume.Extensions; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle10")] - public class RankingBattle10 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address avatarAddress; - public Address enemyAddress; - public Address weeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public EnemyPlayerDigest EnemyPlayerDigest; - public ArenaInfo ArenaInfo; - public ArenaInfo EnemyArenaInfo; - - Address IRankingBattleV2.AvatarAddress => avatarAddress; - Address IRankingBattleV2.EnemyAddress => enemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => weeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(weeklyArenaAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100190ObsoleteIndex, ctx); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress, enemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (avatarAddress.Equals(enemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out var avatarState, out bool migrationRequired)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var sheets = states.GetSheetsV100291( - containRankingSimulatorSheets:true, - sheetTypes: new[] - { - typeof(CharacterSheet), - typeof(CostumeStatSheet), - }); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get Sheets: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - AvatarState enemyAvatarState; - try - { - enemyAvatarState = states.GetAvatarStateV2(enemyAddress); - } - // BackWard compatible. - catch (FailedLoadStateException) - { - enemyAvatarState = states.GetAvatarState(enemyAddress); - } - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({enemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetState(weeklyArenaAddress, out Dictionary rawWeeklyArenaState)) - { - return states; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, weeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - bool arenaEnded = rawWeeklyArenaState["ended"].ToBoolean(); - Dictionary weeklyArenaMap = (Dictionary) rawWeeklyArenaState["map"]; - if (arenaEnded) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = sheets.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - IKey arenaKey = (IKey) avatarAddress.Serialize(); - if (!weeklyArenaMap.ContainsKey(arenaKey)) - { - var characterSheet = sheets.GetSheet(); - var newInfo = new ArenaInfo(avatarState, characterSheet, costumeStatSheet, false); - weeklyArenaMap = - (Dictionary) weeklyArenaMap.Add(arenaKey, newInfo.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = new ArenaInfo((Dictionary) weeklyArenaMap[arenaKey]); - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - IKey enemyKey = (IKey) enemyAddress.Serialize(); - if (!weeklyArenaMap.ContainsKey(enemyKey)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, enemyAddress); - } - - var enemyArenaInfo = new ArenaInfo((Dictionary) weeklyArenaMap[enemyKey]); - if (!enemyArenaInfo.Active) - { - enemyArenaInfo.Activate(); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaAddress.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - ArenaInfo = new ArenaInfo((Dictionary)weeklyArenaMap[arenaKey]); - EnemyArenaInfo = new ArenaInfo((Dictionary)weeklyArenaMap[enemyKey]); - var rankingSheets = sheets.GetRankingSimulatorSheetsV100291(); - var player = new Player(avatarState, rankingSheets); - var enemyPlayerDigest = new EnemyPlayerDigest(enemyAvatarState); - var simulator = new RankingSimulatorV1( - ctx.Random, - player, - enemyPlayerDigest, - new List(), - rankingSheets, - StageId, - arenaInfo, - enemyArenaInfo, - costumeStatSheet); - - simulator.Simulate(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - avatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem(itemBase); - } - - var arenaMapDict = new Dictionary(); - foreach (var kv in weeklyArenaMap) - { - var key = kv.Key; - var value = kv.Value; - if (key.Equals(arenaKey)) - { - value = arenaInfo.Serialize(); - } - - if (key.Equals(enemyKey)) - { - value = enemyArenaInfo.Serialize(); - } - - arenaMapDict[key] = value; - } - - var weeklyArenaDict = new Dictionary(); - foreach (var kv in rawWeeklyArenaState) - { - weeklyArenaDict[kv.Key] = kv.Key.Equals((Text)"map") - ? new Dictionary(arenaMapDict) - : kv.Value; - } - - states = states.SetState(weeklyArenaAddress, new Dictionary(weeklyArenaDict)); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()); - - if (migrationRequired) - { - states = states - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - EnemyPlayerDigest = enemyPlayerDigest; - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["enemyAddress"] = enemyAddress.Serialize(), - ["weeklyArenaAddress"] = weeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - enemyAddress = plainValue["enemyAddress"].ToAddress(); - weeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle2.cs b/Lib9c/Action/RankingBattle2.cs deleted file mode 100644 index 9ff7f646ab..0000000000 --- a/Lib9c/Action/RankingBattle2.cs +++ /dev/null @@ -1,257 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle2")] - public class RankingBattle2 : GameAction, IRankingBattleV1 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address AvatarAddress; - public Address EnemyAddress; - public Address WeeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV1.AvatarAddress => AvatarAddress; - Address IRankingBattleV1.EnemyAddress => EnemyAddress; - Address IRankingBattleV1.WeeklyArenaAddress => WeeklyArenaAddress; - IEnumerable IRankingBattleV1.CostumeIds => costumeIds; - IEnumerable IRankingBattleV1.EquipmentIds => equipmentIds; - IEnumerable IRankingBattleV1.ConsumableIds => consumableIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - return states.SetState(ctx.Signer, MarkChanged) - .SetState(AvatarAddress, MarkChanged) - .SetState(WeeklyArenaAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, WeeklyArenaAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress, EnemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (AvatarAddress.Equals(EnemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarState(ctx.Signer, AvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.ValidateEquipments(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - avatarState.EquipCostumes(new HashSet(costumeIds)); - avatarState.EquipEquipments(equipmentIds); - avatarState.ValidateCostume(new HashSet(costumeIds)); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var enemyAvatarState = states.GetAvatarState(EnemyAddress); - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({EnemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, WeeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException( - addressesHex + WeeklyArenaStateAlreadyEndedException.BaseMessage); - } - - if (!weeklyArenaState.ContainsKey(AvatarAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, AvatarAddress); - } - - var arenaInfo = weeklyArenaState[AvatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(EnemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, EnemyAddress); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - weeklyArenaState[EnemyAddress], - costumeStatSheet); - - simulator.SimulateV1(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - AvatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem2(itemBase); - } - - states = states.SetState(WeeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(AvatarAddress, avatarState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["enemyAddress"] = EnemyAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - EnemyAddress = plainValue["enemyAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToInteger()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle3.cs b/Lib9c/Action/RankingBattle3.cs deleted file mode 100644 index a696b754d0..0000000000 --- a/Lib9c/Action/RankingBattle3.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle3")] - public class RankingBattle3 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address AvatarAddress; - public Address EnemyAddress; - public Address WeeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV2.AvatarAddress => AvatarAddress; - Address IRankingBattleV2.EnemyAddress => EnemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => WeeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - IEnumerable IRankingBattleV2.ConsumableIds => consumableIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - return states.SetState(ctx.Signer, MarkChanged) - .SetState(AvatarAddress, MarkChanged) - .SetState(WeeklyArenaAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, WeeklyArenaAddress); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress, EnemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (AvatarAddress.Equals(EnemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarState(ctx.Signer, AvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var enemyAvatarState = states.GetAvatarState(EnemyAddress); - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({EnemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, WeeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException( - addressesHex + WeeklyArenaStateAlreadyEndedException.BaseMessage); - } - - if (!weeklyArenaState.ContainsKey(AvatarAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, AvatarAddress); - } - - var arenaInfo = weeklyArenaState[AvatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(EnemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, EnemyAddress); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - weeklyArenaState[EnemyAddress], - costumeStatSheet); - - simulator.SimulateV2(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - AvatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem2(itemBase); - } - - states = states.SetState(WeeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(AvatarAddress, avatarState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["enemyAddress"] = EnemyAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - EnemyAddress = plainValue["enemyAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle4.cs b/Lib9c/Action/RankingBattle4.cs deleted file mode 100644 index 7b262c8c07..0000000000 --- a/Lib9c/Action/RankingBattle4.cs +++ /dev/null @@ -1,269 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle4")] - public class RankingBattle4 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address AvatarAddress; - public Address EnemyAddress; - public Address WeeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV2.AvatarAddress => AvatarAddress; - Address IRankingBattleV2.EnemyAddress => EnemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => WeeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - IEnumerable IRankingBattleV2.ConsumableIds => consumableIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - return states.SetState(ctx.Signer, MarkChanged) - .SetState(AvatarAddress, MarkChanged) - .SetState(WeeklyArenaAddress, MarkChanged) - .SetState(ctx.Signer, MarkChanged) - .MarkBalanceChanged(ctx, GoldCurrencyMock, ctx.Signer, WeeklyArenaAddress); - } - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress, EnemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (AvatarAddress.Equals(EnemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarState(ctx.Signer, AvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - var enemyAvatarState = states.GetAvatarState(EnemyAddress); - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({EnemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, WeeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!weeklyArenaState.ContainsKey(AvatarAddress)) - { - var characterSheet = states.GetSheet(); - weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = weeklyArenaState[AvatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(EnemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, EnemyAddress); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - weeklyArenaState[EnemyAddress], - costumeStatSheet); - - simulator.SimulateV2(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - AvatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem2(itemBase); - } - - states = states.SetState(WeeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(AvatarAddress, avatarState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["enemyAddress"] = EnemyAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - EnemyAddress = plainValue["enemyAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle5.cs b/Lib9c/Action/RankingBattle5.cs deleted file mode 100644 index 6aa29055dc..0000000000 --- a/Lib9c/Action/RankingBattle5.cs +++ /dev/null @@ -1,287 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle5")] - public class RankingBattle5 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address AvatarAddress; - public Address EnemyAddress; - public Address WeeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV2.AvatarAddress => AvatarAddress; - Address IRankingBattleV2.EnemyAddress => EnemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => WeeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - IEnumerable IRankingBattleV2.ConsumableIds => consumableIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(AvatarAddress, MarkChanged) - .SetState(WeeklyArenaAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress, EnemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (AvatarAddress.Equals(EnemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(ctx.Signer, AvatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - AvatarState enemyAvatarState; - try - { - enemyAvatarState = states.GetAvatarStateV2(EnemyAddress); - } - // BackWard compatible. - catch (FailedLoadStateException) - { - enemyAvatarState = states.GetAvatarState(EnemyAddress); - } - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({EnemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(WeeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, WeeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!weeklyArenaState.ContainsKey(AvatarAddress)) - { - var characterSheet = states.GetSheet(); - weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = weeklyArenaState[AvatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(EnemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, EnemyAddress); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - weeklyArenaState[EnemyAddress], - costumeStatSheet); - - simulator.SimulateV2(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - AvatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem2(itemBase); - } - - states = states.SetState(WeeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(AvatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = AvatarAddress.Serialize(), - ["enemyAddress"] = EnemyAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["avatarAddress"].ToAddress(); - EnemyAddress = plainValue["enemyAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle6.cs b/Lib9c/Action/RankingBattle6.cs deleted file mode 100644 index f788953732..0000000000 --- a/Lib9c/Action/RankingBattle6.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle6")] - public class RankingBattle6 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address avatarAddress; - public Address enemyAddress; - public Address weeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV2.AvatarAddress => avatarAddress; - Address IRankingBattleV2.EnemyAddress => enemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => weeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(weeklyArenaAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, ctx); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress, enemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (avatarAddress.Equals(enemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - AvatarState enemyAvatarState; - try - { - enemyAvatarState = states.GetAvatarStateV2(enemyAddress); - } - // BackWard compatible. - catch (FailedLoadStateException) - { - enemyAvatarState = states.GetAvatarState(enemyAddress); - } - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({enemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(weeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, weeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!weeklyArenaState.ContainsKey(avatarAddress)) - { - var characterSheet = states.GetSheet(); - weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = weeklyArenaState[avatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(enemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, enemyAddress); - } - - var enemyArenaInfo = weeklyArenaState[enemyAddress]; - if (!enemyArenaInfo.Active) - { - enemyArenaInfo.Activate(); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - enemyArenaInfo, - costumeStatSheet); - - simulator.SimulateV2(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - avatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem(itemBase); - } - - states = states.SetState(weeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["enemyAddress"] = enemyAddress.Serialize(), - ["weeklyArenaAddress"] = weeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - enemyAddress = plainValue["enemyAddress"].ToAddress(); - weeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle7.cs b/Lib9c/Action/RankingBattle7.cs deleted file mode 100644 index 70e6e150a3..0000000000 --- a/Lib9c/Action/RankingBattle7.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle7")] - public class RankingBattle7 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address avatarAddress; - public Address enemyAddress; - public Address weeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - - Address IRankingBattleV2.AvatarAddress => avatarAddress; - Address IRankingBattleV2.EnemyAddress => enemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => weeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(weeklyArenaAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100086ObsoleteIndex, context); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress, enemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (avatarAddress.Equals(enemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - AvatarState enemyAvatarState; - try - { - enemyAvatarState = states.GetAvatarStateV2(enemyAddress); - } - // BackWard compatible. - catch (FailedLoadStateException) - { - enemyAvatarState = states.GetAvatarState(enemyAddress); - } - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({enemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(weeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, weeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!weeklyArenaState.ContainsKey(avatarAddress)) - { - var characterSheet = states.GetSheet(); - weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = weeklyArenaState[avatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(enemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, enemyAddress); - } - - var enemyArenaInfo = weeklyArenaState[enemyAddress]; - if (!enemyArenaInfo.Active) - { - enemyArenaInfo.Activate(); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - enemyArenaInfo, - costumeStatSheet); - - simulator.SimulateV3(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - avatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem(itemBase); - } - - states = states.SetState(weeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["enemyAddress"] = enemyAddress.Serialize(), - ["weeklyArenaAddress"] = weeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - enemyAddress = plainValue["enemyAddress"].ToAddress(); - weeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle8.cs b/Lib9c/Action/RankingBattle8.cs deleted file mode 100644 index 0eaa5a6c3f..0000000000 --- a/Lib9c/Action/RankingBattle8.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("ranking_battle8")] - public class RankingBattle8 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address avatarAddress; - public Address enemyAddress; - public Address weeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - public AvatarState EnemyAvatarState; - public ArenaInfo ArenaInfo; - public ArenaInfo EnemyArenaInfo; - - Address IRankingBattleV2.AvatarAddress => avatarAddress; - Address IRankingBattleV2.EnemyAddress => enemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => weeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(weeklyArenaAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100089ObsoleteIndex, context); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress, enemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (avatarAddress.Equals(enemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - AvatarState enemyAvatarState; - try - { - enemyAvatarState = states.GetAvatarStateV2(enemyAddress); - } - // BackWard compatible. - catch (FailedLoadStateException) - { - enemyAvatarState = states.GetAvatarState(enemyAddress); - } - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({enemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(weeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, weeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!weeklyArenaState.ContainsKey(avatarAddress)) - { - var characterSheet = states.GetSheet(); - weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = weeklyArenaState[avatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(enemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, enemyAddress); - } - - var enemyArenaInfo = weeklyArenaState[enemyAddress]; - if (!enemyArenaInfo.Active) - { - enemyArenaInfo.Activate(); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - ArenaInfo = new ArenaInfo((Dictionary)weeklyArenaState[avatarAddress].Serialize()); - EnemyArenaInfo = new ArenaInfo((Dictionary)weeklyArenaState[enemyAddress].Serialize()); - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - enemyArenaInfo, - costumeStatSheet); - - simulator.SimulateV4(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - avatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem(itemBase); - } - - states = states.SetState(weeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - EnemyAvatarState = enemyAvatarState; - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["enemyAddress"] = enemyAddress.Serialize(), - ["weeklyArenaAddress"] = weeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - enemyAddress = plainValue["enemyAddress"].ToAddress(); - weeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RankingBattle9.cs b/Lib9c/Action/RankingBattle9.cs deleted file mode 100644 index 49c905e9e4..0000000000 --- a/Lib9c/Action/RankingBattle9.cs +++ /dev/null @@ -1,298 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Action; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionType("ranking_battle9")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class RankingBattle9 : GameAction, IRankingBattleV2 - { - public const int StageId = 999999; - public static readonly BigInteger EntranceFee = 100; - - public Address avatarAddress; - public Address enemyAddress; - public Address weeklyArenaAddress; - public List costumeIds; - public List equipmentIds; - public List consumableIds; - public BattleLog Result { get; private set; } - public AvatarState EnemyAvatarState; - public ArenaInfo ArenaInfo; - public ArenaInfo EnemyArenaInfo; - - Address IRankingBattleV2.AvatarAddress => avatarAddress; - Address IRankingBattleV2.EnemyAddress => enemyAddress; - Address IRankingBattleV2.WeeklyArenaAddress => weeklyArenaAddress; - IEnumerable IRankingBattleV2.CostumeIds => costumeIds; - IEnumerable IRankingBattleV2.EquipmentIds => equipmentIds; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (ctx.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(weeklyArenaAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100093ObsoleteIndex, context); - - // Avoid InvalidBlockStateRootHashException - if (ctx.BlockIndex == 680341 && Id.Equals(new Guid("df37dbd8-5703-4dff-918b-ad22ee4c34c6"))) - { - return states; - } - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress, enemyAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose( - "{AddressesHex}RankingBattle exec started. costume: ({CostumeIds}), equipment: ({EquipmentIds})", - addressesHex, - string.Join(",", costumeIds), - string.Join(",", equipmentIds) - ); - - if (avatarAddress.Equals(enemyAddress)) - { - throw new InvalidAddressException($"{addressesHex}Aborted as the signer tried to battle for themselves."); - } - - if (!states.TryGetAvatarStateV2(ctx.Signer, avatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var items = equipmentIds.Concat(costumeIds); - - avatarState.ValidateEquipmentsV2(equipmentIds, context.BlockIndex); - avatarState.ValidateConsumable(consumableIds, context.BlockIndex); - avatarState.ValidateCostume(costumeIds); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.EquipItems(items); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Equip Equipments: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex(out var world) || - world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInRankingBoard) - { - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard, - world.StageClearedId); - } - - AvatarState enemyAvatarState; - try - { - enemyAvatarState = states.GetAvatarStateV2(enemyAddress); - } - // BackWard compatible. - catch (FailedLoadStateException) - { - enemyAvatarState = states.GetAvatarState(enemyAddress); - } - if (enemyAvatarState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the opponent ({enemyAddress}) was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get Enemy AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var weeklyArenaState = states.GetWeeklyArenaState(weeklyArenaAddress); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get WeeklyArenaState ({Address}): {Elapsed}", addressesHex, weeklyArenaAddress, sw.Elapsed); - sw.Restart(); - - if (weeklyArenaState.Ended) - { - throw new WeeklyArenaStateAlreadyEndedException(); - } - - var costumeStatSheet = states.GetSheet(); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Get CostumeStatSheet: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!weeklyArenaState.ContainsKey(avatarAddress)) - { - var characterSheet = states.GetSheet(); - weeklyArenaState.SetV2(avatarState, characterSheet, costumeStatSheet); - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Set AvatarInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - } - - var arenaInfo = weeklyArenaState[avatarAddress]; - - if (arenaInfo.DailyChallengeCount <= 0) - { - throw new NotEnoughWeeklyArenaChallengeCountException( - addressesHex + NotEnoughWeeklyArenaChallengeCountException.BaseMessage); - } - - if (!arenaInfo.Active) - { - arenaInfo.Activate(); - } - - if (!weeklyArenaState.ContainsKey(enemyAddress)) - { - throw new WeeklyArenaStateNotContainsAvatarAddressException(addressesHex, enemyAddress); - } - - var enemyArenaInfo = weeklyArenaState[enemyAddress]; - if (!enemyArenaInfo.Active) - { - enemyArenaInfo.Activate(); - } - - Log.Verbose("{WeeklyArenaStateAddress}", weeklyArenaState.address.ToHex()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Validate ArenaInfo: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - ArenaInfo = new ArenaInfo((Dictionary)weeklyArenaState[avatarAddress].Serialize()); - EnemyArenaInfo = new ArenaInfo((Dictionary)weeklyArenaState[enemyAddress].Serialize()); - var simulator = new RankingSimulatorV1( - ctx.Random, - avatarState, - enemyAvatarState, - consumableIds, - states.GetRankingSimulatorSheetsV1(), - StageId, - arenaInfo, - enemyArenaInfo, - costumeStatSheet); - - simulator.Simulate(); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}RankingBattle Simulate() with equipment:({Equipment}), costume:({Costume}): {Elapsed}", - addressesHex, - string.Join(",", simulator.Player.Equipments.Select(r => r.ItemId)), - string.Join(",", simulator.Player.Costumes.Select(r => r.ItemId)), - sw.Elapsed - ); - - Log.Verbose( - "{AddressesHex}Execute RankingBattle({AvatarAddress}); result: {Result} event count: {EventCount}", - addressesHex, - avatarAddress, - simulator.Log.result, - simulator.Log.Count - ); - sw.Restart(); - - Result = simulator.Log; - - foreach (var itemBase in simulator.Reward.OrderBy(i => i.Id)) - { - Log.Verbose( - "{AddressesHex}RankingBattle Add Reward Item({ItemBaseId}): {Elapsed}", - addressesHex, - itemBase.Id, - sw.Elapsed); - avatarState.inventory.AddItem(itemBase); - } - - states = states.SetState(weeklyArenaAddress, weeklyArenaState.Serialize()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - - sw.Stop(); - Log.Verbose("{AddressesHex}RankingBattle Serialize AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}RankingBattle Total Executed Time: {Elapsed}", addressesHex, ended - started); - EnemyAvatarState = enemyAvatarState; - return states; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["enemyAddress"] = enemyAddress.Serialize(), - ["weeklyArenaAddress"] = weeklyArenaAddress.Serialize(), - ["costume_ids"] = new List(costumeIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["equipment_ids"] = new List(equipmentIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - ["consumable_ids"] = new List(consumableIds - .OrderBy(element => element) - .Select(e => e.Serialize())), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - enemyAddress = plainValue["enemyAddress"].ToAddress(); - weeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - costumeIds = ((List) plainValue["costume_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - equipmentIds = ((List) plainValue["equipment_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - consumableIds = ((List) plainValue["consumable_ids"]) - .Select(e => e.ToGuid()) - .ToList(); - } - } -} diff --git a/Lib9c/Action/RapidCombination2.cs b/Lib9c/Action/RapidCombination2.cs deleted file mode 100644 index 430edaad60..0000000000 --- a/Lib9c/Action/RapidCombination2.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("rapid_combination2")] - public class RapidCombination2 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - Address IRapidCombinationV1.AvatarAddress => avatarAddress; - int IRapidCombinationV1.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStates( - context.Signer, - avatarAddress, - out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState?.Result is null) - { - throw new CombinationSlotResultNullException($"{addressesHex}CombinationSlot Result is null. ({avatarAddress}), ({slotIndex})"); - } - - if(!avatarState.worldInformation.IsStageCleared(slotState.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, slotState.UnlockStage, current); - } - - var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; - if (diff <= 0) - { - throw new RequiredBlockIndexException($"{addressesHex}Already met the required block index. context block index: {context.BlockIndex}, required block index: {slotState.Result.itemUsable.RequiredBlockIndex}"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the GameConfigState was failed to load."); - } - - var count = RapidCombination0.CalculateHourglassCount(gameConfigState, diff); - var materialItemSheet = states.GetSheet(); - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - var hourGlass = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem2(hourGlass, count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {count})"); - } - - slotState.Update(context.BlockIndex, hourGlass, count); - avatarState.UpdateFromRapidCombination( - (CombinationConsumable5.ResultModel) slotState.Result, - context.BlockIndex - ); - return states - .SetState(avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/RapidCombination3.cs b/Lib9c/Action/RapidCombination3.cs deleted file mode 100644 index 0f7d0e4323..0000000000 --- a/Lib9c/Action/RapidCombination3.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("rapid_combination3")] - public class RapidCombination3 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - Address IRapidCombinationV1.AvatarAddress => avatarAddress; - int IRapidCombinationV1.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStates( - context.Signer, - avatarAddress, - out var agentState, - out var avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState?.Result is null) - { - throw new CombinationSlotResultNullException($"{addressesHex}CombinationSlot Result is null. ({avatarAddress}), ({slotIndex})"); - } - - if(!avatarState.worldInformation.IsStageCleared(slotState.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, slotState.UnlockStage, current); - } - - var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; - if (diff <= 0) - { - throw new RequiredBlockIndexException($"{addressesHex}Already met the required block index. context block index: {context.BlockIndex}, required block index: {slotState.Result.itemUsable.RequiredBlockIndex}"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the GameConfigState was failed to load."); - } - - var count = RapidCombination0.CalculateHourglassCount(gameConfigState, diff); - var materialItemSheet = states.GetSheet(); - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - var hourGlass = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem(hourGlass, context.BlockIndex, count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {count})"); - } - - slotState.Update(context.BlockIndex, hourGlass, count); - avatarState.UpdateFromRapidCombination( - (CombinationConsumable5.ResultModel) slotState.Result, - context.BlockIndex - ); - return states - .SetState(avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/RapidCombination4.cs b/Lib9c/Action/RapidCombination4.cs deleted file mode 100644 index de59e5d111..0000000000 --- a/Lib9c/Action/RapidCombination4.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("rapid_combination4")] - public class RapidCombination4 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - Address IRapidCombinationV1.AvatarAddress => avatarAddress; - int IRapidCombinationV1.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - avatarAddress, - out var agentState, - out var avatarState, - out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState?.Result is null) - { - throw new CombinationSlotResultNullException($"{addressesHex}CombinationSlot Result is null. ({avatarAddress}), ({slotIndex})"); - } - - if(!avatarState.worldInformation.IsStageCleared(slotState.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, slotState.UnlockStage, current); - } - - var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; - if (diff <= 0) - { - throw new RequiredBlockIndexException($"{addressesHex}Already met the required block index. context block index: {context.BlockIndex}, required block index: {slotState.Result.itemUsable.RequiredBlockIndex}"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the GameConfigState was failed to load."); - } - - var count = RapidCombination0.CalculateHourglassCount(gameConfigState, diff); - var materialItemSheet = states.GetSheet(); - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - var hourGlass = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem(hourGlass, context.BlockIndex, count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {count})"); - } - - slotState.Update(context.BlockIndex, hourGlass, count); - avatarState.UpdateFromRapidCombination( - (CombinationConsumable5.ResultModel) slotState.Result, - context.BlockIndex - ); - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/RapidCombination6.cs b/Lib9c/Action/RapidCombination6.cs deleted file mode 100644 index d0ad37a45e..0000000000 --- a/Lib9c/Action/RapidCombination6.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Extensions; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/682 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// Updated at https://github.com/planetarium/lib9c/pull/1194 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("rapid_combination6")] - public class RapidCombination6 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - Address IRapidCombinationV1.AvatarAddress => avatarAddress; - int IRapidCombinationV1.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - CheckObsolete(ActionObsoleteConfig.V100310ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - avatarAddress, - out var agentState, - out var avatarState, - out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - - // exception handling for v100240. - if (context.BlockIndex > 4377159 && context.BlockIndex < 4377430 && slotState.Result is ItemEnhancement9.ResultModel) - { - return states; - } - - if (slotState?.Result is null) - { - throw new CombinationSlotResultNullException($"{addressesHex}CombinationSlot Result is null. ({avatarAddress}), ({slotIndex})"); - } - - if(!avatarState.worldInformation.IsStageCleared(slotState.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, slotState.UnlockStage, current); - } - - var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; - if (diff <= 0) - { - throw new RequiredBlockIndexException($"{addressesHex}Already met the required block index. context block index: {context.BlockIndex}, required block index: {slotState.Result.itemUsable.RequiredBlockIndex}"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the GameConfigState was failed to load."); - } - - if (context.BlockIndex < slotState.StartBlockIndex + GameConfig.RequiredAppraiseBlock) - { - throw new AppraiseBlockNotReachedException( - $"{addressesHex}Aborted as Item appraisal block section. " + - $"context block index: {context.BlockIndex}, " + - $"actionable block index : {slotState.StartBlockIndex + GameConfig.RequiredAppraiseBlock}"); - } - - var count = RapidCombination0.CalculateHourglassCount(gameConfigState, diff); - var materialItemSheet = states.GetSheet(); - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - var hourGlass = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem(hourGlass, context.BlockIndex, count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {count})"); - } - - if (slotState.TryGetResultIdV1(out var resultId) && - avatarState.mailBox.All(mail => mail.id != resultId) && - slotState.TryGetMailV1( - context.BlockIndex, - context.BlockIndex, - out var combinationMail, - out var itemEnhanceMail)) - { - if (combinationMail != null) - { - avatarState.Update(combinationMail); - } - else if (itemEnhanceMail != null) - { - avatarState.Update(itemEnhanceMail); - } - } - else - { - // exception handling for v100240. - if (context.BlockIndex > 4133442 && context.BlockIndex < 4374195) - { - if (slotState.TryGetResultId(out var resultIdFix) && - avatarState.mailBox.All(mail => mail.id != resultIdFix) && - slotState.TryGetMail( - context.BlockIndex, - context.BlockIndex, - out var combinationMailFix, - out var itemEnhanceMailFix)) - { - if (combinationMailFix != null) - { - avatarState.Update(combinationMailFix); - } - else if (itemEnhanceMailFix != null) - { - avatarState.Update(itemEnhanceMailFix); - } - } - } - } - - slotState.UpdateV2(context.BlockIndex, hourGlass, count); - avatarState.UpdateFromRapidCombinationV2( - (RapidCombination5.ResultModel)slotState.Result, - context.BlockIndex); - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/RapidCombination7.cs b/Lib9c/Action/RapidCombination7.cs deleted file mode 100644 index 8082eb1539..0000000000 --- a/Lib9c/Action/RapidCombination7.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Extensions; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1194 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("rapid_combination7")] - public class RapidCombination7 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - Address IRapidCombinationV1.AvatarAddress => avatarAddress; - int IRapidCombinationV1.SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - CheckObsolete(ActionObsoleteConfig.V100310ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - avatarAddress, - out var agentState, - out var avatarState, - out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState?.Result is null) - { - throw new CombinationSlotResultNullException($"{addressesHex}CombinationSlot Result is null. ({avatarAddress}), ({slotIndex})"); - } - - if(!avatarState.worldInformation.IsStageCleared(slotState.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, slotState.UnlockStage, current); - } - - var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; - if (diff <= 0) - { - throw new RequiredBlockIndexException($"{addressesHex}Already met the required block index. context block index: {context.BlockIndex}, required block index: {slotState.Result.itemUsable.RequiredBlockIndex}"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the GameConfigState was failed to load."); - } - - if (context.BlockIndex < slotState.StartBlockIndex + GameConfig.RequiredAppraiseBlock) - { - throw new AppraiseBlockNotReachedException( - $"{addressesHex}Aborted as Item appraisal block section. " + - $"context block index: {context.BlockIndex}, " + - $"actionable block index : {slotState.StartBlockIndex + GameConfig.RequiredAppraiseBlock}"); - } - - var count = RapidCombination0.CalculateHourglassCount(gameConfigState, diff); - var materialItemSheet = states.GetSheet(); - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - var hourGlass = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem(hourGlass, context.BlockIndex, count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {count})"); - } - - if (slotState.TryGetResultId(out var resultId) && - avatarState.mailBox.All(mail => mail.id != resultId) && - slotState.TryGetMail( - context.BlockIndex, - context.BlockIndex, - out var combinationMail, - out var itemEnhanceMail)) - { - if (combinationMail != null) - { - avatarState.Update(combinationMail); - } - else if (itemEnhanceMail != null) - { - avatarState.Update(itemEnhanceMail); - } - } - - slotState.UpdateV2(context.BlockIndex, hourGlass, count); - avatarState.UpdateFromRapidCombinationV2( - (RapidCombination5.ResultModel)slotState.Result, - context.BlockIndex); - - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/RapidCombination8.cs b/Lib9c/Action/RapidCombination8.cs deleted file mode 100644 index 03d04e0e75..0000000000 --- a/Lib9c/Action/RapidCombination8.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Extensions; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1378 - /// - [Serializable] - [ActionType("rapid_combination8")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class RapidCombination8 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - public Address AvatarAddress => avatarAddress; - public int SlotIndex => slotIndex; - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - if (context.Rehearsal) - { - return states - .SetState(avatarAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(slotAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}RapidCombination exec started", addressesHex); - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - avatarAddress, - out var agentState, - out var avatarState, - out _)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); - if (slotState?.Result is null) - { - throw new CombinationSlotResultNullException($"{addressesHex}CombinationSlot Result is null. ({avatarAddress}), ({slotIndex})"); - } - - if(!avatarState.worldInformation.IsStageCleared(slotState.UnlockStage)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, slotState.UnlockStage, current); - } - - var diff = slotState.Result.itemUsable.RequiredBlockIndex - context.BlockIndex; - if (diff <= 0) - { - throw new RequiredBlockIndexException($"{addressesHex}Already met the required block index. context block index: {context.BlockIndex}, required block index: {slotState.Result.itemUsable.RequiredBlockIndex}"); - } - - var gameConfigState = states.GetGameConfigState(); - if (gameConfigState is null) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the GameConfigState was failed to load."); - } - - var actionableBlockIndex = slotState.StartBlockIndex + - states.GetGameConfigState().RequiredAppraiseBlock; - if (context.BlockIndex < actionableBlockIndex) - { - throw new AppraiseBlockNotReachedException( - $"{addressesHex}Aborted as Item appraisal block section. " + - $"context block index: {context.BlockIndex}, actionable block index : {actionableBlockIndex}"); - } - - var count = RapidCombination0.CalculateHourglassCount(gameConfigState, diff); - var materialItemSheet = states.GetSheet(); - var row = materialItemSheet.Values.First(r => r.ItemSubType == ItemSubType.Hourglass); - var hourGlass = ItemFactory.CreateMaterial(row); - if (!avatarState.inventory.RemoveFungibleItem(hourGlass, context.BlockIndex, count)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {count})"); - } - - if (slotState.TryGetResultId(out var resultId) && - avatarState.mailBox.All(mail => mail.id != resultId) && - slotState.TryGetMail( - context.BlockIndex, - context.BlockIndex, - out var combinationMail, - out var itemEnhanceMail)) - { - if (combinationMail != null) - { - avatarState.Update(combinationMail); - } - else if (itemEnhanceMail != null) - { - avatarState.Update(itemEnhanceMail); - } - } - - slotState.UpdateV2(context.BlockIndex, hourGlass, count); - avatarState.UpdateFromRapidCombinationV2( - (RapidCombination5.ResultModel)slotState.Result, - context.BlockIndex); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}RapidCombination Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["slotIndex"] = slotIndex.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - slotIndex = plainValue["slotIndex"].ToInteger(); - } - } -} diff --git a/Lib9c/Action/Sell.cs b/Lib9c/Action/Sell.cs deleted file mode 100644 index 65bca601e4..0000000000 --- a/Lib9c/Action/Sell.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1640 - /// - [Serializable] - [ActionType("sell12")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class Sell : GameAction, ISellV2 - { - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - public Guid orderId; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - Guid? ISellV2.OrderId => orderId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [OrderIdKey] = orderId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - orderId = plainValue[OrderIdKey].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - Address itemAddress = Addresses.GetItemAddress(tradableId); - Address orderAddress = Order.DeriveAddress(orderId); - Address orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(orderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - if (!(states.GetState(Addresses.Market) is null)) - { - throw new ActionObsoletedException("Sell action is obsoleted. please use SellProduct."); - } - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Sell exec started", addressesHex); - - var ncg = states.GetGoldCurrency(); - if (!price.Currency.Equals(ncg) || - !price.MinorUnit.IsZero || - price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Order order = OrderFactory.Create( - context.Signer, - sellerAvatarAddress, - orderId, - price, - tradableId, - context.BlockIndex, - itemSubType, - count); - order.Validate(avatarState, count); - - ITradableItem tradableItem = order.Sell(avatarState); - - var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(shopAddress); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - OrderDigest orderDigest = order.Digest(avatarState, costumeStatSheet); - shardedShopState.Add(orderDigest, context.BlockIndex); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var orderReceiptList = - states.TryGetState(orderReceiptAddress, out Dictionary receiptDict) - ? new OrderDigestListState(receiptDict) - : new OrderDigestListState(orderReceiptAddress); - - orderReceiptList.Add(orderDigest); - - states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize()); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(orderAddress, order.Serialize()) - .SetState(shopAddress, shardedShopState.Serialize()); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Debug( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell0.cs b/Lib9c/Action/Sell0.cs deleted file mode 100644 index 821fe2cc86..0000000000 --- a/Lib9c/Action/Sell0.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell")] - public class Sell0 : GameAction, ISellV1 - { - public Address sellerAvatarAddress; - public Guid itemId; - public FungibleAssetValue price; - - Address ISellV1.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV1.ItemId => itemId; - FungibleAssetValue ISellV1.Price => price; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["itemId"] = itemId.Serialize(), - ["price"] = price.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - itemId = plainValue["itemId"].ToGuid(); - price = plainValue["price"].ToFungibleAssetValue(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - states = states.SetState(sellerAvatarAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Sell exec started", addressesHex); - - - if (price.Sign < 0) - { - throw new InvalidPriceException($"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out AgentState agentState, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Debug("{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - Log.Debug("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - Log.Debug("{AddressesHex}Sell Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Debug("{AddressesHex}Execute Sell; seller: {SellerAvatarAddress}", addressesHex, sellerAvatarAddress); - - // 인벤토리에서 판매할 아이템을 선택하고 수량을 조절한다. - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable nonFungibleItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." - ); - } - - if (nonFungibleItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to sell ({itemId}) is not available yet; it will be available at the block #{nonFungibleItem.RequiredBlockIndex}." - ); - } - - avatarState.inventory.RemoveNonFungibleItem(nonFungibleItem); - if (nonFungibleItem is Equipment equipment) - { - equipment.equipped = false; - } - - var productId = context.Random.GenerateRandomGuid(); - - var shopItem = new ShopItem( - ctx.Signer, - sellerAvatarAddress, - productId, - price, - nonFungibleItem); - - IValue shopItemSerialized = shopItem.Serialize(); - IKey productIdSerialized = (IKey)productId.Serialize(); - - Dictionary products = (Dictionary)shopStateDict["products"]; - products = (Dictionary)products.Add(productIdSerialized, shopItemSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Debug("{AddressesHex}Sell Get Register Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Debug("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Debug("{AddressesHex}Sell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell10.cs b/Lib9c/Action/Sell10.cs deleted file mode 100644 index 21e1360342..0000000000 --- a/Lib9c/Action/Sell10.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/602 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell10")] - public class Sell10 : GameAction, ISellV2 - { - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - public Guid orderId; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - Guid? ISellV2.OrderId => orderId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [OrderIdKey] = orderId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - orderId = plainValue[OrderIdKey].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - Address itemAddress = Addresses.GetItemAddress(tradableId); - Address orderAddress = Order.DeriveAddress(orderId); - Address orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(orderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100300ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Order order = OrderFactory.Create(context.Signer, sellerAvatarAddress, orderId, price, tradableId, - context.BlockIndex, itemSubType, count); - order.Validate(avatarState, count); - - ITradableItem tradableItem = order.Sell4(avatarState); - - var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(shopAddress); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - OrderDigest orderDigest = order.Digest(avatarState, costumeStatSheet); - shardedShopState.Add(orderDigest, context.BlockIndex); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var orderReceiptList = states.TryGetState(orderReceiptAddress, out Dictionary receiptDict) - ? new OrderDigestListState(receiptDict) - : new OrderDigestListState(orderReceiptAddress); - - orderReceiptList.Add(orderDigest); - - states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize()); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(orderAddress, order.Serialize()) - .SetState(shopAddress, shardedShopState.Serialize()); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell11.cs b/Lib9c/Action/Sell11.cs deleted file mode 100644 index 8dfded76c9..0000000000 --- a/Lib9c/Action/Sell11.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1376 - /// - [Serializable] - [ActionType("sell11")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class Sell11 : GameAction, ISellV2 - { - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - public Guid orderId; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - Guid? ISellV2.OrderId => orderId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [OrderIdKey] = orderId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - orderId = plainValue[OrderIdKey].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - Address itemAddress = Addresses.GetItemAddress(tradableId); - Address orderAddress = Order.DeriveAddress(orderId); - Address orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(orderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100351ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Sell exec started", addressesHex); - - var ncg = states.GetGoldCurrency(); - if (!price.Currency.Equals(ncg) || - !price.MinorUnit.IsZero || - price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Order order = OrderFactory.Create( - context.Signer, - sellerAvatarAddress, - orderId, - price, - tradableId, - context.BlockIndex, - itemSubType, - count); - order.Validate(avatarState, count); - - ITradableItem tradableItem = order.Sell4(avatarState); - - var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(shopAddress); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - OrderDigest orderDigest = order.Digest(avatarState, costumeStatSheet); - shardedShopState.Add(orderDigest, context.BlockIndex); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var orderReceiptList = - states.TryGetState(orderReceiptAddress, out Dictionary receiptDict) - ? new OrderDigestListState(receiptDict) - : new OrderDigestListState(orderReceiptAddress); - - orderReceiptList.Add(orderDigest); - - states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize()); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(orderAddress, order.Serialize()) - .SetState(shopAddress, shardedShopState.Serialize()); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Debug( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell2.cs b/Lib9c/Action/Sell2.cs deleted file mode 100644 index 59ae0a5799..0000000000 --- a/Lib9c/Action/Sell2.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell2")] - public class Sell2 : GameAction, ISellV1 - { - public Address sellerAvatarAddress; - public Guid itemId; - public FungibleAssetValue price; - - Address ISellV1.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV1.ItemId => itemId; - FungibleAssetValue ISellV1.Price => price; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["itemId"] = itemId.Serialize(), - ["price"] = price.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - itemId = plainValue["itemId"].ToGuid(); - price = plainValue["price"].ToFungibleAssetValue(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - states = states.SetState(sellerAvatarAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - - if (price.Sign < 0) - { - throw new InvalidPriceException($"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out AgentState agentState, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - Log.Verbose("{AddressesHex}Sell Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute Sell; seller: {SellerAvatarAddress}", addressesHex, sellerAvatarAddress); - - var productId = context.Random.GenerateRandomGuid(); - ShopItem shopItem; - - // 인벤토리에서 판매할 아이템을 선택하고 수량을 조절한다. - if (avatarState.inventory.TryGetNonFungibleItem(itemId, out var equipment)) - { - if (equipment.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet; it will be available at the block #{equipment.RequiredBlockIndex}." - ); - } - - avatarState.inventory.RemoveNonFungibleItem(itemId); - equipment.equipped = false; - shopItem = new ShopItem( - ctx.Signer, - sellerAvatarAddress, - productId, - price, - equipment); - } - else if (avatarState.inventory.TryGetNonFungibleItem(itemId, out var costume)) - { -#pragma warning disable 618 - avatarState.inventory.RemoveNonFungibleItem2(itemId); -#pragma warning restore 618 - costume.equipped = false; - shopItem = new ShopItem( - ctx.Signer, - sellerAvatarAddress, - productId, - price, - costume); - } - else - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory."); - } - - IValue shopItemSerialized = shopItem.Serialize(); - IKey productIdSerialized = (IKey)productId.Serialize(); - - Dictionary products = (Dictionary)shopStateDict["products"]; - products = (Dictionary)products.Add(productIdSerialized, shopItemSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get Register Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell3.cs b/Lib9c/Action/Sell3.cs deleted file mode 100644 index b2a7f7dfe0..0000000000 --- a/Lib9c/Action/Sell3.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Numerics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell3")] - public class Sell3 : GameAction, ISellV1 - { - public Address sellerAvatarAddress; - public Guid itemId; - public FungibleAssetValue price; - - Address ISellV1.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV1.ItemId => itemId; - FungibleAssetValue ISellV1.Price => price; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - ["itemId"] = itemId.Serialize(), - ["price"] = price.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - itemId = plainValue["itemId"].ToGuid(); - price = plainValue["price"].ToFungibleAssetValue(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - states = states.SetState(sellerAvatarAddress, MarkChanged); - return states.SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - - if (price.Sign < 0) - { - throw new InvalidPriceException($"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out AgentState agentState, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the shop state was failed to load."); - } - - Log.Verbose("{AddressesHex}Sell Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute Sell; seller: {SellerAvatarAddress}", addressesHex, sellerAvatarAddress); - - var productId = context.Random.GenerateRandomGuid(); - ShopItem shopItem; - - void CheckRequiredBlockIndex(ItemUsable itemUsable) - { - if (itemUsable.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException($"{addressesHex}Aborted as the itemUsable to enhance ({itemId}) is not available yet; it will be available at the block #{itemUsable.RequiredBlockIndex}."); - } - } - - ShopItem PopShopItemFromInventory(ItemUsable itemUsable, Costume costume) - { - avatarState.inventory.RemoveNonFungibleItem(itemId); - return itemUsable is null - ? new ShopItem(ctx.Signer, sellerAvatarAddress, productId, price, costume) - : new ShopItem(ctx.Signer, sellerAvatarAddress, productId, price, itemUsable); - } - - // Select an item to sell from the inventory and adjust the quantity. - if (avatarState.inventory.TryGetNonFungibleItem(itemId, out var equipment)) - { - CheckRequiredBlockIndex(equipment); - // FIXME: Use `equipment.Unequip()` - equipment.equipped = false; - shopItem = PopShopItemFromInventory(equipment, null); - } - else if (avatarState.inventory.TryGetNonFungibleItem(itemId, out var consumable)) - { - CheckRequiredBlockIndex(consumable); - avatarState.inventory.RemoveNonFungibleItem(itemId); - shopItem = PopShopItemFromInventory(consumable, null); - } - else if (avatarState.inventory.TryGetNonFungibleItem(itemId, out var costume)) - { - // FIXME: Use `costume.Unequip()` - costume.equipped = false; - shopItem = PopShopItemFromInventory(null, costume); - } - else - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory."); - } - - IValue shopItemSerialized = shopItem.Serialize(); - IKey productIdSerialized = (IKey)productId.Serialize(); - - Dictionary products = (Dictionary)shopStateDict["products"]; - products = (Dictionary)products.Add(productIdSerialized, shopItemSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get Register Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell4.cs b/Lib9c/Action/Sell4.cs deleted file mode 100644 index 8e2faa8ab9..0000000000 --- a/Lib9c/Action/Sell4.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell4")] - public class Sell4 : GameAction, ISellV1 - { - public const long ExpiredBlockIndex = 16000; - public Address sellerAvatarAddress; - public Guid itemId; - public ItemSubType itemSubType; - public FungibleAssetValue price; - - Address ISellV1.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV1.ItemId => itemId; - FungibleAssetValue ISellV1.Price => price; - string ISellV1.ItemSubType => itemSubType.ToString(); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = itemId.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemId = plainValue[ItemIdKey].ToGuid(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(sellerAvatarAddress, MarkChanged); - states = ShardedShopState.AddressKeys.Aggregate(states, - (current, addressKey) => - current.SetState(ShardedShopState.DeriveAddress(itemSubType, addressKey), MarkChanged)); - return states - .SetState(ctx.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - - if (price.Sign < 0) - { - throw new InvalidPriceException($"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out AgentState agentState, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - Log.Verbose("{AddressesHex}Execute Sell; seller: {SellerAvatarAddress}", addressesHex, sellerAvatarAddress); - - var productId = context.Random.GenerateRandomGuid(); - long expiredBlockIndex = context.BlockIndex + ExpiredBlockIndex; - - // Select an item to sell from the inventory and adjust the quantity. - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out INonFungibleItem nonFungibleItem)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory."); - } - - ItemSubType nonFungibleItemType = nonFungibleItem is Costume costume - ? costume.ItemSubType - : ((ItemUsable) nonFungibleItem).ItemSubType; - - if (!nonFungibleItemType.Equals(itemSubType)) - { - throw new InvalidItemTypeException($"Expected ItemType: {nonFungibleItemType}. Actual ItemType: {itemSubType}"); - } - - if (nonFungibleItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the itemUsable to sell ({itemId}) is not available yet; it will be available at the block #{nonFungibleItem.RequiredBlockIndex}."); - } - - if (nonFungibleItem is Equipment equipment) - { - equipment.Unequip(); - } - nonFungibleItem.RequiredBlockIndex = expiredBlockIndex; - - ShopItem shopItem = new ShopItem(ctx.Signer, sellerAvatarAddress, productId, price, expiredBlockIndex, nonFungibleItem); - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict)) - { - ShardedShopState shardedShopState = new ShardedShopState(shardedShopAddress); - shopStateDict = (Dictionary) shardedShopState.Serialize(); - } - - Log.Verbose("{AddressesHex}Sell Get ShardedShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - List products = (List)shopStateDict[ProductsKey]; - string productKey = LegacyItemUsableKey; - string itemIdKey = LegacyItemIdKey; - string requiredBlockIndexKey = LegacyRequiredBlockIndexKey; - if (nonFungibleItem is Costume) - { - productKey = LegacyCostumeKey; - itemIdKey = LegacyCostumeItemIdKey; - requiredBlockIndexKey = RequiredBlockIndexKey; - } -#pragma warning disable LAA1002 - Dictionary productSerialized = products - .Select(p => (Dictionary) p) - .FirstOrDefault(p => - ((Dictionary) p[productKey])[itemIdKey].Equals(nonFungibleItem.NonFungibleId.Serialize())); -#pragma warning restore LAA1002 - - // 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; - - // Register new ShopItem - if (productSerialized.Equals(Dictionary.Empty)) - { - IValue shopItemSerialized = shopItem.Serialize(); - products = products.Add(shopItemSerialized); - } - // Update Registered ShopItem - else - { - // Delete current ShopItem - products = (List) products.Remove(productSerialized); - - // Update INonfungibleItem.RequiredBlockIndex - Dictionary item = (Dictionary) productSerialized[productKey]; - item = item.SetItem(requiredBlockIndexKey, expiredBlockIndex.Serialize()); - - // Update ShopItem.ExpiredBlockIndex - productSerialized = productSerialized - .SetItem(ExpiredBlockIndexKey, expiredBlockIndex.Serialize()) - .SetItem(productKey, item); - products = products.Add(productSerialized); - shopItem = new ShopItem(productSerialized); - } - shopStateDict = shopStateDict.SetItem(ProductsKey, products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get Register Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - var result = new SellCancellation.Result - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume - }; - var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), expiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(shardedShopAddress, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell5.cs b/Lib9c/Action/Sell5.cs deleted file mode 100644 index 71c6d9d0a6..0000000000 --- a/Lib9c/Action/Sell5.cs +++ /dev/null @@ -1,324 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using BxDictionary = Bencodex.Types.Dictionary; -using BxList = Bencodex.Types.List; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell5")] - public class Sell5 : GameAction, ISellV2 - { - public const long ExpiredBlockIndex = 16000; - - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - if (context.Rehearsal) - { - states = states.SetState(sellerAvatarAddress, MarkChanged); - states = ShardedShopState.AddressKeys.Aggregate( - states, - (current, addressKey) => current.SetState( - ShardedShopState.DeriveAddress(itemSubType, addressKey), - MarkChanged)); - return states.SetState(context.Signer, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStates( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - switch (itemSubType) - { - case ItemSubType.EquipmentMaterial: - case ItemSubType.FoodMaterial: - case ItemSubType.MonsterPart: - case ItemSubType.NormalMaterial: - throw new InvalidShopItemException( - $"{addressesHex}Aborted because {nameof(itemSubType)}({itemSubType}) does not support."); - } - - if (count < 1) - { - throw new InvalidShopItemException( - $"{addressesHex}Aborted because {nameof(count)}({count}) should be greater than or equal to 1."); - } - - if (!avatarState.inventory.TryGetTradableItems(tradableId, context.BlockIndex, count, out List inventoryItems)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted because the tradable item({tradableId}) was failed to load from avatar's inventory."); - } - - IEnumerable tradableItems = inventoryItems.Select(i => (ITradableItem)i.item).ToList(); - var expiredBlockIndex = context.BlockIndex + ExpiredBlockIndex; - - foreach (var ti in tradableItems) - { - if (!ti.ItemSubType.Equals(itemSubType)) - { - throw new InvalidItemTypeException( - $"{addressesHex}Expected ItemSubType: {ti.ItemSubType}. Actual ItemSubType: {itemSubType}"); - } - - if (ti is INonFungibleItem) - { - if (count != 1) - { - throw new ArgumentOutOfRangeException( - $"{addressesHex}Aborted because {nameof(count)}({count}) should be 1 because {nameof(tradableId)}({tradableId}) is non-fungible item."); - } - } - } - - ITradableItem tradableItem = avatarState.inventory.SellItem(tradableId, context.BlockIndex, count); - - var productId = context.Random.GenerateRandomGuid(); - var shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - if (!states.TryGetState(shardedShopAddress, out BxDictionary serializedSharedShopState)) - { - var shardedShopState = new ShardedShopState(shardedShopAddress); - serializedSharedShopState = (BxDictionary) shardedShopState.Serialize(); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var serializedProductList = (BxList) serializedSharedShopState[ProductsKey]; - string productKey; - string itemIdKey; - string requiredBlockIndexKey; - switch (tradableItem.ItemType) - { - case ItemType.Consumable: - case ItemType.Equipment: - productKey = LegacyItemUsableKey; - itemIdKey = LegacyItemIdKey; - requiredBlockIndexKey = LegacyRequiredBlockIndexKey; - break; - case ItemType.Costume: - productKey = LegacyCostumeKey; - itemIdKey = LegacyCostumeItemIdKey; - requiredBlockIndexKey = RequiredBlockIndexKey; - break; - case ItemType.Material: - productKey = TradableFungibleItemKey; - itemIdKey = LegacyCostumeItemIdKey; - requiredBlockIndexKey = RequiredBlockIndexKey; - break; - default: - throw new ArgumentOutOfRangeException(); - } - - BxDictionary serializedProductDictionary; - if (tradableItem.ItemType == ItemType.Material) - { - // Find expired TradableMaterial - serializedProductDictionary = serializedProductList - .Select(p => (BxDictionary) p) - .FirstOrDefault(p => - { - var materialItemId = - ((BxDictionary) p[productKey])[itemIdKey].ToItemId(); - var requiredBlockIndex = p[ExpiredBlockIndexKey].ToLong(); - return TradableMaterial.DeriveTradableId(materialItemId) - .Equals(tradableItem.TradableId) && requiredBlockIndex <= context.BlockIndex; - }); - } - else - { - var serializedTradeId = tradableItem.TradableId.Serialize(); - serializedProductDictionary = serializedProductList - .Select(p => (BxDictionary) p) - .FirstOrDefault(p => - ((BxDictionary) p[productKey])[itemIdKey].Equals(serializedTradeId)); - } - - // Since Bencodex 0.4, Dictionary/List are reference types; so their default values - // are not a empty container, but a null reference: - serializedProductDictionary = serializedProductDictionary ?? Dictionary.Empty; - - ShopItem shopItem; - // Register new ShopItem - if (serializedProductDictionary.Equals(BxDictionary.Empty)) - { - shopItem = new ShopItem( - context.Signer, - sellerAvatarAddress, - productId, - price, - expiredBlockIndex, - tradableItem, - count); - var serializedShopItem = shopItem.Serialize(); - serializedProductList = serializedProductList.Add(serializedShopItem); - } - // Update Registered ShopItem - else - { - // Delete current ShopItem - serializedProductList = - (BxList) serializedProductList.Remove(serializedProductDictionary); - - // Update ITradableItem.RequiredBlockIndex - var inChainShopItem = (BxDictionary) serializedProductDictionary[productKey]; - inChainShopItem = inChainShopItem - .SetItem(requiredBlockIndexKey, expiredBlockIndex.Serialize()); - - // Update ShopItem.ExpiredBlockIndex - serializedProductDictionary = serializedProductDictionary - .SetItem(ExpiredBlockIndexKey, expiredBlockIndex.Serialize()) - .SetItem(productKey, inChainShopItem); - - // Update only Material for backwardCompatible. - if (tradableItem.ItemType == ItemType.Material) - { - serializedProductDictionary = serializedProductDictionary - .SetItem(TradableFungibleItemCountKey, count.Serialize()); - } - - serializedProductList = serializedProductList.Add(serializedProductDictionary); - shopItem = new ShopItem(serializedProductDictionary); - } - - serializedSharedShopState = serializedSharedShopState.SetItem( - ProductsKey, serializedProductList); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Get Register Item: {Elapsed}", addressesHex, - sw.Elapsed); - sw.Restart(); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var result = new SellCancellation.Result - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume, - tradableFungibleItem = shopItem.TradableFungibleItem, - tradableFungibleItemCount = shopItem.TradableFungibleItemCount, - }; - var mail = new SellCancelMail( - result, - context.BlockIndex, - context.Random.GenerateRandomGuid(), - expiredBlockIndex); - result.id = mail.id; - avatarState.Update(mail); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(shardedShopAddress, serializedSharedShopState); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell7.cs b/Lib9c/Action/Sell7.cs deleted file mode 100644 index ee143900f5..0000000000 --- a/Lib9c/Action/Sell7.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell7")] - public class Sell7 : GameAction, ISellV2 - { - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - public Guid orderId; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - Guid? ISellV2.OrderId => orderId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [OrderIdKey] = orderId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - orderId = plainValue[OrderIdKey].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - Address itemAddress = Addresses.GetItemAddress(tradableId); - Address orderAddress = Order.DeriveAddress(orderId); - Address orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(orderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Order order = OrderFactory.Create(context.Signer, sellerAvatarAddress, orderId, price, tradableId, - context.BlockIndex, itemSubType, count); - order.Validate(avatarState, count); - - ITradableItem tradableItem = order.Sell2(avatarState); - - var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(shopAddress); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - OrderDigest orderDigest = order.Digest2(avatarState, costumeStatSheet); - shardedShopState.Add(orderDigest, context.BlockIndex); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var mail = new OrderExpirationMail( - context.BlockIndex, - orderId, - order.ExpiredBlockIndex, - orderId - ); - avatarState.Update(mail); - - var orderReceiptList = states.TryGetState(orderReceiptAddress, out Dictionary receiptDict) - ? new OrderDigestListState(receiptDict) - : new OrderDigestListState(orderReceiptAddress); - orderReceiptList.Add(orderDigest); - - states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize()); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(orderAddress, order.Serialize()) - .SetState(shopAddress, shardedShopState.Serialize()); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell8.cs b/Lib9c/Action/Sell8.cs deleted file mode 100644 index cada6e520f..0000000000 --- a/Lib9c/Action/Sell8.cs +++ /dev/null @@ -1,188 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell8")] - public class Sell8 : GameAction, ISellV2 - { - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - public Guid orderId; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - Guid? ISellV2.OrderId => orderId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [OrderIdKey] = orderId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - orderId = plainValue[OrderIdKey].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - Address itemAddress = Addresses.GetItemAddress(tradableId); - Address orderAddress = Order.DeriveAddress(orderId); - Address orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(orderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Order order = OrderFactory.Create(context.Signer, sellerAvatarAddress, orderId, price, tradableId, - context.BlockIndex, itemSubType, count); - order.Validate(avatarState, count); - - ITradableItem tradableItem = order.Sell2(avatarState); - - var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(shopAddress); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - OrderDigest orderDigest = order.Digest2(avatarState, costumeStatSheet); - shardedShopState.Add(orderDigest, context.BlockIndex); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var orderReceiptList = states.TryGetState(orderReceiptAddress, out Dictionary receiptDict) - ? new OrderDigestListState(receiptDict) - : new OrderDigestListState(orderReceiptAddress); - orderReceiptList.Add(orderDigest); - - states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize()); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(orderAddress, order.Serialize()) - .SetState(shopAddress, shardedShopState.Serialize()); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Sell9.cs b/Lib9c/Action/Sell9.cs deleted file mode 100644 index 880b472a6a..0000000000 --- a/Lib9c/Action/Sell9.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell9")] - public class Sell9 : GameAction, ISellV2 - { - public Address sellerAvatarAddress; - public Guid tradableId; - public int count; - public FungibleAssetValue price; - public ItemSubType itemSubType; - public Guid orderId; - - Address ISellV2.SellerAvatarAddress => sellerAvatarAddress; - Guid ISellV2.TradableId => tradableId; - int ISellV2.Count => count; - FungibleAssetValue ISellV2.Price => price; - string ISellV2.ItemSubType => itemSubType.ToString(); - Guid? ISellV2.OrderId => orderId; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [ItemCountKey] = count.Serialize(), - [PriceKey] = price.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [OrderIdKey] = orderId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - tradableId = plainValue[ItemIdKey].ToGuid(); - count = plainValue[ItemCountKey].ToInteger(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - orderId = plainValue[OrderIdKey].ToGuid(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - Address shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - Address itemAddress = Addresses.GetItemAddress(tradableId); - Address orderAddress = Order.DeriveAddress(orderId); - Address orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(orderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex}Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAgentAvatarStatesV2( - context.Signer, - sellerAvatarAddress, - out _, - out var avatarState, - out _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get AgentAvatarStates: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Order order = OrderFactory.Create(context.Signer, sellerAvatarAddress, orderId, price, tradableId, - context.BlockIndex, itemSubType, count); - order.Validate(avatarState, count); - - ITradableItem tradableItem = order.Sell3(avatarState); - - var shardedShopState = states.TryGetState(shopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(shopAddress); - - sw.Stop(); - Log.Verbose( - "{AddressesHex}Sell Get ShardedShopState: {Elapsed}", - addressesHex, - sw.Elapsed); - sw.Restart(); - - var costumeStatSheet = states.GetSheet(); - OrderDigest orderDigest = order.Digest(avatarState, costumeStatSheet); - shardedShopState.Add(orderDigest, context.BlockIndex); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var orderReceiptList = states.TryGetState(orderReceiptAddress, out Dictionary receiptDict) - ? new OrderDigestListState(receiptDict) - : new OrderDigestListState(orderReceiptAddress); - orderReceiptList.Add(orderDigest); - - states = states.SetState(orderReceiptAddress, orderReceiptList.Serialize()); - states = states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(orderAddress, order.Serialize()) - .SetState(shopAddress, shardedShopState.Serialize()); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose( - "{AddressesHex}Sell Total Executed Time: {Elapsed}", - addressesHex, - ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation0.cs b/Lib9c/Action/SellCancellation0.cs deleted file mode 100644 index 6fd07d312b..0000000000 --- a/Lib9c/Action/SellCancellation0.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation")] - public class SellCancellation0 : GameAction, ISellCancellationV1 - { - public Guid productId; - public Address sellerAvatarAddress; - public SellCancellation.Result result; - - Guid ISellCancellationV1.ProductId => productId; - Address ISellCancellationV1.SellerAvatarAddress => sellerAvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["productId"] = productId.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - productId = plainValue["productId"].ToGuid(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - return states.SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out _, out var avatarState)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - return states; - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) - { - // 스테이지 클리어 부족 에러. - return states; - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 상점에서 아이템을 빼온다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - return states; - } - - ShopItem outUnregisteredItem = new ShopItem((Dictionary)products[productIdSerialized]); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get Unregister Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - //9c-beta 브랜치에서는 블록 인덱스도 확인 해야함 (이전 블록 유효성 보장) - if (outUnregisteredItem.SellerAvatarAddress != sellerAvatarAddress) - { - Log.Error("{AddressesHex}Invalid Avatar Address", addressesHex); - return states; - } - - // 메일에 아이템을 넣는다. - result = new SellCancellation.Result - { - shopItem = outUnregisteredItem, - itemUsable = outUnregisteredItem.ItemUsable - }; - var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - result.id = mail.id; - - avatarState.Update2(mail); - avatarState.UpdateFromAddItem2(result.itemUsable, true); - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation2.cs b/Lib9c/Action/SellCancellation2.cs deleted file mode 100644 index b9ebaede98..0000000000 --- a/Lib9c/Action/SellCancellation2.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation2")] - public class SellCancellation2 : GameAction, ISellCancellationV1 - { - public Guid productId; - public Address sellerAvatarAddress; - public SellCancellation.Result result; - - Guid ISellCancellationV1.ProductId => productId; - Address ISellCancellationV1.SellerAvatarAddress => sellerAvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["productId"] = productId.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - productId = plainValue["productId"].ToGuid(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - return states.SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out _, out var avatarState)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - return states; - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) - { - // 스테이지 클리어 부족 에러. - return states; - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 상점에서 아이템을 빼온다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - return states; - } - - ShopItem outUnregisteredItem = new ShopItem((Dictionary)products[productIdSerialized]); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get Unregister Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - //9c-beta 브랜치에서는 블록 인덱스도 확인 해야함 (이전 블록 유효성 보장) - if (outUnregisteredItem.SellerAvatarAddress != sellerAvatarAddress) - { - Log.Error("{AddressesHex}Invalid Avatar Address", addressesHex); - return states; - } - - // 메일에 아이템을 넣는다. - result = new SellCancellation.Result - { - shopItem = outUnregisteredItem, - itemUsable = outUnregisteredItem.ItemUsable - }; - var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - result.id = mail.id; - - avatarState.Update3(mail); - avatarState.UpdateFromAddItem2(result.itemUsable, true); - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation3.cs b/Lib9c/Action/SellCancellation3.cs deleted file mode 100644 index 1f6c05fbff..0000000000 --- a/Lib9c/Action/SellCancellation3.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation3")] - public class SellCancellation3 : GameAction, ISellCancellationV1 - { - public Guid productId; - public Address sellerAvatarAddress; - public SellCancellation.Result result; - - Guid ISellCancellationV1.ProductId => productId; - Address ISellCancellationV1.SellerAvatarAddress => sellerAvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["productId"] = productId.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - productId = plainValue["productId"].ToGuid(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - return states.SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out _, out var avatarState)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - return states; - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) - { - // 스테이지 클리어 부족 에러. - return states; - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 상점에서 아이템을 빼온다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - return states; - } - - ShopItem outUnregisteredItem = new ShopItem((Dictionary)products[productIdSerialized]); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get Unregister Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - //9c-beta 브랜치에서는 블록 인덱스도 확인 해야함 (이전 블록 유효성 보장) - if (outUnregisteredItem.SellerAvatarAddress != sellerAvatarAddress) - { - Log.Error("{AddressesHex}Invalid Avatar Address", addressesHex); - return states; - } - - // 메일에 아이템을 넣는다. - result = new SellCancellation.Result - { - shopItem = outUnregisteredItem, - itemUsable = outUnregisteredItem.ItemUsable, - costume = outUnregisteredItem.Costume - }; - var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - result.id = mail.id; - - avatarState.Update2(mail); - - if (result.itemUsable != null) - { - avatarState.UpdateFromAddItem2(result.itemUsable, true); - } - - if (result.costume != null) - { - avatarState.UpdateFromAddCostume(result.costume, true); - } - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation4.cs b/Lib9c/Action/SellCancellation4.cs deleted file mode 100644 index 372790e82b..0000000000 --- a/Lib9c/Action/SellCancellation4.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation4")] - public class SellCancellation4 : GameAction, ISellCancellationV1 - { - public Guid productId; - public Address sellerAvatarAddress; - public SellCancellation.Result result; - - Guid ISellCancellationV1.ProductId => productId; - Address ISellCancellationV1.SellerAvatarAddress => sellerAvatarAddress; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - ["productId"] = productId.Serialize(), - ["sellerAvatarAddress"] = sellerAvatarAddress.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - productId = plainValue["productId"].ToGuid(); - sellerAvatarAddress = plainValue["sellerAvatarAddress"].ToAddress(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - if (ctx.Rehearsal) - { - states = states.SetState(ShopState.Address, MarkChanged); - return states.SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex); - - if (!states.TryGetAgentAvatarStates(ctx.Signer, sellerAvatarAddress, out _, out var avatarState)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.TryGetUnlockedWorldByStageClearedBlockIndex( - out var world)) - return states; - - if (world.StageClearedId < GameConfig.RequireClearedStageLevel.ActionsInShop) - { - // 스테이지 클리어 부족 에러. - return states; - } - - if (!states.TryGetState(ShopState.Address, out Bencodex.Types.Dictionary shopStateDict)) - { - return states; - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 상점에서 아이템을 빼온다. - Dictionary products = (Dictionary)shopStateDict["products"]; - - IKey productIdSerialized = (IKey)productId.Serialize(); - if (!products.ContainsKey(productIdSerialized)) - { - return states; - } - - ShopItem outUnregisteredItem = new ShopItem((Dictionary)products[productIdSerialized]); - - products = (Dictionary)products.Remove(productIdSerialized); - shopStateDict = shopStateDict.SetItem("products", products); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get Unregister Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - //9c-beta 브랜치에서는 블록 인덱스도 확인 해야함 (이전 블록 유효성 보장) - if (outUnregisteredItem.SellerAvatarAddress != sellerAvatarAddress) - { - Log.Error("{AddressesHex}Invalid Avatar Address", addressesHex); - return states; - } - - // 메일에 아이템을 넣는다. - result = new SellCancellation.Result - { - shopItem = outUnregisteredItem, - itemUsable = outUnregisteredItem.ItemUsable, - costume = outUnregisteredItem.Costume - }; - var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - result.id = mail.id; - - avatarState.Update(mail); - - if (result.itemUsable != null) - { - avatarState.UpdateFromAddItem2(result.itemUsable, true); - } - - if (result.costume != null) - { - avatarState.UpdateFromAddCostume(result.costume, true); - } - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(ShopState.Address, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation5.cs b/Lib9c/Action/SellCancellation5.cs deleted file mode 100644 index 876096c764..0000000000 --- a/Lib9c/Action/SellCancellation5.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation5")] - public class SellCancellation5 : GameAction, ISellCancellationV2 - { - public Guid productId; - public Address sellerAvatarAddress; - public SellCancellation.Result result; - public ItemSubType itemSubType; - - Guid ISellCancellationV2.ProductId => productId; - Address ISellCancellationV2.SellerAvatarAddress => sellerAvatarAddress; - string ISellCancellationV2.ItemSubType => itemSubType.ToString(); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [ProductIdKey] = productId.Serialize(), - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - productId = plainValue[ProductIdKey].ToGuid(); - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - if (ctx.Rehearsal) - { - states = states.SetState(shardedShopAddress, MarkChanged); - return states - .SetState(Addresses.Shop, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex); - - if (!states.TryGetAvatarState(ctx.Signer, sellerAvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the seller failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - if (!states.TryGetState(shardedShopAddress, out Dictionary shopStateDict)) - { - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - shopStateDict = (Dictionary) shopState.Serialize(); - } - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 상점에서 아이템을 빼온다. - 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 backwardCompatible = 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)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) could not be found from the legacy shop." - ); - } - - productSerialized = (Dictionary) legacyProducts[productKey]; - legacyProducts = (Dictionary) legacyProducts.Remove(productKey); - legacyShopDict = legacyShopDict.SetItem(LegacyProductsKey, legacyProducts); - states = states.SetState(Addresses.Shop, legacyShopDict); - backwardCompatible = true; - } - } - else - { - products = (List) products.Remove(productSerialized); - shopStateDict = shopStateDict.SetItem(ProductsKey, products); - } - ShopItem shopItem = new ShopItem(productSerialized); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get Unregister Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (shopItem.SellerAvatarAddress != sellerAvatarAddress || shopItem.SellerAgentAddress != ctx.Signer) - { - throw new InvalidAddressException($"{addressesHex}Invalid Avatar Address"); - } - - INonFungibleItem nonFungibleItem = (INonFungibleItem)shopItem.ItemUsable ?? shopItem.Costume; - if (avatarState.inventory.TryGetNonFungibleItem(nonFungibleItem.NonFungibleId, out INonFungibleItem outNonFungibleItem)) - { - outNonFungibleItem.RequiredBlockIndex = ctx.BlockIndex; - } - nonFungibleItem.RequiredBlockIndex = ctx.BlockIndex; - - if (backwardCompatible) - { - switch (nonFungibleItem) - { - case ItemUsable itemUsable: - avatarState.UpdateFromAddItem2(itemUsable, true); - break; - case Costume costume: - avatarState.UpdateFromAddCostume(costume, true); - break; - } - } - // 메일에 아이템을 넣는다. - result = new SellCancellation.Result - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume - }; - var mail = new SellCancelMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), ctx.BlockIndex); - result.id = mail.id; - - avatarState.Update(mail); - avatarState.updatedAt = ctx.BlockIndex; - avatarState.blockIndex = ctx.BlockIndex; - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(shardedShopAddress, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation6.cs b/Lib9c/Action/SellCancellation6.cs deleted file mode 100644 index 3d9af5980c..0000000000 --- a/Lib9c/Action/SellCancellation6.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Serilog; -using BxDictionary = Bencodex.Types.Dictionary; -using BxList = Bencodex.Types.List; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation6")] - public class SellCancellation6 : GameAction, ISellCancellationV2 - { - public Guid productId; - public Address sellerAvatarAddress; - public SellCancellation.Result result; - public ItemSubType itemSubType; - - Guid ISellCancellationV2.ProductId => productId; - Address ISellCancellationV2.SellerAvatarAddress => sellerAvatarAddress; - string ISellCancellationV2.ItemSubType => itemSubType.ToString(); - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [ProductIdKey] = productId.Serialize(), - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - productId = plainValue[ProductIdKey].ToGuid(); - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - if (context.Rehearsal) - { - states = states.SetState(shardedShopAddress, MarkChanged); - return states - .SetState(Addresses.Shop, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel exec started", addressesHex); - - if (!states.TryGetAvatarState(context.Signer, sellerAvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the seller failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - if (!states.TryGetState(shardedShopAddress, out BxDictionary shopStateDict)) - { - var shopState = new ShardedShopState(shardedShopAddress); - shopStateDict = (BxDictionary) shopState.Serialize(); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // 상점에서 아이템을 빼온다. - var products = (BxList)shopStateDict[ProductsKey]; - var productIdSerialized = productId.Serialize(); - var productSerialized = products - .Select(p => (BxDictionary) 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; - - var backwardCompatible = false; - if (productSerialized.Equals(BxDictionary.Empty)) - { - if (itemSubType == ItemSubType.Hourglass || itemSubType == ItemSubType.ApStone) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) could not be found from the shop."); - } - // Backward compatibility. - var rawShop = states.GetState(Addresses.Shop); - if (!(rawShop is null)) - { - var legacyShopDict = (BxDictionary) rawShop; - var legacyProducts = (BxDictionary) legacyShopDict[LegacyProductsKey]; - var productKey = (IKey) productId.Serialize(); - // SoldOut - if (!legacyProducts.ContainsKey(productKey)) - { - throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the shop item ({productId}) could not be found from the legacy shop." - ); - } - - productSerialized = (BxDictionary) legacyProducts[productKey]; - legacyProducts = (BxDictionary) legacyProducts.Remove(productKey); - legacyShopDict = legacyShopDict.SetItem(LegacyProductsKey, legacyProducts); - states = states.SetState(Addresses.Shop, legacyShopDict); - backwardCompatible = true; - } - } - else - { - products = (BxList) products.Remove(productSerialized); - shopStateDict = shopStateDict.SetItem(ProductsKey, products); - } - - var shopItem = new ShopItem(productSerialized); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get Unregister Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (shopItem.SellerAvatarAddress != sellerAvatarAddress || shopItem.SellerAgentAddress != context.Signer) - { - throw new InvalidAddressException($"{addressesHex}Invalid Avatar Address"); - } - - ITradableItem tradableItem; - int itemCount = 1; - if (!(shopItem.ItemUsable is null)) - { - tradableItem = shopItem.ItemUsable; - } - else if (!(shopItem.Costume is null)) - { - tradableItem = shopItem.Costume; - } - else if (!(shopItem.TradableFungibleItem is null)) - { - tradableItem = shopItem.TradableFungibleItem; - itemCount = shopItem.TradableFungibleItemCount; - } - else - { - throw new InvalidShopItemException($"{addressesHex}Tradable Item is null."); - } - - if (!backwardCompatible) - { - avatarState.inventory.UpdateTradableItem(tradableItem.TradableId, - tradableItem.RequiredBlockIndex, itemCount, context.BlockIndex); - } - - if (tradableItem is INonFungibleItem nonFungibleItem) - { - nonFungibleItem.RequiredBlockIndex = context.BlockIndex; - if (backwardCompatible) - { - switch (nonFungibleItem) - { - case ItemUsable itemUsable: - avatarState.UpdateFromAddItem2(itemUsable, true); - break; - case Costume costume: - avatarState.UpdateFromAddCostume(costume, true); - break; - } - } - } - - // 메일에 아이템을 넣는다. - result = new SellCancellation.Result - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume, - tradableFungibleItem = shopItem.TradableFungibleItem, - tradableFungibleItemCount = shopItem.TradableFungibleItemCount, - }; - var mail = new SellCancelMail(result, context.BlockIndex, context.Random.GenerateRandomGuid(), context.BlockIndex); - result.id = mail.id; - - avatarState.Update(mail); - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(sellerAvatarAddress, avatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states.SetState(shardedShopAddress, shopStateDict); - sw.Stop(); - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Sell Cancel Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex}Sell Cancel Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} diff --git a/Lib9c/Action/TransferAsset.cs b/Lib9c/Action/TransferAsset.cs index f247af2638..b7b8e57c3d 100644 --- a/Lib9c/Action/TransferAsset.cs +++ b/Lib9c/Action/TransferAsset.cs @@ -28,6 +28,29 @@ public class TransferAsset : ActionBase, ISerializable, ITransferAsset, ITransfe private const int MemoMaxLength = 80; public const string TypeIdentifier = "transfer_asset4"; + + // Copied from deleted TransferAsset3 + // FIXME justify this policy. + public const long CrystalTransferringRestrictionStartIndex = 6_220_000L; + + // FIXME justify this policy. + public static readonly IReadOnlyList
AllowedCrystalTransfers = new Address[] + { + // world boss service + new Address("CFCd6565287314FF70e4C4CF309dB701C43eA5bD"), + // world boss ops + new Address("3ac40802D359a6B51acB0AC0710cc90de19C9B81"), + }; + + public static void CheckCrystalSender(Currency currency, long blockIndex, Address sender) + { + if (currency.Equals(CrystalCalculator.CRYSTAL) && + blockIndex >= CrystalTransferringRestrictionStartIndex && !AllowedCrystalTransfers.Contains(sender)) + { + throw new InvalidTransferCurrencyException($"transfer crystal not allowed {sender}"); + } + } + public TransferAsset() { } @@ -116,7 +139,7 @@ public override IAccountStateDelta Execute(IActionContext context) ); } - TransferAsset3.CheckCrystalSender(currency, context.BlockIndex, Sender); + CheckCrystalSender(currency, context.BlockIndex, Sender); var ended = DateTimeOffset.UtcNow; Log.Debug("{AddressesHex}TransferAsset4 Total Executed Time: {Elapsed}", addressesHex, ended - started); return state.TransferAsset(context, Sender, Recipient, Amount); diff --git a/Lib9c/Action/TransferAsset0.cs b/Lib9c/Action/TransferAsset0.cs deleted file mode 100644 index 97c868a08e..0000000000 --- a/Lib9c/Action/TransferAsset0.cs +++ /dev/null @@ -1,141 +0,0 @@ -using Bencodex; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using Lib9c.Abstractions; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("transfer_asset")] - public class TransferAsset0 : ActionBase, ISerializable, ITransferAsset, ITransferAssetV1 - { - private const int MemoMaxLength = 80; - - public TransferAsset0() - { - } - - public TransferAsset0(Address sender, Address recipient, FungibleAssetValue amount, string memo = null) - { - Sender = sender; - Recipient = recipient; - Amount = amount; - - CheckMemoLength(memo); - Memo = memo; - } - - protected TransferAsset0(SerializationInfo info, StreamingContext context) - { - var rawBytes = (byte[])info.GetValue("serialized", typeof(byte[])); - Dictionary pv = (Dictionary) new Codec().Decode(rawBytes); - - LoadPlainValue(pv); - } - - public Address Sender { get; private set; } - public Address Recipient { get; private set; } - public FungibleAssetValue Amount { get; private set; } - public string Memo { get; private set; } - - Address ITransferAssetV1.Sender => Sender; - Address ITransferAssetV1.Recipient => Recipient; - FungibleAssetValue ITransferAssetV1.Amount => Amount; - string ITransferAssetV1.Memo => Memo; - - public override IValue PlainValue - { - get - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text) "sender", Sender.Serialize()), - new KeyValuePair((Text) "recipient", Recipient.Serialize()), - new KeyValuePair((Text) "amount", Amount.Serialize()), - }; - - if (!(Memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text) "memo", Memo.Serialize())); - } - - return Dictionary.Empty - .Add("type_id", "transfer_asset") - .Add("values", new Dictionary(pairs)); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - if (context.Rehearsal) - { - return state.MarkBalanceChanged(context, Amount.Currency, new[] { Sender, Recipient }); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - if (Sender != context.Signer) - { - throw new InvalidTransferSignerException(context.Signer, Sender, Recipient); - } - - // This works for block after 380000. Please take a look at - // https://github.com/planetarium/libplanet/pull/1133 - if (context.BlockIndex > 380000 && Sender == Recipient) - { - throw new InvalidTransferRecipientException(Sender, Recipient); - } - - Currency currency = Amount.Currency; - if (!(currency.Minters is null) && - (currency.Minters.Contains(Sender) || currency.Minters.Contains(Recipient))) - { - throw new InvalidTransferMinterException( - currency.Minters, - Sender, - Recipient - ); - } - - return state.TransferAsset(context, Sender, Recipient, Amount); - } - - public override void LoadPlainValue(IValue plainValue) - { - var asDict = (Dictionary)((Dictionary)plainValue)["values"]; - - Sender = asDict["sender"].ToAddress(); - Recipient = asDict["recipient"].ToAddress(); - Amount = asDict["amount"].ToFungibleAssetValue(); - Memo = asDict.TryGetValue((Text) "memo", out IValue memo) ? memo.ToDotnetString() : null; - - CheckMemoLength(Memo); - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("serialized", new Codec().Encode(PlainValue)); - } - - private void CheckMemoLength(string memo) - { - if (memo?.Length > MemoMaxLength) - { - string msg = $"The length of the memo, {memo.Length}, " + - $"is overflowed than the max length, {MemoMaxLength}."; - throw new MemoLengthOverflowException(msg); - } - } - } -} diff --git a/Lib9c/Action/TransferAsset2.cs b/Lib9c/Action/TransferAsset2.cs deleted file mode 100644 index 15cd9e895e..0000000000 --- a/Lib9c/Action/TransferAsset2.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Bencodex; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using Lib9c.Abstractions; -using Nekoyume.Model; -using Serilog; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/636 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionObsolete(TransferAsset3.CrystalTransferringRestrictionStartIndex - 1)] - [ActionType("transfer_asset2")] - public class TransferAsset2 : ActionBase, ISerializable, ITransferAsset, ITransferAssetV1 - { - private const int MemoMaxLength = 80; - - public TransferAsset2() - { - } - - public TransferAsset2(Address sender, Address recipient, FungibleAssetValue amount, string memo = null) - { - Sender = sender; - Recipient = recipient; - Amount = amount; - - CheckMemoLength(memo); - Memo = memo; - } - - protected TransferAsset2(SerializationInfo info, StreamingContext context) - { - var rawBytes = (byte[])info.GetValue("serialized", typeof(byte[])); - Dictionary pv = (Dictionary) new Codec().Decode(rawBytes); - - LoadPlainValue(pv); - } - - public Address Sender { get; private set; } - public Address Recipient { get; private set; } - public FungibleAssetValue Amount { get; private set; } - public string Memo { get; private set; } - - Address ITransferAssetV1.Sender => Sender; - Address ITransferAssetV1.Recipient => Recipient; - FungibleAssetValue ITransferAssetV1.Amount => Amount; - string ITransferAssetV1.Memo => Memo; - - public override IValue PlainValue - { - get - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text) "sender", Sender.Serialize()), - new KeyValuePair((Text) "recipient", Recipient.Serialize()), - new KeyValuePair((Text) "amount", Amount.Serialize()), - }; - - if (!(Memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text) "memo", Memo.Serialize())); - } - - return Dictionary.Empty - .Add("type_id", "transfer_asset2") - .Add("values", new Dictionary(pairs)); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - if (context.Rehearsal) - { - return state.MarkBalanceChanged(context, Amount.Currency, new[] { Sender, Recipient }); - } - - CheckObsolete(TransferAsset3.CrystalTransferringRestrictionStartIndex - 1, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, context.Signer); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}TransferAsset2 exec started", addressesHex); - if (Sender != context.Signer) - { - throw new InvalidTransferSignerException(context.Signer, Sender, Recipient); - } - - // This works for block after 380000. Please take a look at - // https://github.com/planetarium/libplanet/pull/1133 - if (context.BlockIndex > 380000 && Sender == Recipient) - { - throw new InvalidTransferRecipientException(Sender, Recipient); - } - - Address recipientAddress = Recipient.Derive(ActivationKey.DeriveKey); - - // Check new type of activation first. - if (state.GetState(recipientAddress) is null && state.GetState(Addresses.ActivatedAccount) is Dictionary asDict ) - { - var activatedAccountsState = new ActivatedAccountsState(asDict); - var activatedAccounts = activatedAccountsState.Accounts; - // if ActivatedAccountsState is empty, all user is activate. - if (activatedAccounts.Count != 0 - && !activatedAccounts.Contains(Recipient)) - { - throw new InvalidTransferUnactivatedRecipientException(Sender, Recipient); - } - } - - Currency currency = Amount.Currency; - if (!(currency.Minters is null) && - (currency.Minters.Contains(Sender) || currency.Minters.Contains(Recipient))) - { - throw new InvalidTransferMinterException( - currency.Minters, - Sender, - Recipient - ); - } - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}TransferAsset2 Total Executed Time: {Elapsed}", addressesHex, ended - started); - return state.TransferAsset(context, Sender, Recipient, Amount); - } - - public override void LoadPlainValue(IValue plainValue) - { - var asDict = (Dictionary)((Dictionary)plainValue)["values"]; - - Sender = asDict["sender"].ToAddress(); - Recipient = asDict["recipient"].ToAddress(); - Amount = asDict["amount"].ToFungibleAssetValue(); - Memo = asDict.TryGetValue((Text) "memo", out IValue memo) ? memo.ToDotnetString() : null; - - CheckMemoLength(Memo); - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("serialized", new Codec().Encode(PlainValue)); - } - - private void CheckMemoLength(string memo) - { - if (memo?.Length > MemoMaxLength) - { - string msg = $"The length of the memo, {memo.Length}, " + - $"is overflowed than the max length, {MemoMaxLength}."; - throw new MemoLengthOverflowException(msg); - } - } - } -} diff --git a/Lib9c/Action/TransferAsset3.cs b/Lib9c/Action/TransferAsset3.cs deleted file mode 100644 index e498a35fd2..0000000000 --- a/Lib9c/Action/TransferAsset3.cs +++ /dev/null @@ -1,193 +0,0 @@ -using Bencodex; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using Lib9c.Abstractions; -using Libplanet.Action.State; -using Nekoyume.Helper; -using Nekoyume.Model; -using Serilog; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/636 - /// Updated at https://github.com/planetarium/lib9c/pull/1718 - /// - [Serializable] - [ActionType("transfer_asset3")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class TransferAsset3 : ActionBase, ISerializable, ITransferAsset, ITransferAssetV1 - { - private const int MemoMaxLength = 80; - - // FIXME justify this policy. - public const long CrystalTransferringRestrictionStartIndex = 6_220_000L; - - // FIXME justify this policy. - public static readonly IReadOnlyList
AllowedCrystalTransfers = new Address[] - { - // world boss service - new Address("CFCd6565287314FF70e4C4CF309dB701C43eA5bD"), - // world boss ops - new Address("3ac40802D359a6B51acB0AC0710cc90de19C9B81"), - }; - - public TransferAsset3() - { - } - - public TransferAsset3(Address sender, Address recipient, FungibleAssetValue amount, string memo = null) - { - Sender = sender; - Recipient = recipient; - Amount = amount; - - CheckMemoLength(memo); - Memo = memo; - } - - protected TransferAsset3(SerializationInfo info, StreamingContext context) - { - var rawBytes = (byte[])info.GetValue("serialized", typeof(byte[])); - Dictionary pv = (Dictionary) new Codec().Decode(rawBytes); - - LoadPlainValue(pv); - } - - public Address Sender { get; private set; } - public Address Recipient { get; private set; } - public FungibleAssetValue Amount { get; private set; } - public string Memo { get; private set; } - - Address ITransferAssetV1.Sender => Sender; - Address ITransferAssetV1.Recipient => Recipient; - FungibleAssetValue ITransferAssetV1.Amount => Amount; - string ITransferAssetV1.Memo => Memo; - - public override IValue PlainValue - { - get - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text) "sender", Sender.Serialize()), - new KeyValuePair((Text) "recipient", Recipient.Serialize()), - new KeyValuePair((Text) "amount", Amount.Serialize()), - }; - - if (!(Memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text) "memo", Memo.Serialize())); - } - - return Dictionary.Empty - .Add("type_id", "transfer_asset3") - .Add("values", new Dictionary(pairs)); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - if (context.Rehearsal) - { - return state.MarkBalanceChanged(context, Amount.Currency, new[] { Sender, Recipient }); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, context.Signer); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}TransferAsset3 exec started", addressesHex); - if (Sender != context.Signer) - { - throw new InvalidTransferSignerException(context.Signer, Sender, Recipient); - } - - if (Sender == Recipient) - { - throw new InvalidTransferRecipientException(Sender, Recipient); - } - - Address recipientAddress = Recipient.Derive(ActivationKey.DeriveKey); - - // Check new type of activation first. - // If result of GetState is not null, it is assumed that it has been activated. - if ( - state.GetState(recipientAddress) is null && - state.GetState(Addresses.ActivatedAccount) is Dictionary asDict && - state.GetState(Recipient) is null - ) - { - var activatedAccountsState = new ActivatedAccountsState(asDict); - var activatedAccounts = activatedAccountsState.Accounts; - // if ActivatedAccountsState is empty, all user is activate. - if (activatedAccounts.Count != 0 - && !activatedAccounts.Contains(Recipient) - && state.GetState(Recipient) is null) - { - throw new InvalidTransferUnactivatedRecipientException(Sender, Recipient); - } - } - - Currency currency = Amount.Currency; - if (!(currency.Minters is null) && - (currency.Minters.Contains(Sender) || currency.Minters.Contains(Recipient))) - { - throw new InvalidTransferMinterException( - currency.Minters, - Sender, - Recipient - ); - } - - CheckCrystalSender(currency, context.BlockIndex, Sender); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}TransferAsset3 Total Executed Time: {Elapsed}", addressesHex, ended - started); - return state.TransferAsset(context, Sender, Recipient, Amount); - } - - public override void LoadPlainValue(IValue plainValue) - { - var asDict = (Dictionary)((Dictionary)plainValue)["values"]; - - Sender = asDict["sender"].ToAddress(); - Recipient = asDict["recipient"].ToAddress(); - Amount = asDict["amount"].ToFungibleAssetValue(); - Memo = asDict.TryGetValue((Text) "memo", out IValue memo) ? memo.ToDotnetString() : null; - - CheckMemoLength(Memo); - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("serialized", new Codec().Encode(PlainValue)); - } - - public static void CheckCrystalSender(Currency currency, long blockIndex, Address sender) - { - if (currency.Equals(CrystalCalculator.CRYSTAL) && - blockIndex >= CrystalTransferringRestrictionStartIndex && !AllowedCrystalTransfers.Contains(sender)) - { - throw new InvalidTransferCurrencyException($"transfer crystal not allowed {sender}"); - } - } - - private void CheckMemoLength(string memo) - { - if (memo?.Length > MemoMaxLength) - { - string msg = $"The length of the memo, {memo.Length}, " + - $"is overflowed than the max length, {MemoMaxLength}."; - throw new MemoLengthOverflowException(msg); - } - } - } -} diff --git a/Lib9c/Action/TransferAssets.cs b/Lib9c/Action/TransferAssets.cs index 36a1e392f4..48c823d90a 100644 --- a/Lib9c/Action/TransferAssets.cs +++ b/Lib9c/Action/TransferAssets.cs @@ -159,7 +159,7 @@ private IAccountStateDelta Transfer( ); } - TransferAsset3.CheckCrystalSender(currency, blockIndex, Sender); + TransferAsset.CheckCrystalSender(currency, blockIndex, Sender); return state.TransferAsset(context, Sender, recipient, amount); } } diff --git a/Lib9c/Action/TransferAssets0.cs b/Lib9c/Action/TransferAssets0.cs deleted file mode 100644 index 55e5663c95..0000000000 --- a/Lib9c/Action/TransferAssets0.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Bencodex; -using Bencodex.Types; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.State; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.Serialization; -using Lib9c.Abstractions; -using Nekoyume.Model; -using Serilog; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/636 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionType("transfer_assets")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class TransferAssets0 : ActionBase, ISerializable, ITransferAssets, ITransferAssetsV1 - { - public const int RecipientsCapacity = 100; - private const int MemoMaxLength = 80; - - public TransferAssets0() - { - } - - public TransferAssets0(Address sender, List<(Address, FungibleAssetValue)> recipients, string memo = null) - { - Sender = sender; - Recipients = recipients; - - CheckMemoLength(memo); - Memo = memo; - } - - protected TransferAssets0(SerializationInfo info, StreamingContext context) - { - var rawBytes = (byte[])info.GetValue("serialized", typeof(byte[])); - Dictionary pv = (Dictionary) new Codec().Decode(rawBytes); - - LoadPlainValue(pv); - } - - public Address Sender { get; private set; } - public List<(Address recipient, FungibleAssetValue amount)> Recipients { get; private set; } - public string Memo { get; private set; } - - Address ITransferAssetsV1.Sender => Sender; - - List<(Address recipient, FungibleAssetValue amount)> ITransferAssetsV1.Recipients => - Recipients; - string ITransferAssetsV1.Memo => Memo; - - public override IValue PlainValue - { - get - { - IEnumerable> pairs = new[] - { - new KeyValuePair((Text) "sender", Sender.Serialize()), - new KeyValuePair((Text) "recipients", Recipients.Aggregate(List.Empty, (list, t) => list.Add(List.Empty.Add(t.recipient.Serialize()).Add(t.amount.Serialize())))), - }; - - if (!(Memo is null)) - { - pairs = pairs.Append(new KeyValuePair((Text) "memo", Memo.Serialize())); - } - - return Dictionary.Empty - .Add("type_id", "transfer_assets") - .Add("values", new Dictionary(pairs)); - } - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - if (context.Rehearsal) - { - return Recipients.Aggregate(state, (current, t) => current.MarkBalanceChanged(context, t.amount.Currency, new[] {Sender, t.recipient})); - } - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - if (Recipients.Count > RecipientsCapacity) - { - throw new ArgumentOutOfRangeException($"{nameof(Recipients)} must be less than or equal {RecipientsCapacity}."); - } - var addressesHex = GetSignerAndOtherAddressesHex(context, context.Signer); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}transfer_assets exec started", addressesHex); - - var activatedAccountsState = state.GetState(Addresses.ActivatedAccount) is Dictionary asDict - ? new ActivatedAccountsState(asDict) - : new ActivatedAccountsState(); - - state = Recipients.Aggregate(state, (current, t) => Transfer(context, current, context.Signer, t.recipient, t.amount, activatedAccountsState, context.BlockIndex)); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}transfer_assets Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return state; - } - - public override void LoadPlainValue(IValue plainValue) - { - var asDict = (Dictionary)((Dictionary)plainValue)["values"]; - - Sender = asDict["sender"].ToAddress(); - var rawMap = (List)asDict["recipients"]; - Recipients = new List<(Address recipient, FungibleAssetValue amount)>(); - foreach (var iValue in rawMap) - { - var list = (List) iValue; - Recipients.Add((list[0].ToAddress(), list[1].ToFungibleAssetValue())); - } - Memo = asDict.TryGetValue((Text) "memo", out IValue memo) ? memo.ToDotnetString() : null; - - CheckMemoLength(Memo); - } - - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - info.AddValue("serialized", new Codec().Encode(PlainValue)); - } - - private void CheckMemoLength(string memo) - { - if (memo?.Length > MemoMaxLength) - { - string msg = $"The length of the memo, {memo.Length}, " + - $"is overflowed than the max length, {MemoMaxLength}."; - throw new MemoLengthOverflowException(msg); - } - } - - private IAccountStateDelta Transfer( - IActionContext context, IAccountStateDelta state, Address signer, Address recipient, FungibleAssetValue amount, ActivatedAccountsState activatedAccountsState, long blockIndex) - { - if (Sender != signer) - { - throw new InvalidTransferSignerException(signer, Sender, recipient); - } - - if (Sender == recipient) - { - throw new InvalidTransferRecipientException(Sender, recipient); - } - - Address recipientAddress = recipient.Derive(ActivationKey.DeriveKey); - - // Check new type of activation first. - // If result of GetState is not null, it is assumed that it has been activated. - if ( - state.GetState(recipientAddress) is null && - state.GetState(recipient) is null - ) - { - var activatedAccounts = activatedAccountsState.Accounts; - // if ActivatedAccountsState is empty, all user is activate. - if (activatedAccounts.Count != 0 - && !activatedAccounts.Contains(recipient) - && state.GetState(recipient) is null) - { - throw new InvalidTransferUnactivatedRecipientException(Sender, recipient); - } - } - - Currency currency = amount.Currency; - if (!(currency.Minters is null) && - (currency.Minters.Contains(Sender) || currency.Minters.Contains(recipient))) - { - throw new InvalidTransferMinterException( - currency.Minters, - Sender, - recipient - ); - } - - TransferAsset3.CheckCrystalSender(currency, blockIndex, Sender); - return state.TransferAsset(context, Sender, recipient, amount); - } - } -} diff --git a/Lib9c/Action/UpdateSell0.cs b/Lib9c/Action/UpdateSell0.cs deleted file mode 100644 index f7572cc461..0000000000 --- a/Lib9c/Action/UpdateSell0.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; -using BxDictionary = Bencodex.Types.Dictionary; -using BxList = Bencodex.Types.List; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("update_sell")] - public class UpdateSell0 : GameAction, IUpdateSellV1 - { - public Guid orderId; - public Guid updateSellOrderId; - public Guid tradableId; - public Address sellerAvatarAddress; - public ItemSubType itemSubType; - public FungibleAssetValue price; - public int count; - - Guid IUpdateSellV1.OrderId => orderId; - Guid IUpdateSellV1.UpdateSellOrderId => updateSellOrderId; - Guid IUpdateSellV1.TradableId => tradableId; - Address IUpdateSellV1.SellerAvatarAddress => sellerAvatarAddress; - string IUpdateSellV1.ItemSubType => itemSubType.ToString(); - FungibleAssetValue IUpdateSellV1.Price => price; - int IUpdateSellV1.Count => count; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [OrderIdKey] = orderId.Serialize(), - [updateSellOrderIdKey] = updateSellOrderId.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [PriceKey] = price.Serialize(), - [ItemCountKey] = count.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - orderId = plainValue[OrderIdKey].ToGuid(); - updateSellOrderId = plainValue[updateSellOrderIdKey].ToGuid(); - tradableId = plainValue[ItemIdKey].ToGuid(); - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - count = plainValue[ItemCountKey].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - var shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); - var updateSellOrderAddress = Order.DeriveAddress(updateSellOrderId); - var itemAddress = Addresses.GetItemAddress(tradableId); - var orderReceiptAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(updateSellShopAddress, MarkChanged) - .SetState(updateSellOrderAddress, MarkChanged) - .SetState(orderReceiptAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - // common - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex} updateSell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex} Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, sellerAvatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex} Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - - Log.Verbose("{AddressesHex} Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - sw.Stop(); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - // for sell cancel - Log.Verbose("{AddressesHex} UpdateSell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!states.TryGetState(shopAddress, out BxDictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shopAddress})."); - } - sw.Stop(); - - Log.Verbose("{AddressesHex} UpdateSell Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!states.TryGetState(Order.DeriveAddress(orderId), out Dictionary orderDict)) - { - throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(Order)}({Order.DeriveAddress(orderId)})."); - } - - var orderOnSale = OrderFactory.Deserialize(orderDict); - var fromPreviousAction = false; - try - { - orderOnSale.ValidateCancelOrder(avatarState, tradableId); - } - catch (Exception) - { - orderOnSale.ValidateCancelOrder2(avatarState, tradableId); - fromPreviousAction = true; - } - - var itemOnSale = fromPreviousAction - ? orderOnSale.Cancel2(avatarState, context.BlockIndex) - : orderOnSale.Cancel(avatarState, context.BlockIndex); - if (context.BlockIndex < orderOnSale.ExpiredBlockIndex) - { - var shardedShopState = new ShardedShopStateV2(shopStateDict); - shardedShopState.Remove(orderOnSale, context.BlockIndex); - states = states.SetState(shopAddress, shardedShopState.Serialize()); - } - - if (!states.TryGetState(orderReceiptAddress, out Dictionary rawList)) - { - throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(OrderDigest)}({orderReceiptAddress})."); - } - var digestList = new OrderDigestListState(rawList); - digestList.Remove(orderOnSale.OrderId); - states = states.SetState(itemAddress, itemOnSale.Serialize()) - .SetState(orderReceiptAddress, digestList.Serialize()); - sw.Stop(); - - var expirationMail = avatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - avatarState.mailBox.Remove(expirationMail); - } - - // for updateSell - var updateSellShopState = states.TryGetState(updateSellShopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(updateSellShopAddress); - - Log.Verbose("{AddressesHex} UpdateSell Get ShardedShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var newOrder = OrderFactory.Create(context.Signer, sellerAvatarAddress, updateSellOrderId, price, tradableId, - context.BlockIndex, itemSubType, count); - newOrder.Validate(avatarState, count); - - var tradableItem = newOrder.Sell3(avatarState); - var costumeStatSheet = states.GetSheet(); - var orderDigest = newOrder.Digest(avatarState, costumeStatSheet); - updateSellShopState.Add(orderDigest, context.BlockIndex); - - digestList.Add(orderDigest); - states = states.SetState(orderReceiptAddress, digestList.Serialize()); - states = states.SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - - Log.Verbose("{AddressesHex} UpdateSell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(updateSellOrderAddress, newOrder.Serialize()) - .SetState(updateSellShopAddress, updateSellShopState.Serialize()); - sw.Stop(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex} UpdateSell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex} UpdateSell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/UpdateSell2.cs b/Lib9c/Action/UpdateSell2.cs deleted file mode 100644 index 1d955deb21..0000000000 --- a/Lib9c/Action/UpdateSell2.cs +++ /dev/null @@ -1,226 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; -using BxDictionary = Bencodex.Types.Dictionary; -using BxList = Bencodex.Types.List; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/602 - /// Updated at https://github.com/planetarium/lib9c/pull/1022 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("update_sell2")] - public class UpdateSell2 : GameAction, IUpdateSellV1 - { - public Guid orderId; - public Guid updateSellOrderId; - public Guid tradableId; - public Address sellerAvatarAddress; - public ItemSubType itemSubType; - public FungibleAssetValue price; - public int count; - - Guid IUpdateSellV1.OrderId => orderId; - Guid IUpdateSellV1.UpdateSellOrderId => updateSellOrderId; - Guid IUpdateSellV1.TradableId => tradableId; - Address IUpdateSellV1.SellerAvatarAddress => sellerAvatarAddress; - string IUpdateSellV1.ItemSubType => itemSubType.ToString(); - FungibleAssetValue IUpdateSellV1.Price => price; - int IUpdateSellV1.Count => count; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [OrderIdKey] = orderId.Serialize(), - [updateSellOrderIdKey] = updateSellOrderId.Serialize(), - [ItemIdKey] = tradableId.Serialize(), - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [PriceKey] = price.Serialize(), - [ItemCountKey] = count.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - orderId = plainValue[OrderIdKey].ToGuid(); - updateSellOrderId = plainValue[updateSellOrderIdKey].ToGuid(); - tradableId = plainValue[ItemIdKey].ToGuid(); - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - price = plainValue[PriceKey].ToFungibleAssetValue(); - count = plainValue[ItemCountKey].ToInteger(); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - var shopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, updateSellOrderId); - var updateSellOrderAddress = Order.DeriveAddress(updateSellOrderId); - var itemAddress = Addresses.GetItemAddress(tradableId); - var digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states - .SetState(context.Signer, MarkChanged) - .SetState(itemAddress, MarkChanged) - .SetState(digestListAddress, MarkChanged) - .SetState(shopAddress, MarkChanged) - .SetState(updateSellShopAddress, MarkChanged) - .SetState(updateSellOrderAddress, MarkChanged) - .SetState(inventoryAddress, MarkChanged) - .SetState(worldInformationAddress, MarkChanged) - .SetState(questListAddress, MarkChanged) - .SetState(sellerAvatarAddress, MarkChanged); - } - - CheckObsolete(ActionObsoleteConfig.V100270ObsoleteIndex, context); - - // common - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex} updateSell exec started", addressesHex); - - if (price.Sign < 0) - { - throw new InvalidPriceException( - $"{addressesHex} Aborted as the price is less than zero: {price}."); - } - - if (!states.TryGetAvatarStateV2(context.Signer, sellerAvatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex} Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - - Log.Verbose("{AddressesHex} Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!avatarState.worldInformation.IsStageCleared( - GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - sw.Stop(); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - if (!states.TryGetState(digestListAddress, out Dictionary rawList)) - { - throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(OrderDigest)}({digestListAddress})."); - } - var digestList = new OrderDigestListState(rawList); - - // migration method - avatarState.inventory.UnlockInvalidSlot(digestList, context.Signer, sellerAvatarAddress); - avatarState.inventory.ReconfigureFungibleItem(digestList, tradableId); - avatarState.inventory.LockByReferringToDigestList(digestList, tradableId, context.BlockIndex); - // - - // for sell cancel - Log.Verbose("{AddressesHex} UpdateSell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!states.TryGetState(shopAddress, out BxDictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shopAddress})."); - } - sw.Stop(); - - Log.Verbose("{AddressesHex} UpdateSell Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!states.TryGetState(Order.DeriveAddress(orderId), out Dictionary orderDict)) - { - throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(Order)}({Order.DeriveAddress(orderId)})."); - } - - var orderOnSale = OrderFactory.Deserialize(orderDict); - orderOnSale.ValidateCancelOrder(avatarState, tradableId); - var itemOnSale = orderOnSale.Cancel(avatarState, context.BlockIndex); - if (context.BlockIndex < orderOnSale.ExpiredBlockIndex) - { - var shardedShopState = new ShardedShopStateV2(shopStateDict); - shardedShopState.Remove(orderOnSale, context.BlockIndex); - states = states.SetState(shopAddress, shardedShopState.Serialize()); - } - - digestList.Remove(orderOnSale.OrderId); - states = states.SetState(itemAddress, itemOnSale.Serialize()) - .SetState(digestListAddress, digestList.Serialize()); - sw.Stop(); - - var expirationMail = avatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - avatarState.mailBox.Remove(expirationMail); - } - - // for updateSell - var updateSellShopState = states.TryGetState(updateSellShopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(updateSellShopAddress); - - Log.Verbose("{AddressesHex} UpdateSell Get ShardedShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var newOrder = OrderFactory.Create(context.Signer, sellerAvatarAddress, updateSellOrderId, price, tradableId, - context.BlockIndex, itemSubType, count); - newOrder.Validate(avatarState, count); - - var tradableItem = newOrder.Sell4(avatarState); - var costumeStatSheet = states.GetSheet(); - var orderDigest = newOrder.Digest(avatarState, costumeStatSheet); - updateSellShopState.Add(orderDigest, context.BlockIndex); - - digestList.Add(orderDigest); - states = states.SetState(digestListAddress, digestList.Serialize()); - states = states.SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()); - sw.Stop(); - - Log.Verbose("{AddressesHex} UpdateSell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(updateSellOrderAddress, newOrder.Serialize()) - .SetState(updateSellShopAddress, updateSellShopState.Serialize()); - sw.Stop(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex} UpdateSell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - Log.Verbose("{AddressesHex} UpdateSell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/UpdateSell3.cs b/Lib9c/Action/UpdateSell3.cs deleted file mode 100644 index eb524d0233..0000000000 --- a/Lib9c/Action/UpdateSell3.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; -using BxDictionary = Bencodex.Types.Dictionary; -using BxList = Bencodex.Types.List; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1022 - /// Updated at https://github.com/planetarium/lib9c/pull/1022 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("update_sell3")] - public class UpdateSell3 : GameAction, IUpdateSellV2 - { - public Address sellerAvatarAddress; - public IEnumerable updateSellInfos; - - Address IUpdateSellV2.SellerAvatarAddress => sellerAvatarAddress; - IEnumerable IUpdateSellV2.UpdateSellInfos => - updateSellInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [UpdateSellInfoKey] = updateSellInfos.Select(info => info.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - updateSellInfos = plainValue[UpdateSellInfoKey] - .ToEnumerable(info => new UpdateSellInfo((List)info)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - var digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100320ObsoleteIndex, context); - - // common - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex} updateSell exec started", addressesHex); - - if (!updateSellInfos.Any()) - { - throw new ListEmptyException($"{addressesHex} List - UpdateSell infos was empty."); - } - if (!states.TryGetAvatarStateV2(context.Signer, sellerAvatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex} Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex} Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var costumeStatSheet = states.GetSheet(); - - if (!states.TryGetState(digestListAddress, out Dictionary rawList)) - { - throw new FailedLoadStateException( - $"{addressesHex} failed to load {nameof(OrderDigest)}({digestListAddress})."); - } - var digestList = new OrderDigestListState(rawList); - - foreach (var updateSellInfo in updateSellInfos) - { - if (updateSellInfo.price.Sign < 0) - { - throw new InvalidPriceException($"{addressesHex} Aborted as the price is less than zero: {updateSellInfo.price}."); - } - - var shopAddress = ShardedShopStateV2.DeriveAddress(updateSellInfo.itemSubType, updateSellInfo.orderId); - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(updateSellInfo.itemSubType, updateSellInfo.updateSellOrderId); - var updateSellOrderAddress = Order.DeriveAddress(updateSellInfo.updateSellOrderId); - var itemAddress = Addresses.GetItemAddress(updateSellInfo.tradableId); - - // migration method - avatarState.inventory.UnlockInvalidSlot(digestList, context.Signer, sellerAvatarAddress); - avatarState.inventory.ReconfigureFungibleItem(digestList, updateSellInfo.tradableId); - avatarState.inventory.LockByReferringToDigestList(digestList, updateSellInfo.tradableId, - context.BlockIndex); - - // for sell cancel - sw.Restart(); - if (!states.TryGetState(shopAddress, out BxDictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shopAddress})."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!states.TryGetState(Order.DeriveAddress(updateSellInfo.orderId), out Dictionary orderDict)) - { - throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(Order)}({Order.DeriveAddress(updateSellInfo.orderId)})."); - } - - var orderOnSale = OrderFactory.Deserialize(orderDict); - orderOnSale.ValidateCancelOrder(avatarState, updateSellInfo.tradableId); - orderOnSale.Cancel(avatarState, context.BlockIndex); - if (context.BlockIndex < orderOnSale.ExpiredBlockIndex) - { - var shardedShopState = new ShardedShopStateV2(shopStateDict); - shardedShopState.Remove(orderOnSale, context.BlockIndex); - states = states.SetState(shopAddress, shardedShopState.Serialize()); - } - - digestList.Remove(orderOnSale.OrderId); - sw.Stop(); - - var expirationMail = avatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(updateSellInfo.orderId)); - if (!(expirationMail is null)) - { - avatarState.mailBox.Remove(expirationMail); - } - - // for updateSell - var updateSellShopState = - states.TryGetState(updateSellShopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(updateSellShopAddress); - - Log.Verbose("{AddressesHex} UpdateSell Get ShardedShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var newOrder = OrderFactory.Create( - context.Signer, - sellerAvatarAddress, - updateSellInfo.updateSellOrderId, - updateSellInfo.price, - updateSellInfo.tradableId, - context.BlockIndex, - updateSellInfo.itemSubType, - updateSellInfo.count - ); - - newOrder.Validate(avatarState, updateSellInfo.count); - - var tradableItem = newOrder.Sell4(avatarState); - var orderDigest = newOrder.Digest(avatarState, costumeStatSheet); - updateSellShopState.Add(orderDigest, context.BlockIndex); - - digestList.Add(orderDigest); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(updateSellOrderAddress, newOrder.Serialize()) - .SetState(updateSellShopAddress, updateSellShopState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - states = states.SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()) - .SetState(digestListAddress, digestList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex} UpdateSell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/UpdateSell4.cs b/Lib9c/Action/UpdateSell4.cs deleted file mode 100644 index 36ca37c709..0000000000 --- a/Lib9c/Action/UpdateSell4.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Libplanet.Types.Assets; -using Nekoyume.Battle; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; -using BxDictionary = Bencodex.Types.Dictionary; -using BxList = Bencodex.Types.List; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1022 - /// Updated at https://github.com/planetarium/lib9c/pull/1022 - /// - [Serializable] - [ActionType("update_sell4")] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - public class UpdateSell4 : GameAction, IUpdateSellV2 - { - private const int UpdateCapacity = 100; - public Address sellerAvatarAddress; - public IEnumerable updateSellInfos; - - Address IUpdateSellV2.SellerAvatarAddress => sellerAvatarAddress; - IEnumerable IUpdateSellV2.UpdateSellInfos => - updateSellInfos.Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [UpdateSellInfoKey] = updateSellInfos.Select(info => info.Serialize()).Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - updateSellInfos = plainValue[UpdateSellInfoKey] - .ToEnumerable(info => new UpdateSellInfo((List)info)); - } - - public override IAccountStateDelta Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = sellerAvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = sellerAvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = sellerAvatarAddress.Derive(LegacyQuestListKey); - var digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - if (context.Rehearsal) - { - return states; - } - - CheckObsolete(ActionObsoleteConfig.V100351ObsoleteIndex, context); - - if (updateSellInfos.Count() > UpdateCapacity) - { - throw new ArgumentOutOfRangeException($"{nameof(updateSellInfos)} must be less than or equal 100."); - } - // common - var addressesHex = GetSignerAndOtherAddressesHex(context, sellerAvatarAddress); - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex} updateSell exec started", addressesHex); - - if (!updateSellInfos.Any()) - { - throw new ListEmptyException($"{addressesHex} List - UpdateSell infos was empty."); - } - if (!states.TryGetAvatarStateV2(context.Signer, sellerAvatarAddress, out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"{addressesHex} Aborted as the avatar state of the signer was failed to load."); - } - sw.Stop(); - Log.Verbose("{AddressesHex} Sell Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.ActionsInShop, - current); - } - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell IsStageCleared: {Elapsed}", addressesHex, sw.Elapsed); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - var costumeStatSheet = states.GetSheet(); - - if (!states.TryGetState(digestListAddress, out Dictionary rawList)) - { - throw new FailedLoadStateException( - $"{addressesHex} failed to load {nameof(OrderDigest)}({digestListAddress})."); - } - var digestList = new OrderDigestListState(rawList); - - foreach (var updateSellInfo in updateSellInfos) - { - if (updateSellInfo.price.Sign < 0) - { - throw new InvalidPriceException($"{addressesHex} Aborted as the price is less than zero: {updateSellInfo.price}."); - } - - var shopAddress = ShardedShopStateV2.DeriveAddress(updateSellInfo.itemSubType, updateSellInfo.orderId); - var updateSellShopAddress = ShardedShopStateV2.DeriveAddress(updateSellInfo.itemSubType, updateSellInfo.updateSellOrderId); - var updateSellOrderAddress = Order.DeriveAddress(updateSellInfo.updateSellOrderId); - var itemAddress = Addresses.GetItemAddress(updateSellInfo.tradableId); - - // migration method - avatarState.inventory.UnlockInvalidSlot(digestList, context.Signer, sellerAvatarAddress); - avatarState.inventory.ReconfigureFungibleItem(digestList, updateSellInfo.tradableId); - avatarState.inventory.LockByReferringToDigestList(digestList, updateSellInfo.tradableId, - context.BlockIndex); - - // for sell cancel - sw.Restart(); - if (!states.TryGetState(shopAddress, out BxDictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shopAddress})."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - if (!states.TryGetState(Order.DeriveAddress(updateSellInfo.orderId), out Dictionary orderDict)) - { - throw new FailedLoadStateException($"{addressesHex} failed to load {nameof(Order)}({Order.DeriveAddress(updateSellInfo.orderId)})."); - } - - var orderOnSale = OrderFactory.Deserialize(orderDict); - orderOnSale.ValidateCancelOrder(avatarState, updateSellInfo.tradableId); - orderOnSale.Cancel(avatarState, context.BlockIndex); - if (context.BlockIndex < orderOnSale.ExpiredBlockIndex) - { - var shardedShopState = new ShardedShopStateV2(shopStateDict); - shardedShopState.Remove(orderOnSale, context.BlockIndex); - states = states.SetState(shopAddress, shardedShopState.Serialize()); - } - - digestList.Remove(orderOnSale.OrderId); - sw.Stop(); - - var expirationMail = avatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(updateSellInfo.orderId)); - if (!(expirationMail is null)) - { - avatarState.mailBox.Remove(expirationMail); - } - - // for updateSell - var updateSellShopState = - states.TryGetState(updateSellShopAddress, out Dictionary serializedState) - ? new ShardedShopStateV2(serializedState) - : new ShardedShopStateV2(updateSellShopAddress); - - Log.Verbose("{AddressesHex} UpdateSell Get ShardedShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - var newOrder = OrderFactory.Create( - context.Signer, - sellerAvatarAddress, - updateSellInfo.updateSellOrderId, - updateSellInfo.price, - updateSellInfo.tradableId, - context.BlockIndex, - updateSellInfo.itemSubType, - updateSellInfo.count - ); - - newOrder.Validate(avatarState, updateSellInfo.count); - - var tradableItem = newOrder.Sell4(avatarState); - var orderDigest = newOrder.Digest(avatarState, costumeStatSheet); - updateSellShopState.Add(orderDigest, context.BlockIndex); - - digestList.Add(orderDigest); - - states = states - .SetState(itemAddress, tradableItem.Serialize()) - .SetState(updateSellOrderAddress, newOrder.Serialize()) - .SetState(updateSellShopAddress, updateSellShopState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - sw.Restart(); - states = states.SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(sellerAvatarAddress, avatarState.SerializeV2()) - .SetState(digestListAddress, digestList.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex} UpdateSell Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex} UpdateSell Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Model/State/StateExtensions.cs b/Lib9c/Model/State/StateExtensions.cs index bfd5697c14..6db839b195 100644 --- a/Lib9c/Model/State/StateExtensions.cs +++ b/Lib9c/Model/State/StateExtensions.cs @@ -453,15 +453,9 @@ serialized is Bencodex.Types.List serializedList public static PurchaseInfo0 ToPurchaseInfo(this IValue serialized) => new PurchaseInfo0((Dictionary) serialized); - public static BuyMultiple.PurchaseInfo ToPurchaseInfoLegacy(this IValue serialized) => - new BuyMultiple.PurchaseInfo((Dictionary) serialized); - public static Buy7.PurchaseResult ToPurchaseResult(this IValue serialized) => new Buy7.PurchaseResult((Dictionary) serialized); - public static BuyMultiple.PurchaseResult ToPurchaseResultLegacy(this IValue serialized) => - new BuyMultiple.PurchaseResult((Dictionary) serialized); - public static Buy7.SellerResult ToSellerResult(this IValue serialized) => new Buy7.SellerResult((Dictionary) serialized);