diff --git a/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs b/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs index 3c9a2756dd..2017ffd3c7 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 f233512f74..e978a56b48 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/BattleArena10Test.cs b/.Lib9c.Tests/Action/BattleArena10Test.cs deleted file mode 100644 index d44b77aed8..0000000000 --- a/.Lib9c.Tests/Action/BattleArena10Test.cs +++ /dev/null @@ -1,1337 +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 IAccount _initialStates; - - public BattleArena10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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 f5441e588f..0000000000 --- a/.Lib9c.Tests/Action/BattleArena11Test.cs +++ /dev/null @@ -1,1337 +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 IAccount _initialStates; - - public BattleArena11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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/BattleArena12Test.cs b/.Lib9c.Tests/Action/BattleArena12Test.cs deleted file mode 100644 index 3061b5f790..0000000000 --- a/.Lib9c.Tests/Action/BattleArena12Test.cs +++ /dev/null @@ -1,1376 +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 BattleArena12Test - { - 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 IAccount _initialStates; - - public BattleArena12Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena12 - { - 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, - RandomSeed = 0, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena12 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 0, - round = 1, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena12 - { - 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, - RandomSeed = 0, - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena12 - { - 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, - RandomSeed = 0, - 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 BattleArena12 - { - 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, - RandomSeed = 0, - 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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena.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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena12 - { - 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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - var action = new BattleArena12 - { - 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, - RandomSeed = random.Seed, - 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(BattleArena.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 BattleArena12 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_Throw_ActionObsoleteException() - { - var action = new BattleArena12 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - championshipId = 6, - round = 2, - ticket = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - }; - - // Unavailable round - Assert.Throws(() => action.Execute(new ActionContext - { - Signer = _agent1Address, - BlockIndex = 1L, - })); - - action.championshipId = 7; - action.round = 1; - // Unavailable championship - Assert.Throws(() => action.Execute(new ActionContext - { - Signer = _agent1Address, - BlockIndex = 1L, - })); - - // Unavailable block index - Assert.Throws(() => action.Execute(new ActionContext - { - Signer = _agent1Address, - BlockIndex = 7_716_401L, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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 BattleArena12 - { - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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/BattleArena13Test.cs b/.Lib9c.Tests/Action/BattleArena13Test.cs deleted file mode 100644 index 37ca76d60d..0000000000 --- a/.Lib9c.Tests/Action/BattleArena13Test.cs +++ /dev/null @@ -1,1337 +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 BattleArena13Test - { - 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 IAccount _initialStates; - - public BattleArena13Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new BattleArena13 - { - 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, - RandomSeed = 0, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena13 - { - 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, - RandomSeed = 0, - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena13 - { - 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, - RandomSeed = 0, - 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 BattleArena13 - { - 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, - RandomSeed = 0, - 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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena13.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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - [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 BattleArena13 - { - 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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - var action = new BattleArena13 - { - 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, - RandomSeed = random.Seed, - 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(BattleArena13.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 BattleArena13 - { - 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, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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 BattleArena13 - { - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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/BattleArena14Test.cs b/.Lib9c.Tests/Action/BattleArena14Test.cs deleted file mode 100644 index 2f80c4a071..0000000000 --- a/.Lib9c.Tests/Action/BattleArena14Test.cs +++ /dev/null @@ -1,1312 +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 BattleArena14Test - { - 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 IAccount _initialStates; - - public BattleArena14Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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/1419 - _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().Address; - 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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var action = new BattleArena14 - { - 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, - RandomSeed = 0, - BlockIndex = 4480001, - })); - } - - [Fact] - public void Execute_ArenaParticipantsNotFoundException() - { - var action = new BattleArena14 - { - 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, - RandomSeed = 0, - 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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = 0, - 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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena14)}] 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(BattleArena14.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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - [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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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(BattleArena14)}] 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, - RandomSeed = 0, - }); - - var action = new BattleArena14 - { - 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, - RandomSeed = random.Seed, - 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(BattleArena14)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - if (roundData.ArenaType != ArenaType.OffSeason) - { - throw new InvalidSeasonException($"[{nameof(BattleArena14)}] 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(BattleArena14.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 BattleArena14 - { - 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, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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(BattleArena14)}] 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 BattleArena14 - { - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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 391e8d9cc0..0000000000 --- a/.Lib9c.Tests/Action/BattleArena1Test.cs +++ /dev/null @@ -1,601 +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 IAccount _state; - - public BattleArena1Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new Account(MockState.Empty); - - _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().Address; - 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().Address; - 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 IAccount 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, - RandomSeed = random.Seed, - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena2Test.cs b/.Lib9c.Tests/Action/BattleArena2Test.cs deleted file mode 100644 index 601e9748a9..0000000000 --- a/.Lib9c.Tests/Action/BattleArena2Test.cs +++ /dev/null @@ -1,794 +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 IAccount _state; - - public BattleArena2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new Account(MockState.Empty); - - _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().Address; - 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().Address; - 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 IAccount 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, - RandomSeed = random.Seed, - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena3Test.cs b/.Lib9c.Tests/Action/BattleArena3Test.cs deleted file mode 100644 index a1f844053f..0000000000 --- a/.Lib9c.Tests/Action/BattleArena3Test.cs +++ /dev/null @@ -1,794 +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 IAccount _state; - - public BattleArena3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new Account(MockState.Empty); - - _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().Address; - 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().Address; - 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 IAccount 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, - RandomSeed = random.Seed, - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleArena4Test.cs b/.Lib9c.Tests/Action/BattleArena4Test.cs deleted file mode 100644 index acbfc0f972..0000000000 --- a/.Lib9c.Tests/Action/BattleArena4Test.cs +++ /dev/null @@ -1,985 +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 IAccount _state; - - public BattleArena4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new Account(MockState.Empty); - - _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().Address; - 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().Address; - 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 IAccount 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, - RandomSeed = random.Seed, - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = blockIndex + 1, - PreviousState = newState, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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.SetNull(Addresses.TableSheet.Derive(key)); - } - } - - 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, - RandomSeed = random.Seed, - 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 2cf996f50a..0000000000 --- a/.Lib9c.Tests/Action/BattleArena5Test.cs +++ /dev/null @@ -1,1029 +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 IAccount _initialStates; - - public BattleArena5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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 41ee8306ae..0000000000 --- a/.Lib9c.Tests/Action/BattleArena6Test.cs +++ /dev/null @@ -1,1115 +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 IAccount _initialStates; - - public BattleArena6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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 f1e6b41d2b..0000000000 --- a/.Lib9c.Tests/Action/BattleArena7Test.cs +++ /dev/null @@ -1,1128 +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 IAccount _initialStates; - - public BattleArena7Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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 56fe5f5532..0000000000 --- a/.Lib9c.Tests/Action/BattleArena8Test.cs +++ /dev/null @@ -1,1337 +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 IAccount _initialStates; - - public BattleArena8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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 b840f76f5a..0000000000 --- a/.Lib9c.Tests/Action/BattleArena9Test.cs +++ /dev/null @@ -1,1337 +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 IAccount _initialStates; - - public BattleArena9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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().Address; - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = blockIndex + 1, - PreviousState = nextStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = random.Seed, - 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, - RandomSeed = 0, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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, - RandomSeed = random.Seed, - 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 IAccount JoinArena( - IActionContext context, - IAccount 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, - RandomSeed = random.Seed, - 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/BattleGrandFinale1Test.cs b/.Lib9c.Tests/Action/BattleGrandFinale1Test.cs deleted file mode 100644 index d1c808f920..0000000000 --- a/.Lib9c.Tests/Action/BattleGrandFinale1Test.cs +++ /dev/null @@ -1,449 +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 Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Exceptions; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.GrandFinale; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Nekoyume.TableData.GrandFinale; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleGrandFinale1Test - { - private readonly int _validSeason; - 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 IAccount _initialStates; - - public BattleGrandFinale1Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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 - var ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(ncg); - - var rankingMapAddress = new PrivateKey().Address; - 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; - var arenaAvatarState1 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar1Address), - avatar1State); - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - var arenaAvatarState2 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar2Address), - avatar2State); - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - var arenaAvatarState3 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar3Address), - avatar3State); - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - var arenaAvatarState4 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar4Address), - avatar4State); - - // update GrandFinaleParticipantsSheet - _validSeason = _tableSheets.GrandFinaleParticipantsSheet.First!.Key; - var participantsSheetCsv = _sheets[nameof(GrandFinaleParticipantsSheet)]; - participantsSheetCsv = - new[] { _avatar1Address, _avatar2Address, _avatar3Address, _avatar4Address, } - .Aggregate( - participantsSheetCsv, - (current, avatarAddr) => - current + $"{_validSeason},{avatarAddr.ToString()}\n"); - _tableSheets.GrandFinaleParticipantsSheet.Set(participantsSheetCsv); - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState( - Addresses.GetSheetAddress(), - _tableSheets.GrandFinaleParticipantsSheet.Serialize()) - .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(arenaAvatarState1.Address, arenaAvatarState1.Serialize()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(arenaAvatarState2.Address, arenaAvatarState2.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(arenaAvatarState3.Address, arenaAvatarState3.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(arenaAvatarState4.Address, arenaAvatarState4.Serialize()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 2, true)] - [InlineData(1, 2, false)] - public void Execute_Success( - long nextBlockIndex, - int randomSeed, - bool win) - { - Execute( - nextBlockIndex, - _validSeason, - randomSeed, - _avatar1Address, - _avatar2Address, - win); - } - - [Fact] - public void Execute_AlreadyFoughtAvatarException() - { - var action = new BattleGrandFinale1 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - var next = action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = 1, - }); - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = next, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = 2, - })); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleGrandFinale1 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleGrandFinale1 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleGrandFinale1 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - grandFinaleId = 9999999, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var blockIndex = - _tableSheets.GrandFinaleScheduleSheet.GetRowByBlockIndex(0)?.StartBlockIndex - 1 ?? - 0; - var action = new BattleGrandFinale1 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int grandFinaleId = 1; - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - grandFinaleId, - out var row)); - - var (tempAgent, tempAvatar) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - new PrivateKey().Address, - 1); - var myAvatar = - excludeMe ? previousStates.GetAvatarStateV2(_avatar1Address) : tempAvatar; - var (enemyAgent, enemyAvatar) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - new PrivateKey().Address, - 1); - previousStates = previousStates - .SetState(tempAgent.address, tempAgent.Serialize()) - .SetState(enemyAgent.address, enemyAgent.Serialize()) - .SetState(myAvatar.address, myAvatar.Serialize()) - .SetState(enemyAvatar.address, enemyAvatar.Serialize()); - - var action = new BattleGrandFinale1 - { - myAvatarAddress = myAvatar.address, - enemyAvatarAddress = enemyAvatar.address, - grandFinaleId = grandFinaleId, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAvatar.agentAddress, - RandomSeed = 0, - BlockIndex = row.StartBlockIndex, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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 blockIndex, int grandFinaleId, int randomSeed, Address myAvatarAddr, Address enemyAvatarAddr, bool setToWin) - { - var states = _initialStates; - if (!states.TryGetArenaAvatarState( - ArenaAvatarState.DeriveAddress(myAvatarAddr), - out var myArenaAvatar)) - { - throw new ArenaAvatarStateNotFoundException( - $"my avatar not has arena avatar state...: {myAvatarAddr}"); - } - - var myAvatar = states.GetAvatarState(myAvatarAddr); - var myAgentAddr = myAvatar.agentAddress; - var participantsCount = states - .GetSheet()[grandFinaleId] - .Participants.Count; - if (setToWin) - { - myAvatar.level = 100; - states = states.SetState(myAvatar.address, myAvatar.SerializeV2()) - .SetState(myArenaAvatar.Address, new ArenaAvatarState(myAvatar).Serialize()); - } - else - { - var enemyAvatar = states.GetAvatarState(enemyAvatarAddr); - enemyAvatar.level = 100; - states = states.SetState(enemyAvatar.address, enemyAvatar.SerializeV2()) - .SetState( - ArenaAvatarState.DeriveAddress(enemyAvatar.address), - new ArenaAvatarState(enemyAvatar).Serialize()); - } - - var action = new BattleGrandFinale1 - { - myAvatarAddress = myAvatarAddr, - enemyAvatarAddress = enemyAvatarAddr, - grandFinaleId = grandFinaleId, - costumes = new List(), - equipments = new List(), - }; - var nextStates = action.Execute(new ActionContext - { - Signer = myAgentAddr, - BlockIndex = blockIndex, - PreviousState = states, - RandomSeed = randomSeed, - }); - Assert.True(nextStates.TryGetState( - myAvatarAddr.Derive( - string.Format( - CultureInfo.InvariantCulture, - BattleGrandFinale.ScoreDeriveKey, - grandFinaleId)), - out var myScore)); - Assert.Equal(setToWin ? 1020 : 1001, myScore); - Assert.True(nextStates.TryGetState( - GrandFinaleInformation.DeriveAddress( - myAvatarAddr, - grandFinaleId), - out var serialized)); - var nextInformation = new GrandFinaleInformation(serialized); - Assert.True(nextInformation.TryGetBattleRecord(enemyAvatarAddr, out var win)); - Assert.Equal(setToWin, win); - } - } -} diff --git a/.Lib9c.Tests/Action/BattleGrandFinale2Test.cs b/.Lib9c.Tests/Action/BattleGrandFinale2Test.cs deleted file mode 100644 index 2f3f133e14..0000000000 --- a/.Lib9c.Tests/Action/BattleGrandFinale2Test.cs +++ /dev/null @@ -1,449 +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 Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Exceptions; - using Nekoyume.Model; - using Nekoyume.Model.Arena; - using Nekoyume.Model.GrandFinale; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Nekoyume.TableData.GrandFinale; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class BattleGrandFinale2Test - { - private readonly int _validSeason; - 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 IAccount _initialStates; - - public BattleGrandFinale2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialStates = new Account(MockState.Empty); - - _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 - var ncg = Currency.Legacy("NCG", 2, null); -#pragma warning restore CS0618 - var goldCurrencyState = new GoldCurrencyState(ncg); - - var rankingMapAddress = new PrivateKey().Address; - 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; - var arenaAvatarState1 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar1Address), - avatar1State); - - // account 2 - var (agent2State, avatar2State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - clearStageId); - _agent2Address = agent2State.address; - _avatar2Address = avatar2State.address; - var arenaAvatarState2 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar2Address), - avatar2State); - - // account 3 - var (agent3State, avatar3State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - _agent3Address = agent3State.address; - _avatar3Address = avatar3State.address; - var arenaAvatarState3 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar3Address), - avatar3State); - - // account 4 - var (agent4State, avatar4State) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - rankingMapAddress, - 1); - var arenaAvatarState4 = - _initialStates.GetArenaAvatarState( - ArenaAvatarState.DeriveAddress(_avatar4Address), - avatar4State); - - // update GrandFinaleParticipantsSheet - _validSeason = _tableSheets.GrandFinaleParticipantsSheet.First!.Key; - var participantsSheetCsv = _sheets[nameof(GrandFinaleParticipantsSheet)]; - participantsSheetCsv = - new[] { _avatar1Address, _avatar2Address, _avatar3Address, _avatar4Address, } - .Aggregate( - participantsSheetCsv, - (current, avatarAddr) => - current + $"{_validSeason},{avatarAddr.ToString()}\n"); - _tableSheets.GrandFinaleParticipantsSheet.Set(participantsSheetCsv); - _agent4Address = agent4State.address; - _avatar4Address = avatar4State.address; - - _initialStates = _initialStates - .SetState( - Addresses.GetSheetAddress(), - _tableSheets.GrandFinaleParticipantsSheet.Serialize()) - .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(arenaAvatarState1.Address, arenaAvatarState1.Serialize()) - .SetState(_agent2Address, agent2State.Serialize()) - .SetState(_avatar2Address, avatar2State.Serialize()) - .SetState(arenaAvatarState2.Address, arenaAvatarState2.Serialize()) - .SetState(_agent3Address, agent3State.Serialize()) - .SetState(_avatar3Address, avatar3State.Serialize()) - .SetState(arenaAvatarState3.Address, arenaAvatarState3.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(arenaAvatarState4.Address, arenaAvatarState4.Serialize()) - .SetState( - Addresses.GameConfig, - new GameConfigState(_sheets[nameof(GameConfigSheet)]).Serialize()); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - } - - [Theory] - [InlineData(1, 2, true)] - [InlineData(1, 2, false)] - public void Execute_Success( - long nextBlockIndex, - int randomSeed, - bool win) - { - Execute( - nextBlockIndex, - _validSeason, - randomSeed, - _avatar1Address, - _avatar2Address, - win); - } - - [Fact] - public void Execute_AlreadyFoughtAvatarException() - { - var action = new BattleGrandFinale2 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - var next = action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = 1, - }); - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = next, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = 2, - })); - } - - [Fact] - public void Execute_InvalidAddressException() - { - var action = new BattleGrandFinale2 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar1Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_FailedLoadStateException() - { - var action = new BattleGrandFinale2 - { - myAvatarAddress = _avatar2Address, - enemyAvatarAddress = _avatar1Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_SheetRowNotFoundException() - { - var action = new BattleGrandFinale2 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - grandFinaleId = 9999999, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_ThisArenaIsClosedException() - { - var blockIndex = - _tableSheets.GrandFinaleScheduleSheet.GetRowByBlockIndex(0)?.StartBlockIndex - 1 ?? - 0; - var action = new BattleGrandFinale2 - { - myAvatarAddress = _avatar1Address, - enemyAvatarAddress = _avatar2Address, - grandFinaleId = 1, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialStates, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_AddressNotFoundInArenaParticipantsException(bool excludeMe) - { - const int grandFinaleId = 1; - var previousStates = _initialStates; - Assert.True(previousStates.GetSheet().TryGetValue( - grandFinaleId, - out var row)); - - var (tempAgent, tempAvatar) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - new PrivateKey().Address, - 1); - var myAvatar = - excludeMe ? previousStates.GetAvatarStateV2(_avatar1Address) : tempAvatar; - var (enemyAgent, enemyAvatar) = GetAgentStateWithAvatarState( - _sheets, - _tableSheets, - new PrivateKey().Address, - 1); - previousStates = previousStates - .SetState(tempAgent.address, tempAgent.Serialize()) - .SetState(enemyAgent.address, enemyAgent.Serialize()) - .SetState(myAvatar.address, myAvatar.Serialize()) - .SetState(enemyAvatar.address, enemyAvatar.Serialize()); - - var action = new BattleGrandFinale2 - { - myAvatarAddress = myAvatar.address, - enemyAvatarAddress = enemyAvatar.address, - grandFinaleId = grandFinaleId, - costumes = new List(), - equipments = new List(), - }; - - Assert.Throws(() => - action.Execute(new ActionContext - { - PreviousState = previousStates, - Signer = myAvatar.agentAddress, - RandomSeed = 0, - BlockIndex = row.StartBlockIndex, - })); - } - - private static (AgentState AgentState, AvatarState AvatarState) GetAgentStateWithAvatarState( - IReadOnlyDictionary sheets, - TableSheets tableSheets, - Address rankingMapAddress, - int clearStageId) - { - var agentAddress = new PrivateKey().Address; - 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 blockIndex, int grandFinaleId, int randomSeed, Address myAvatarAddr, Address enemyAvatarAddr, bool setToWin) - { - var states = _initialStates; - if (!states.TryGetArenaAvatarState( - ArenaAvatarState.DeriveAddress(myAvatarAddr), - out var myArenaAvatar)) - { - throw new ArenaAvatarStateNotFoundException( - $"my avatar not has arena avatar state...: {myAvatarAddr}"); - } - - var myAvatar = states.GetAvatarState(myAvatarAddr); - var myAgentAddr = myAvatar.agentAddress; - var participantsCount = states - .GetSheet()[grandFinaleId] - .Participants.Count; - if (setToWin) - { - myAvatar.level = 100; - states = states.SetState(myAvatar.address, myAvatar.SerializeV2()) - .SetState(myArenaAvatar.Address, new ArenaAvatarState(myAvatar).Serialize()); - } - else - { - var enemyAvatar = states.GetAvatarState(enemyAvatarAddr); - enemyAvatar.level = 100; - states = states.SetState(enemyAvatar.address, enemyAvatar.SerializeV2()) - .SetState( - ArenaAvatarState.DeriveAddress(enemyAvatar.address), - new ArenaAvatarState(enemyAvatar).Serialize()); - } - - var action = new BattleGrandFinale2 - { - myAvatarAddress = myAvatarAddr, - enemyAvatarAddress = enemyAvatarAddr, - grandFinaleId = grandFinaleId, - costumes = new List(), - equipments = new List(), - }; - var nextStates = action.Execute(new ActionContext - { - Signer = myAgentAddr, - BlockIndex = blockIndex, - PreviousState = states, - RandomSeed = randomSeed, - }); - Assert.True(nextStates.TryGetState( - myAvatarAddr.Derive( - string.Format( - CultureInfo.InvariantCulture, - BattleGrandFinale2.ScoreDeriveKey, - grandFinaleId)), - out var myScore)); - Assert.Equal(setToWin ? 1020 : 1001, myScore); - Assert.True(nextStates.TryGetState( - GrandFinaleInformation.DeriveAddress( - myAvatarAddr, - grandFinaleId), - out var serialized)); - var nextInformation = new GrandFinaleInformation(serialized); - Assert.True(nextInformation.TryGetBattleRecord(enemyAvatarAddr, out var win)); - Assert.Equal(setToWin, win); - } - } -} diff --git a/.Lib9c.Tests/Action/Buy10Test.cs b/.Lib9c.Tests/Action/Buy10Test.cs deleted file mode 100644 index 81fcac1da2..0000000000 --- a/.Lib9c.Tests/Action/Buy10Test.cs +++ /dev/null @@ -1,1089 +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 IAccount _initialState; - - public Buy10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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 = (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 }, - }; - - IAccount nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 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, - RandomSeed = 0, - 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().Address; - - 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 1928b096a4..0000000000 --- a/.Lib9c.Tests/Action/Buy11Test.cs +++ /dev/null @@ -1,1146 +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 IAccount _initialState; - private IValue _arenaSheetState; - - public Buy11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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.SetNull(arenaSheetAddress); - } - - public static IEnumerable GetExecuteMemberData() - { - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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 = (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 }, - }; - - IAccount nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 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.SetNull(arenaSheetAddress); - - nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 100, - PreviousState = nextState, - RandomSeed = 0, - 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, - RandomSeed = 0, - Signer = result.GetAgentState().address, - }); - }); - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().Address; - - 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 49c4908953..0000000000 --- a/.Lib9c.Tests/Action/Buy2Test.cs +++ /dev/null @@ -1,177 +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 IAccount _initialState; - - public Buy2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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), - (ITradableItem)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, - RandomSeed = 0, - 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 e7b0af1009..0000000000 --- a/.Lib9c.Tests/Action/Buy3Test.cs +++ /dev/null @@ -1,320 +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 IAccount _initialState; - - public Buy3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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), - (ITradableItem)equipment)); - - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - (ITradableItem)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, - RandomSeed = 0, - 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 Account(MockState.Empty), - RandomSeed = 0, - 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 Account(MockState.Empty), - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - Signer = _buyerAgentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/Buy4Test.cs b/.Lib9c.Tests/Action/Buy4Test.cs deleted file mode 100644 index 6c6d43cb2b..0000000000 --- a/.Lib9c.Tests/Action/Buy4Test.cs +++ /dev/null @@ -1,344 +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 IAccount _initialState; - - public Buy4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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), - (ITradableItem)equipment)); - - shopState.Register(new ShopItem( - _sellerAgentAddress, - _sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(_goldCurrencyState.Currency, ProductPrice, 0), - (ITradableItem)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, - RandomSeed = 0, - 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 Account(MockState.Empty), - RandomSeed = 0, - 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 Account(MockState.Empty), - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - Signer = _buyerAgentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/Buy5Test.cs b/.Lib9c.Tests/Action/Buy5Test.cs deleted file mode 100644 index b6046fb124..0000000000 --- a/.Lib9c.Tests/Action/Buy5Test.cs +++ /dev/null @@ -1,639 +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 Serilog; - using Xunit; - using Xunit.Abstractions; - - public class Buy5Test - { - 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 IAccount _initialState; - - public Buy5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ContainsInInventory = true, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 20, - ContainsInInventory = false, - }, - }; - yield return new object[] - { - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ContainsInInventory = false, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 50, - ContainsInInventory = true, - }, - }; - } - - [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 - ); - INonFungibleItem nonFungibleItem; - Guid productId = shopItemData.ItemId; - long requiredBlockIndex = shopItemData.RequiredBlockIndex; - ItemSubType itemSubType; - if (shopItemData.ItemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - productId, - requiredBlockIndex); - nonFungibleItem = itemUsable; - itemSubType = itemUsable.ItemSubType; - } - else - { - var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.First, productId); - 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); - 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, - (ITradableItem)nonFungibleItem); - - // Case for backward compatibility. - if (shopItemData.ContainsInInventory) - { - shopState.Register(shopItem); - shardedShopStates[shardedShopAddress] = shopState; - sellerAvatarState.inventory.AddItem2((ItemBase)nonFungibleItem); - _initialState = _initialState.SetState(shardedShopAddress, shopState.Serialize()); - } - else - { - legacyShopState.Register(shopItem); - } - - Assert.Equal(requiredBlockIndex, nonFungibleItem.RequiredBlockIndex); - Assert.Equal( - shopItemData.ContainsInInventory, - sellerAvatarState.inventory.TryGetNonFungibleItem(productId, out _) - ); - - var purchaseInfo = new PurchaseInfo0( - shopItem.ProductId, - shopItem.SellerAgentAddress, - shopItem.SellerAvatarAddress, - itemSubType, - shopItem.Price - ); - purchaseInfos.Add(purchaseInfo); - - _initialState = _initialState - .SetState(_buyerAvatarAddress, buyerAvatarState.Serialize()) - .SetState(sellerAvatarState.address, sellerAvatarState.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()) - .SetState(Addresses.Shop, legacyShopState.Serialize()); - } - - Assert.Single(legacyShopState.Products); - Assert.True(shardedShopStates.All(r => r.Value.Products.Count == 1)); - - var buyAction = new Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = purchaseInfos, - }; - var nextState = buyAction.Execute(new ActionContext() - { - BlockIndex = 1, - PreviousState = _initialState, - RandomSeed = 0, - 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 = purchaseInfo.productId; - Buy7.PurchaseResult pr = buyAction.buyerMultipleResult.purchaseResults.First(r => r.productId == itemId); - ShopItem shopItem = pr.shopItem; - FungibleAssetValue tax = shopItem.Price.DivRem(100, out _) * Buy.TaxRate; - FungibleAssetValue taxedPrice = shopItem.Price - tax; - totalTax += tax; - totalPrice += shopItem.Price; - - Assert.True( - nextBuyerAvatarState.inventory.TryGetNonFungibleItem( - itemId, - out INonFungibleItem outNonFungibleItem) - ); - Assert.Equal(1, outNonFungibleItem.RequiredBlockIndex); - - var nextSellerAvatarState = nextState.GetAvatarState(purchaseInfo.sellerAvatarAddress); - Assert.False( - nextSellerAvatarState.inventory.TryGetNonFungibleItem( - itemId, - out INonFungibleItem _) - ); - 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 Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - 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 Buy5 - { - buyerAvatarAddress = default, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = new Account(MockState.Empty), - RandomSeed = 0, - 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 Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - Signer = _buyerAgentAddress, - }) - ); - } - - [Fact] - public void Execute_ErrorCode_ItemDoesNotExist() - { - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - default, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon - ); - - var action = new Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - 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, - (ITradableItem)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, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0) - ); - - var action = new Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeInsufficientBalance, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Fact] - public void Execute_ErrorCode_ItemDoesNotExist_By_SellerAvatar() - { - 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, - (ITradableItem)itemUsable); - - ShardedShopState shopState = new ShardedShopState(shardedShopAddress); - shopState.Register(shopItem); - _initialState = _initialState.SetState(shardedShopAddress, shopState.Serialize()); - - Assert.True(shopItem.ExpiredBlockIndex > 0); - Assert.True(shopItem.ItemUsable.RequiredBlockIndex > 0); - - PurchaseInfo0 purchaseInfo0 = new PurchaseInfo0( - _productId, - _sellerAgentAddress, - _sellerAvatarAddress, - ItemSubType.Weapon, - shopItem.Price - ); - - var action = new Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeItemDoesNotExist, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Fact] - public void Execute_ErrorCode_ShopItemExpired() - { - IAccount 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 Buy5 - { - buyerAvatarAddress = _buyerAvatarAddress, - purchaseInfos = new[] { purchaseInfo0 }, - }; - - action.Execute(new ActionContext() - { - BlockIndex = 11, - PreviousState = previousStates, - RandomSeed = 0, - 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().Address; - - 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 Address SellerAgentAddress { get; set; } - - public Address SellerAvatarAddress { get; set; } - - public BigInteger Price { get; set; } - - public long RequiredBlockIndex { get; set; } - - public bool ContainsInInventory { get; set; } - } - } -} diff --git a/.Lib9c.Tests/Action/Buy6Test.cs b/.Lib9c.Tests/Action/Buy6Test.cs deleted file mode 100644 index 50b8acfc0a..0000000000 --- a/.Lib9c.Tests/Action/Buy6Test.cs +++ /dev/null @@ -1,799 +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 IAccount _initialState; - - public Buy6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ContainsInInventory = true, - ItemCount = 1, - }, - new ShopItemData() - { - ItemType = ItemType.Costume, - ItemId = Guid.NewGuid(), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ContainsInInventory = false, - ItemCount = 1, - }, - new ShopItemData() - { - ItemType = ItemType.Equipment, - ItemId = Guid.NewGuid(), - ProductId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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 = (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 Account(MockState.Empty), - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - Signer = _buyerAgentAddress, - }); - - Assert.Contains( - Buy.ErrorCodeItemDoesNotExist, - action.buyerMultipleResult.purchaseResults.Select(r => r.errorCode) - ); - } - - [Fact] - public void Execute_ErrorCode_ShopItemExpired() - { - IAccount 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, - RandomSeed = 0, - 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().Address; - - 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 adb939bcaf..0000000000 --- a/.Lib9c.Tests/Action/Buy8Test.cs +++ /dev/null @@ -1,768 +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 IAccount _initialState; - - public Buy8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 20, - ItemCount = 1, - }, - }; - yield return new object[] - { - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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 = (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 }, - }; - - IAccount nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - RandomSeed = 0, - 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) - ); - } - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().Address; - - 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 529f8be3c5..0000000000 --- a/.Lib9c.Tests/Action/Buy9Test.cs +++ /dev/null @@ -1,869 +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 IAccount _initialState; - - public Buy9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - 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().Address; - var sellerAgentState = new AgentState(_sellerAgentAddress); - _sellerAvatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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().Address; - var buyerAgentState = new AgentState(_buyerAgentAddress); - _buyerAvatarAddress = new PrivateKey().Address; - _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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - FromPreviousAction = true, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - FromPreviousAction = true, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = Sell6.ExpiredBlockIndex, - Price = 10, - ItemCount = 1, - FromPreviousAction = false, - }, - new OrderData() - { - ItemType = ItemType.Costume, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - RequiredBlockIndex = 0, - Price = 10, - ItemCount = 1, - FromPreviousAction = false, - }, - new OrderData() - { - ItemType = ItemType.Equipment, - TradableId = Guid.NewGuid(), - OrderId = Guid.NewGuid(), - SellerAgentAddress = new PrivateKey().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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().Address, - SellerAvatarAddress = new PrivateKey().Address, - 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 = (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 }, - }; - - IAccount nextState = action.Execute(new ActionContext() - { - BlockIndex = blockIndex, - PreviousState = _initialState, - RandomSeed = 0, - 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) - ); - } - } - - private (AvatarState AvatarState, AgentState AgentState) CreateAvatarState( - Address agentAddress, Address avatarAddress) - { - var agentState = new AgentState(agentAddress); - var rankingMapAddress = new PrivateKey().Address; - - 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/ClaimMonsterCollectionReward0Test.cs b/.Lib9c.Tests/Action/ClaimMonsterCollectionReward0Test.cs deleted file mode 100644 index 606074798a..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 IAccount _state; - - public ClaimMonsterCollectionReward0Test() - { - _signer = default; - _avatarAddress = _signer.Derive("avatar"); - _state = new Account(MockState.Empty); - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - var rankingMapAddress = new PrivateKey().Address; - 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, - }; - - IAccount nextState = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = rewardLevel * MonsterCollectionState0.RewardInterval, - RandomSeed = 0, - }); - - 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().Address, - 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, - RandomSeed = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimMonsterCollectionReward2Test.cs b/.Lib9c.Tests/Action/ClaimMonsterCollectionReward2Test.cs deleted file mode 100644 index 5f516665cf..0000000000 --- a/.Lib9c.Tests/Action/ClaimMonsterCollectionReward2Test.cs +++ /dev/null @@ -1,350 +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 IAccount _state; - - public ClaimMonsterCollectionReward2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _signer = default; - _avatarAddress = _signer.Derive("avatar"); - _state = new Account(MockState.Empty); - Dictionary sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - var rankingMapAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - else - { - IAccount nextState = action.Execute(new ActionContext - { - PreviousState = _state, - Signer = _signer, - BlockIndex = claimBlockIndex, - RandomSeed = 0, - }); - - 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().Address, - 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, - }) - ); - } - - 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 67e40ac78c..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward1Test.cs +++ /dev/null @@ -1,117 +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 IAccount _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 Account(MockState.Empty); - - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(StakeRegularRewardSheet)] = - ClaimStakeReward8.V2.StakeRegularRewardSheetCsv; - 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().Address; - var stakeStateAddress = StakeState.DeriveAddress(_signerAddress); - var agentState = new AgentState(_signerAddress); - _avatarAddress = new PrivateKey().Address; - 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/ClaimStakeReward2Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward2Test.cs deleted file mode 100644 index 3801ccf9c2..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward2Test.cs +++ /dev/null @@ -1,179 +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 ClaimStakeReward2Test - { - private readonly IAccount _initialState; - private readonly Currency _currency; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - private readonly Address _signerAddress; - private readonly Address _avatarAddress; - private readonly Address _avatarAddressForBackwardCompatibility; - - public ClaimStakeReward2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty); - - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(StakeRegularRewardSheet)] = - ClaimStakeReward8.V2.StakeRegularRewardSheetCsv; - 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().Address; - var stakeStateAddress = StakeState.DeriveAddress(_signerAddress); - var agentState = new AgentState(_signerAddress); - _avatarAddress = _signerAddress.Derive("0"); - agentState.avatarAddresses.Add(0, _avatarAddress); - var avatarState = new AvatarState( - _avatarAddress, - _signerAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - new PrivateKey().Address - ) - { - level = 100, - }; - - _avatarAddressForBackwardCompatibility = _signerAddress.Derive("1"); - agentState.avatarAddresses.Add(1, _avatarAddressForBackwardCompatibility); - var avatarStateForBackwardCompatibility = new AvatarState( - _avatarAddressForBackwardCompatibility, - _signerAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), - new PrivateKey().Address - ) - { - 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( - _avatarAddressForBackwardCompatibility, - avatarStateForBackwardCompatibility.Serialize()) - .SetState(GoldCurrencyState.Address, _goldCurrencyState.Serialize()) - .SetState(stakeStateAddress, new StakeState(stakeStateAddress, 0).Serialize()) - .MintAsset(context, stakeStateAddress, _currency * 100); - } - - [Fact] - public void Serialization() - { - var action = new ClaimStakeReward2(_avatarAddress); - var deserialized = new ClaimStakeReward2(); - deserialized.LoadPlainValue(action.PlainValue); - Assert.Equal(action.AvatarAddress, deserialized.AvatarAddress); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Success(bool useOldTable) - { - Execute(_avatarAddress, useOldTable); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_With_Old_AvatarState_Success(bool useOldTable) - { - Execute(_avatarAddressForBackwardCompatibility, useOldTable); - } - - [Fact] - public void Execute_Throw_ActionObsoletedException() - { - var action = new ClaimStakeReward2(_avatarAddress); - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _signerAddress, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + 1, - })); - } - - private void Execute(Address avatarAddress, bool useOldTable) - { - var state = _initialState; - if (useOldTable) - { - 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(); - state = state.SetState(Addresses.GetSheetAddress(), sheet); - } - - var action = new ClaimStakeReward2(avatarAddress); - var states = action.Execute(new ActionContext - { - PreviousState = state, - 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); - } - } -} diff --git a/.Lib9c.Tests/Action/ClaimStakeReward3Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward3Test.cs deleted file mode 100644 index 180b452a78..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward3Test.cs +++ /dev/null @@ -1,246 +0,0 @@ -namespace Lib9c.Tests.Action -{ -#nullable enable - - using System.Collections.Generic; - 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 Nekoyume.TableData; - 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 IAccount _initialStatesWithAvatarStateV1; - private readonly IAccount _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, - sheetsOverride: new Dictionary - { - { - nameof(StakeRegularRewardSheet), - ClaimStakeReward6.V2.StakeRegularRewardSheetCsv - }, - }); - _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( - IAccount 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 0a6d584303..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward4Test.cs +++ /dev/null @@ -1,315 +0,0 @@ -namespace Lib9c.Tests.Action -{ -#nullable enable - - using System.Collections.Generic; - 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 Nekoyume.TableData; - 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 IAccount _initialStatesWithAvatarStateV1; - private readonly IAccount _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, - sheetsOverride: new Dictionary - { - { - nameof(StakeRegularRewardSheet), - ClaimStakeReward6.V2.StakeRegularRewardSheetCsv - }, - }); - _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( - IAccount 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/ClaimStakeReward5Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward5Test.cs deleted file mode 100644 index ad2843b2fc..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward5Test.cs +++ /dev/null @@ -1,316 +0,0 @@ -namespace Lib9c.Tests.Action -{ -#nullable enable - - using System.Collections.Generic; - 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 Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - - public class ClaimStakeReward5Test - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccount _initialStatesWithAvatarStateV1; - private readonly IAccount _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public ClaimStakeReward5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - ( - _, - _, - _avatarAddr, - _initialStatesWithAvatarStateV1, - _initialStatesWithAvatarStateV2) = InitializeUtil.InitializeStates( - agentAddr: _agentAddr, - sheetsOverride: new Dictionary - { - { - nameof(StakeRegularRewardSheet), - ClaimStakeReward6.V2.StakeRegularRewardSheetCsv - }, - }); - _ncg = _initialStatesWithAvatarStateV1.GetGoldCurrency(); - } - - [Fact] - public void Serialization() - { - var action = new ClaimStakeReward5(_avatarAddr); - var deserialized = new ClaimStakeReward5(); - 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, - 3, - 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, - 1_000_000, - 12_502, - 1_666, - 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( - IAccount 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 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 ClaimStakeReward5(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/ClaimStakeReward6Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward6Test.cs deleted file mode 100644 index 51d8c3c5e0..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward6Test.cs +++ /dev/null @@ -1,319 +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 ClaimStakeReward6Test - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccount _initialStatesWithAvatarStateV1; - private readonly IAccount _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public ClaimStakeReward6Test(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 ClaimStakeReward6(_avatarAddr); - var deserialized = new ClaimStakeReward6(); - 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, - 3, - 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, - 1_000_000, - 12_502, - 1_666, - null, - null, - 0)] - // test tx(3baf5904b8499975a27d3873e58953ef8d0aa740318e99b2fe6a85428c9eb7aa) - [InlineData( - 5_350_456L, - 500_000L, - 7_576_016L, - 7_625_216L, - 100_000, - 627, - 83, - 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( - IAccount 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 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 ClaimStakeReward6(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/ClaimStakeReward7Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward7Test.cs deleted file mode 100644 index 533b1275aa..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward7Test.cs +++ /dev/null @@ -1,295 +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 ClaimStakeReward7Test - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccount _initialStatesWithAvatarStateV1; - private readonly IAccount _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public ClaimStakeReward7Test(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 ClaimStakeReward7(_avatarAddr); - var deserialized = new ClaimStakeReward7(); - 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, - 3, - 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, - 3000, - 17, - 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, - 15_000_000, - 75_006, - 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, - 10_000_000, - 50_004, - 3_332, - AgentAddressHex, - "GARAGE", - 100_000L - )] - 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( - IAccount 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 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 ClaimStakeReward7(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/ClaimStakeReward8Test.cs b/.Lib9c.Tests/Action/ClaimStakeReward8Test.cs deleted file mode 100644 index 01e1d72d21..0000000000 --- a/.Lib9c.Tests/Action/ClaimStakeReward8Test.cs +++ /dev/null @@ -1,502 +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 ClaimStakeReward8Test - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - - // VALUE: 6_692_400L - // - receive v1 reward * 1 - // - receive v2(w/o currency) reward * 4 - // - receive v2(w/ currency) reward * 14 - // - receive v3 reward * n - private const long BlockIndexForTest = - StakeState.StakeRewardSheetV3Index - - ((StakeState.StakeRewardSheetV3Index - StakeState.StakeRewardSheetV2Index) / StakeState.RewardInterval + 1) * - StakeState.RewardInterval; - - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccount _initialStatesWithAvatarStateV1; - private readonly IAccount _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public ClaimStakeReward8Test(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 ClaimStakeReward8(_avatarAddr); - var deserialized = new ClaimStakeReward8(); - 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, - 3, - 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, - 3000, - 17, - 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. - // receive v2(w/o currency) * 2, receive v2(w/ currency). check GARAGE. - [InlineData( - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval * 2, - 10_000_000L, - null, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval, - 15_000_000, - 75_006, - 4_998, - AgentAddressHex, - "GARAGE", - 100_000L - )] - // stake before currency as reward, prev. - // receive v2(w/o currency), receive v2(w/ currency). check GARAGE. - [InlineData( - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval * 2, - 10_000_000L, - StakeState.CurrencyAsRewardStartIndex - StakeState.RewardInterval, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval, - 10_000_000, - 50_004, - 3_332, - AgentAddressHex, - "GARAGE", - 100_000L - )] - // stake before v3(crystal), non prev. receive v2. check CRYSTAL. - [InlineData( - StakeState.StakeRewardSheetV3Index - 1, - 500L, - null, - StakeState.StakeRewardSheetV3Index - 1 + StakeState.RewardInterval, - 125, - 2, - 0, - AgentAddressHex, - "CRYSTAL", - 0L - )] - // stake after v3(crystal), non prev. receive v3. check CRYSTAL. - [InlineData( - StakeState.StakeRewardSheetV3Index, - 500L, - null, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval, - 125, - 2, - 0, - AgentAddressHex, - "CRYSTAL", - 5_000L - )] - // stake before v3(crystal), non prev. receive v2 * 2, receive v3. check CRYSTAL. - [InlineData( - StakeState.StakeRewardSheetV3Index - StakeState.RewardInterval * 2, - 10_000_000L, - null, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval, - 35_000_000, - 175_006, - 11_665, - AgentAddressHex, - "CRYSTAL", - 1_000_000_000L - )] - // stake before v3(crystal), prev. receive v2, receive v3. check CRYSTAL. - [InlineData( - StakeState.StakeRewardSheetV3Index - StakeState.RewardInterval * 2, - 10_000_000L, - StakeState.StakeRewardSheetV3Index - StakeState.RewardInterval, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval, - 30_000_000, - 150_004, - 9_999, - AgentAddressHex, - "CRYSTAL", - 1_000_000_000L - )] - // stake after v3(crystal), non prev. receive v2 * 2, receive v3. check CRYSTAL. - [InlineData( - StakeState.StakeRewardSheetV3Index, - 10_000_000L, - null, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval * 3, - 75_000_000, - 375_006, - 24_999, - AgentAddressHex, - "CRYSTAL", - 3_000_000_000L - )] - // stake after v3(crystal), prev. receive v2, receive v3. check CRYSTAL. - [InlineData( - StakeState.StakeRewardSheetV3Index, - 10_000_000L, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval * 3, - 50_000_000, - 250_004, - 16_666, - AgentAddressHex, - "CRYSTAL", - 2_000_000_000L - )] - // stake before v2(w/o currency), non prev. - // receive v1. - [InlineData( - BlockIndexForTest, - 500L, - null, - BlockIndexForTest + StakeState.RewardInterval, - 62, - 2, - 0, - null, - null, - 0L - )] - // stake before v2(w/o currency), non prev. - // receive v1, do not receive v2(w/o currency). - [InlineData( - BlockIndexForTest, - 500L, - null, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval - 1, - 62, - 2, - 0, - null, - null, - 0L - )] - // stake before v2(w/o currency), non prev. - // receive v1, receive v2(w/o currency). - [InlineData( - BlockIndexForTest, - 500L, - null, - StakeState.StakeRewardSheetV2Index + StakeState.RewardInterval * 2 - 1, - 187, - 4, - 0, - null, - null, - 0L - )] - // stake before v2(w/o currency), non prev. - // receive v1, receive v2(w/o currency) * 3, do not receive v2(w/ currency). - [InlineData( - BlockIndexForTest, - 500L, - null, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval - 1, - 562, - 10, - 0, - null, - null, - 0L - )] - // stake before v2(w/o currency), non prev. - // receive v1, receive v2(w/o currency) * 3, receive v2(w/ currency). - // check GARAGE is 0 when stake 500. - [InlineData( - BlockIndexForTest, - 500L, - null, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval * 2 - 1, - 687, - 12, - 0, - AgentAddressHex, - "GARAGE", - 0L - )] - // stake before v2(w/o currency), non prev. - // receive v1, receive v2(w/o currency) * 3, receive v2(w/ currency). - // check GARAGE is 100,000 when stake 10,000,000. - [InlineData( - BlockIndexForTest, - 10_000_000L, - null, - StakeState.CurrencyAsRewardStartIndex + StakeState.RewardInterval * 2 - 1, - 27_000_000, - 137_512, - 9_996, - AgentAddressHex, - "GARAGE", - 100_000L - )] - // stake before v2(w/o currency), non prev. - // receive v1, receive v2(w/o currency) * 3, receive v2(w/ currency) * ???, no receive v3. - // check CRYSTAL is 0. - [InlineData( - BlockIndexForTest, - 500L, - null, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval - 1, - 2_312, - 38, - 0, - AgentAddressHex, - "CRYSTAL", - 0L - )] - // stake before v2(w/o currency), non prev. - // receive v1, receive v2(w/o currency) * 3, receive v2(w/ currency) * ???, receive v3. - // check CRYSTAL is ???. - [InlineData( - BlockIndexForTest, - 500L, - null, - StakeState.StakeRewardSheetV3Index + StakeState.RewardInterval * 2 - 1, - 2_437, - 40, - 0, - AgentAddressHex, - "CRYSTAL", - 5_000L - )] - 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( - IAccount 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 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 ClaimStakeReward8(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 94daf70f7c..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty), - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }) - ); - } - - [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, - RandomSeed = _random.Seed, - }) - ); - } - - [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, - RandomSeed = _random.Seed, - }) - ); - } - - [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, - RandomSeed = _random.Seed, - }) - ); - } - - [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, - RandomSeed = _random.Seed, - }) - ); - } - - [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 a9b75723d2..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 d6e5381f54..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 bf0f336c70..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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/CombinationConsumable5Test.cs b/.Lib9c.Tests/Action/CombinationConsumable5Test.cs deleted file mode 100644 index 788c4f8221..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable5Test.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 CombinationConsumable5Test - { - 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 IAccount _initialState; - - public CombinationConsumable5Test() - { - _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 Account(MockState.Empty) - .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 CombinationConsumable5() - { - AvatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - 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 57a711289c..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 79290a09af..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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/CombinationConsumable8Test.cs b/.Lib9c.Tests/Action/CombinationConsumable8Test.cs deleted file mode 100644 index 90fc3bb85d..0000000000 --- a/.Lib9c.Tests/Action/CombinationConsumable8Test.cs +++ /dev/null @@ -1,147 +0,0 @@ -namespace Lib9c.Tests.Action -{ - 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 Xunit; - using static Lib9c.SerializeKeys; - - public class CombinationConsumable8Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private IAccount _initialState; - - public CombinationConsumable8Test() - { - _agentAddress = new PrivateKey().Address; - _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 Account(MockState.Empty) - .SetState(_agentAddress, agentState.Serialize()) - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState( - slotAddress, - new CombinationSlotState( - slotAddress, - GameConfig.RequireClearedStageLevel.CombinationConsumableAction).Serialize()) - .SetState(GameConfigState.Address, gold.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(); - var costActionPoint = row.RequiredActionPoint; - foreach (var materialInfo in row.Materials) - { - var materialRow = _tableSheets.MaterialItemSheet[materialInfo.Id]; - var material = ItemFactory.CreateItem(materialRow, _random); - avatarState.inventory.AddItem(material, materialInfo.Count); - } - - var previousActionPoint = avatarState.actionPoint; - var previousResultConsumableCount = - avatarState.inventory.Equipments.Count(e => e.Id == row.ResultConsumableItemId); - var previousMailCount = avatarState.mailBox.Count; - - avatarState.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.CombinationConsumableAction); - - IAccount 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 CombinationConsumable8 - { - avatarAddress = _avatarAddress, - recipeId = row.Id, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - Assert.NotNull(slotState.Result); - Assert.NotNull(slotState.Result.itemUsable); - - var consumable = (Consumable)slotState.Result.itemUsable; - Assert.NotNull(consumable); - - 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( - previousResultConsumableCount + 1, - nextAvatarState.inventory.Consumables.Count(e => e.Id == row.ResultConsumableItemId)); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment0Test.cs b/.Lib9c.Tests/Action/CombinationEquipment0Test.cs deleted file mode 100644 index 981663b9c8..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment0Test.cs +++ /dev/null @@ -1,414 +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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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); - } - - var worldSheet = _tableSheets.WorldSheet; - var worldId = 1; - foreach (var worldRow in worldSheet.OrderedList) - { - if (worldRow.StageBegin <= row.UnlockStage && row.UnlockStage <= worldRow.StageEnd) - { - worldId = worldRow.Id; - } - } - - for (int j = 1; j < worldId + 1; j++) - { - for (var i = 1; i < row.UnlockStage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - j, - 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty), - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = _random.Seed, - }) - ); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment10Test.cs b/.Lib9c.Tests/Action/CombinationEquipment10Test.cs deleted file mode 100644 index f5be8578f9..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment10Test.cs +++ /dev/null @@ -1,339 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 CombinationEquipment10Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly IRandom _random; - private readonly IAccount _initialState; - - public CombinationEquipment10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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 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, - RandomSeed = _random.Seed, - }); - - 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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 d2e9bf83b1..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment11Test.cs +++ /dev/null @@ -1,371 +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 Lib9c.Tests.Fixtures.TableCSV; - 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 IAccount _initialState; - private IValue _arenaSheetState; - - public CombinationEquipment11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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.SetNull(arenaSheetAddress); - } - - [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 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, - RandomSeed = _random.Seed, - }); - - 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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/CombinationEquipment12Test.cs b/.Lib9c.Tests/Action/CombinationEquipment12Test.cs deleted file mode 100644 index 5b9b03be8d..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment12Test.cs +++ /dev/null @@ -1,402 +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 Lib9c.Tests.Fixtures.TableCSV; - 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; - 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 IAccount _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().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - })); - } - } - - [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 0628f49368..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment13Test.cs +++ /dev/null @@ -1,529 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Lib9c.Tests.Fixtures.TableCSV; - 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; - 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 IAccount _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().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - })); - } - } - - [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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - }); - }); - } - } - - [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 5f79e0250f..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment14Test.cs +++ /dev/null @@ -1,536 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Lib9c.Tests.Fixtures.TableCSV; - 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; - 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 IAccount _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().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - })); - } - } - - [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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - }); - }); - } - } - - [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 14fc8006f9..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment15Test.cs +++ /dev/null @@ -1,536 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Lib9c.Tests.Fixtures.TableCSV; - 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; - 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 IAccount _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().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - _slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - })); - } - } - - [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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = _random.Seed, - }); - }); - } - } - - [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 0820ac5fd2..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 IAccount _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 f6c10f5648..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment3Test.cs +++ /dev/null @@ -1,258 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _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(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = 0, - })); - } - - [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 fa22f1fdd7..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment4Test.cs +++ /dev/null @@ -1,257 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _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(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = 0, - })); - } - - [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 9fc9b3b06c..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment5Test.cs +++ /dev/null @@ -1,254 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _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(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = 0, - })); - } - - [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 502efe37f1..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment6Test.cs +++ /dev/null @@ -1,272 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _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(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = 0, - })); - } - - [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 7952588831..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment7Test.cs +++ /dev/null @@ -1,271 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _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(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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, - RandomSeed = 0, - })); - } - - [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 8ec4c12b73..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment8Test.cs +++ /dev/null @@ -1,248 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _initialState; - - public CombinationEquipment8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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 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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 b9a91b50e7..0000000000 --- a/.Lib9c.Tests/Action/CombinationEquipment9Test.cs +++ /dev/null @@ -1,248 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Globalization; - using System.Linq; - using Lib9c.Tests.Fixtures.TableCSV; - 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 Nekoyume.TableData; - 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 IAccount _initialState; - - public CombinationEquipment9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _agentAddress = new PrivateKey().Address; - _avatarAddress = _agentAddress.Derive("avatar"); - var slotAddress = _avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - ) - ); - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemRecipeSheet)] = - EquipmentItemSheetFixture.EquipmentItemRecipeSheetWithMimisbrunnr; - _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 Account(MockState.Empty) - .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 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(); - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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/CreateAvatar0Test.cs b/.Lib9c.Tests/Action/CreateAvatar0Test.cs deleted file mode 100644 index 28ba4f4b14..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar0Test.cs +++ /dev/null @@ -1,248 +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.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 CreateAvatar0Test - { - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar0Test() - { - _agentAddress = default; - _avatarAddress = _agentAddress.Derive("avatar"); - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Fact] - public void Execute() - { - var action = new CreateAvatar0() - { - avatarAddress = _avatarAddress, - 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 Account(MockState.Empty) - .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 - ); - 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 avatarAddress = agentAddress.Derive("avatar"); - - var action = new CreateAvatar0() - { - avatarAddress = avatarAddress, - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new Account(MockState.Empty); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void ExecuteThrowInvalidAddressException() - { - var avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var action = new CreateAvatar0() - { - avatarAddress = _avatarAddress, - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new Account(MockState.Empty).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 Account(MockState.Empty).SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar0() - { - avatarAddress = _avatarAddress, - 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); - agentState.avatarAddresses[index] = _avatarAddress; - var state = new Account(MockState.Empty).SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar0() - { - avatarAddress = _avatarAddress, - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void SerializeWithDotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar0() - { - avatarAddress = default, - 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 = (CreateAvatar0)formatter.Deserialize(ms); - - Assert.Equal(default, deserialized.avatarAddress); - 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/CreateAvatar10Test.cs b/.Lib9c.Tests/Action/CreateAvatar10Test.cs deleted file mode 100644 index 71a257306d..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar10Test.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.Action.State; - 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 CreateAvatar10Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar10Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Theory] - [InlineData(0L)] - [InlineData(7_210_000L)] - [InlineData(7_210_001L)] - public void Execute(long blockIndex) - { - var action = new CreateAvatar10() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var sheets = TableSheetsImporter.ImportSheets(); - var state = new Account(MockState.Empty) - .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 = blockIndex, - RandomSeed = 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(200_000 * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - var avatarItemSheet = nextState.GetSheet(); - foreach (var row in avatarItemSheet.Values) - { - Assert.True(nextAvatarState.inventory.HasItem(row.ItemId, row.Count)); - } - - var avatarFavSheet = nextState.GetSheet(); - foreach (var row in avatarFavSheet.Values) - { - var targetAddress = row.Target == CreateAvatarFavSheet.Target.Agent - ? _agentAddress - : avatarAddress; - Assert.Equal(row.Currency * row.Quantity, nextState.GetBalance(targetAddress, row.Currency)); - } - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar10() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new Account(MockState.Empty); - - 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 CreateAvatar10() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new Account(MockState.Empty).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 Account(MockState.Empty).SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar10() - { - 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 Account(MockState.Empty).SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar10() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Serialize_With_DotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar10() - { - 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 = (CreateAvatar10)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); - } - - [Fact] - public void AddItem() - { - var itemSheet = _tableSheets.ItemSheet; - var createAvatarItemSheet = new CreateAvatarItemSheet(); - createAvatarItemSheet.Set(@"item_id,count -10112000,2 -10512000,2 -600201,2 -"); - var avatarState = new AvatarState(default, default, 0L, _tableSheets.GetAvatarSheets(), new GameConfigState(), default, "test"); - CreateAvatar10.AddItem(itemSheet, createAvatarItemSheet, avatarState, new TestRandom()); - foreach (var row in createAvatarItemSheet.Values) - { - Assert.True(avatarState.inventory.HasItem(row.ItemId, row.Count)); - } - - Assert.Equal(4, avatarState.inventory.Equipments.Count()); - foreach (var equipment in avatarState.inventory.Equipments) - { - var equipmentRow = _tableSheets.EquipmentItemSheet[equipment.Id]; - Assert.Equal(equipmentRow.Stat, equipment.Stat); - } - } - - [Fact] - public void MintAsset() - { - var createAvatarFavSheet = new CreateAvatarFavSheet(); - createAvatarFavSheet.Set(@"currency,quantity,target -CRYSTAL,200000,Agent -RUNE_GOLDENLEAF,200000,Avatar -"); - var avatarAddress = new PrivateKey().Address; - var agentAddress = new PrivateKey().Address; - var avatarState = new AvatarState(avatarAddress, agentAddress, 0L, _tableSheets.GetAvatarSheets(), new GameConfigState(), default, "test"); - var nextState = CreateAvatar10.MintAsset(createAvatarFavSheet, avatarState, new Account(MockState.Empty), new ActionContext()); - foreach (var row in createAvatarFavSheet.Values) - { - var targetAddress = row.Target == CreateAvatarFavSheet.Target.Agent - ? agentAddress - : avatarAddress; - Assert.Equal(row.Currency * row.Quantity, nextState.GetBalance(targetAddress, row.Currency)); - } - } - } -} diff --git a/.Lib9c.Tests/Action/CreateAvatar2Test.cs b/.Lib9c.Tests/Action/CreateAvatar2Test.cs deleted file mode 100644 index 91ccc15972..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar2Test.cs +++ /dev/null @@ -1,261 +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.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 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 Account(MockState.Empty) - .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 Account(MockState.Empty); - - 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 Account(MockState.Empty).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 Account(MockState.Empty).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 Account(MockState.Empty).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, - }) - ); - } - - [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 b136d04258..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar3Test.cs +++ /dev/null @@ -1,263 +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.Action.State; - 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 Account(MockState.Empty) - .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 Account(MockState.Empty); - - 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 Account(MockState.Empty).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 Account(MockState.Empty).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 Account(MockState.Empty).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, - }) - ); - } - - [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 5909bfca91..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar6Test.cs +++ /dev/null @@ -1,263 +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.Action.State; - 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 Account(MockState.Empty) - .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 Account(MockState.Empty); - - 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 Account(MockState.Empty).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 Account(MockState.Empty).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 Account(MockState.Empty).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, - }) - ); - } - - [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 39fb3a2277..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar7Test.cs +++ /dev/null @@ -1,244 +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.Action.State; - 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 Account(MockState.Empty) - .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 Account(MockState.Empty); - - 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 Account(MockState.Empty).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 Account(MockState.Empty).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 Account(MockState.Empty).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, - }) - ); - } - - [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 4d484beeb0..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar8Test.cs +++ /dev/null @@ -1,243 +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.Action.State; - 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 Account(MockState.Empty) - .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 Account(MockState.Empty); - - 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 Account(MockState.Empty).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 Account(MockState.Empty).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 Account(MockState.Empty).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, - }) - ); - } - - [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/CreateAvatar9Test.cs b/.Lib9c.Tests/Action/CreateAvatar9Test.cs deleted file mode 100644 index fb2da04642..0000000000 --- a/.Lib9c.Tests/Action/CreateAvatar9Test.cs +++ /dev/null @@ -1,270 +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.Action.State; - 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 CreateAvatar9Test - { - private readonly Address _agentAddress; - private readonly TableSheets _tableSheets; - - public CreateAvatar9Test() - { - _agentAddress = default; - _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); - } - - [Theory] - [InlineData(0L, 600_000, true, true)] - [InlineData(7_210_000L, 600_000, true, true)] - [InlineData(7_210_001L, 200_000, true, true)] - [InlineData(7_210_001L, 200_000, false, true)] - [InlineData(7_210_001L, 200_000, true, false)] - public void Execute(long blockIndex, int expected, bool avatarItemSheetExist, bool avatarFavSheetExist) - { - var action = new CreateAvatar9() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var sheets = TableSheetsImporter.ImportSheets(); - var state = new Account(MockState.Empty) - .SetState( - Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ); - - foreach (var (key, value) in sheets) - { - if (key == nameof(CreateAvatarItemSheet) && !avatarItemSheetExist) - { - continue; - } - - if (key == nameof(CreateAvatarFavSheet) && !avatarFavSheetExist) - { - continue; - } - - state = state.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - Assert.Equal(0 * CrystalCalculator.CRYSTAL, state.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - - if (!avatarItemSheetExist && !avatarFavSheetExist) - { - var nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - }); - - 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(expected * CrystalCalculator.CRYSTAL, nextState.GetBalance(_agentAddress, CrystalCalculator.CRYSTAL)); - } - else - { - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = blockIndex, - })); - } - } - - [Theory] - [InlineData("홍길동")] - [InlineData("山田太郎")] - public void ExecuteThrowInvalidNamePatterException(string nickName) - { - var agentAddress = default(Address); - - var action = new CreateAvatar9() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = nickName, - }; - - var state = new Account(MockState.Empty); - - 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 CreateAvatar9() - { - index = 0, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - var state = new Account(MockState.Empty).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 Account(MockState.Empty).SetState(_agentAddress, agentState.Serialize()); - var action = new CreateAvatar9() - { - 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 Account(MockState.Empty).SetState(_agentAddress, agentState.Serialize()); - - var action = new CreateAvatar9() - { - index = index, - hair = 0, - ear = 0, - lens = 0, - tail = 0, - name = "test", - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - - [Fact] - public void Serialize_With_DotnetAPI() - { - var formatter = new BinaryFormatter(); - var action = new CreateAvatar9() - { - 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 = (CreateAvatar9)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/DailyReward0Test.cs b/.Lib9c.Tests/Action/DailyReward0Test.cs deleted file mode 100644 index cccc7bd897..0000000000 --- a/.Lib9c.Tests/Action/DailyReward0Test.cs +++ /dev/null @@ -1,93 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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, - 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 Account(MockState.Empty), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward2Test.cs b/.Lib9c.Tests/Action/DailyReward2Test.cs deleted file mode 100644 index ef2c3bfb55..0000000000 --- a/.Lib9c.Tests/Action/DailyReward2Test.cs +++ /dev/null @@ -1,106 +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 DailyReward2Test - { - private readonly IAccount _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public DailyReward2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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 DailyReward2 - { - avatarAddress = _avatarAddress, - }; - var nextState = dailyRewardAction.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - 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 DailyReward2 - { - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = new Account(MockState.Empty), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward3Test.cs b/.Lib9c.Tests/Action/DailyReward3Test.cs deleted file mode 100644 index 5115938b3b..0000000000 --- a/.Lib9c.Tests/Action/DailyReward3Test.cs +++ /dev/null @@ -1,106 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - 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 Account(MockState.Empty), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward4Test.cs b/.Lib9c.Tests/Action/DailyReward4Test.cs deleted file mode 100644 index 00bb29a139..0000000000 --- a/.Lib9c.Tests/Action/DailyReward4Test.cs +++ /dev/null @@ -1,121 +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 IAccount _initialState; - - public DailyReward4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - 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 Account(MockState.Empty), - Signer = _agentAddress, - BlockIndex = 0, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward5Test.cs b/.Lib9c.Tests/Action/DailyReward5Test.cs deleted file mode 100644 index f4f722cef0..0000000000 --- a/.Lib9c.Tests/Action/DailyReward5Test.cs +++ /dev/null @@ -1,146 +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 IAccount _initialState; - - public DailyReward5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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()); - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - public void Execute(int avatarStateSerializedVersion) - { - IAccount 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, 2448); - 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 Account(MockState.Empty))); - - [Theory] - [InlineData(0, 0, true)] - [InlineData(0, 2447, true)] - [InlineData(0, 2448, false)] - [InlineData(2448, 2448, true)] - [InlineData(2448, 2448 + 2447, true)] - [InlineData(2448, 2448 + 2448, 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 IAccount SetAvatarStateAsV2To(IAccount 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 IAccount ExecuteInternal(IAccount previousStates, long blockIndex = 0) - { - var dailyRewardAction = new DailyReward5 - { - avatarAddress = _avatarAddress, - }; - - return dailyRewardAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - RandomSeed = 0, - Signer = _agentAddress, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/DailyReward6Test.cs b/.Lib9c.Tests/Action/DailyReward6Test.cs deleted file mode 100644 index 51a7db56c6..0000000000 --- a/.Lib9c.Tests/Action/DailyReward6Test.cs +++ /dev/null @@ -1,171 +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 IAccount _initialState; - - public DailyReward6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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()); - } - - [Theory] - [InlineData(1)] - [InlineData(2)] - public void Execute(int avatarStateSerializedVersion) - { - IAccount 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, 2448); - 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 Account(MockState.Empty))); - - [Theory] - [InlineData(0, 0, true)] - [InlineData(0, 2447, true)] - [InlineData(0, 2448, false)] - [InlineData(2448, 2448, true)] - [InlineData(2448, 2448 + 2447, true)] - [InlineData(2448, 2448 + 2448, 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 IAccount SetAvatarStateAsV2To(IAccount 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 IAccount ExecuteInternal(IAccount previousStates, long blockIndex = 0) - { - var dailyRewardAction = new DailyReward6 - { - avatarAddress = _avatarAddress, - }; - - return dailyRewardAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - RandomSeed = 0, - Signer = _agentAddress, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/EventDungeonBattleV1Test.cs b/.Lib9c.Tests/Action/EventDungeonBattleV1Test.cs deleted file mode 100644 index 460ae73799..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV1Test.cs +++ /dev/null @@ -1,437 +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 IAccount _initialStates; - - public EventDungeonBattleV1Test() - { - _initialStates = new Account(MockState.Empty); - -#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().Address; - _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().Address - ) - { - 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 IAccount Execute( - IAccount 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, - RandomSeed = 0, - 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 93b71a3882..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV2Test.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 EventDungeonBattleV2Test - { - private readonly Currency _ncgCurrency; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccount _initialStates; - - public EventDungeonBattleV2Test() - { - _initialStates = new Account(MockState.Empty); - -#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().Address; - _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().Address - ) - { - 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 IAccount Execute( - IAccount 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, - RandomSeed = 0, - 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 afb60ec55d..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV3Test.cs +++ /dev/null @@ -1,450 +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 IAccount _initialStates; - - public EventDungeonBattleV3Test() - { - _initialStates = new Account(MockState.Empty); - -#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().Address; - _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().Address - ) - { - 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 IAccount Execute( - IAccount 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, - RandomSeed = 0, - 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 0da57c3173..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV4Test.cs +++ /dev/null @@ -1,498 +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 IAccount _initialStates; - - public EventDungeonBattleV4Test() - { - _initialStates = new Account(MockState.Empty); - -#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().Address; - _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().Address - ) - { - 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, - RandomSeed = 0, - }); - - 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 IAccount Execute( - IAccount 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, - RandomSeed = 0, - 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/EventDungeonBattleV5Test.cs b/.Lib9c.Tests/Action/EventDungeonBattleV5Test.cs deleted file mode 100644 index 07197699ad..0000000000 --- a/.Lib9c.Tests/Action/EventDungeonBattleV5Test.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.Blockchain.Policy; - 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 EventDungeonBattleV5Test - { - private readonly Currency _ncgCurrency; - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private IAccount _initialStates; - - public EventDungeonBattleV5Test() - { - _initialStates = new Account(MockState.Empty); - -#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().Address; - _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().Address - ) - { - 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, - RandomSeed = 0, - }); - - 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 IAccount Execute( - IAccount 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 EventDungeonBattleV5 - { - 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, - RandomSeed = 0, - 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 6b23856820..0000000000 --- a/.Lib9c.Tests/Action/Factory/ClaimStakeRewardFactoryTest.cs +++ /dev/null @@ -1,80 +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(ClaimStakeReward5))] - [InlineData(ClaimStakeReward5.ObsoleteBlockIndex, typeof(ClaimStakeReward5))] - [InlineData(ClaimStakeReward5.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward6))] - [InlineData(ClaimStakeReward6.ObsoleteBlockIndex, typeof(ClaimStakeReward6))] - [InlineData(ClaimStakeReward6.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward7))] - [InlineData(ClaimStakeReward7.ObsoleteBlockIndex, typeof(ClaimStakeReward7))] - [InlineData(ClaimStakeReward7.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward8))] - [InlineData(ClaimStakeReward8.ObsoleteBlockIndex, typeof(ClaimStakeReward8))] - [InlineData(ClaimStakeReward8.ObsoleteBlockIndex + 1, typeof(ClaimStakeReward))] - [InlineData(long.MaxValue, typeof(ClaimStakeReward))] - public void Create_ByBlockIndex_Success( - long blockIndex, - Type type) - { - var addr = new PrivateKey().Address; - 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().Address; - 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 88d9a28547..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash0Test.cs +++ /dev/null @@ -1,648 +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 IAccount _initialState; - - public HackAndSlash0Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }) - ); - - 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 Account(MockState.Empty), - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [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, - RandomSeed = 0, - }); - - 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 48ff30b877..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash10Test.cs +++ /dev/null @@ -1,1112 +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 IAccount _initialState; - - public HackAndSlash10Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - 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 4fb9d9a866..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash11Test.cs +++ /dev/null @@ -1,1059 +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 IAccount _initialState; - - public HackAndSlash11Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - 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 f18f21a45e..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash12Test.cs +++ /dev/null @@ -1,1055 +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 IAccount _initialState; - - public HackAndSlash12Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - 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 f0ed23c4c5..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash13Test.cs +++ /dev/null @@ -1,1094 +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 IAccount _initialState; - - public HackAndSlash13Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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.SetNull(arenaSheetAddress); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - 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 0adb82db6a..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash15Test.cs +++ /dev/null @@ -1,1180 +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 IAccount _initialState; - - public HackAndSlash15Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var simulator = new StageSimulatorV1( - new TestRandom(ctx.RandomSeed), - 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 44adceecc0..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash16Test.cs +++ /dev/null @@ -1,1235 +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 IAccount _initialState; - - public HackAndSlash16Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var contextRandom = new TestRandom(ctx.RandomSeed); - 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 a0c2bd92b8..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash17Test.cs +++ /dev/null @@ -1,1235 +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 IAccount _initialState; - - public HackAndSlash17Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var contextRandom = new TestRandom(ctx.RandomSeed); - 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 d4f3a3d892..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash18Test.cs +++ /dev/null @@ -1,1425 +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 IAccount _initialState; - - public HackAndSlash18Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _sheets.Remove(nameof(RuneOptionSheet)); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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, - RandomSeed = 0, - })); - - 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.SetNull(Addresses.TableSheet.Derive(key)); - } - } - - 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, - RandomSeed = 0, - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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.RandomSeed); - 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, - RandomSeed = 0, - 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 32de6cb2fe..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash19Test.cs +++ /dev/null @@ -1,1350 +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 IAccount _initialState; - - public HackAndSlash19Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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.RandomSeed); - 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, - RandomSeed = 0, - 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 3b4bb6b0ad..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash20Test.cs +++ /dev/null @@ -1,1798 +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 IAccount _initialState; - - public HackAndSlash20Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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.RandomSeed); - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - } - - 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/HackAndSlash21Test.cs b/.Lib9c.Tests/Action/HackAndSlash21Test.cs deleted file mode 100644 index 7b5c751c3f..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash21Test.cs +++ /dev/null @@ -1,1799 +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.Blockchain.Policy; - 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 HackAndSlash21Test - { - 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 IAccount _initialState; - - public HackAndSlash21Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - }); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - }); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_FailedLoadStateException(bool backward) - { - var action = new HackAndSlash21 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = 1, - StageId = 1, - AvatarAddress = _avatarAddress, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Theory] - [InlineData(0)] - [InlineData(51)] - public void ExecuteThrowSheetRowColumnException(int stageId) - { - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowSheetRowNotFoundExceptionByStage() - { - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowFailedAddWorldException() - { - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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 HackAndSlash21 - { - Costumes = new List(), - Equipments = new List(), - Foods = new List(), - RuneInfos = new List(), - WorldId = worldId, - StageId = stageId, - AvatarAddress = _avatarAddress, - }; - - IAccount 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageException() - { - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowInvalidStageExceptionUnlockedWorld() - { - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - 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 HackAndSlash21 - { - 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, - RandomSeed = random.Seed, - })); - - SerializeException(exec); - } - } - } - - [Fact] - public void ExecuteThrowInvalidItemCountException() - { - var avatarState = new AvatarState(_avatarState) - { - actionPoint = 99999999, - level = 1, - }; - - var state = _initialState; - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowUsageLimitExceedException() - { - var state = _initialState; - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - SerializeException(exec); - } - - [Fact] - public void ExecuteThrowNotEnoughMaterialException() - { - var state = _initialState; - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - 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.RandomSeed); - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - - 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 HackAndSlash21 - { - 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, - RandomSeed = 0, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - }); - - var action2 = new HackAndSlash21 - { - 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - var action = new HackAndSlash21 - { - 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, - RandomSeed = 0, - })); - } - - 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 19971bf5de..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash2Test.cs +++ /dev/null @@ -1,657 +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 IAccount _initialState; - - public HackAndSlash2Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }) - ); - - 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 Account(MockState.Empty), - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - Assert.Null(action.Result); - - SerializeException(exec); - } - - [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, - RandomSeed = 0, - }); - - 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 0e09de51a5..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash3Test.cs +++ /dev/null @@ -1,166 +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 IAccount _initialState; - - public HackAndSlash3Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - }); - - 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 35834e1e5c..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash4Test.cs +++ /dev/null @@ -1,793 +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 IAccount _initialState; - - public HackAndSlash4Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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 Account(MockState.Empty), - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - 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 12ce93a580..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash5Test.cs +++ /dev/null @@ -1,770 +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 IAccount _initialState; - - public HackAndSlash5Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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 Account(MockState.Empty), - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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 a5eeea5f23..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash6Test.cs +++ /dev/null @@ -1,798 +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 IAccount _initialState; - - public HackAndSlash6Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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); - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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/HackAndSlash7Test.cs b/.Lib9c.Tests/Action/HackAndSlash7Test.cs deleted file mode 100644 index 87858225d4..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash7Test.cs +++ /dev/null @@ -1,861 +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 IAccount _initialState; - - public HackAndSlash7Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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/HackAndSlash8Test.cs b/.Lib9c.Tests/Action/HackAndSlash8Test.cs deleted file mode 100644 index 788f4d1cab..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash8Test.cs +++ /dev/null @@ -1,844 +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 IAccount _initialState; - - public HackAndSlash8Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - // Second Execute - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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/HackAndSlash9Test.cs b/.Lib9c.Tests/Action/HackAndSlash9Test.cs deleted file mode 100644 index a1616c6137..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlash9Test.cs +++ /dev/null @@ -1,1135 +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 IAccount _initialState; - - public HackAndSlash9Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - }) - ); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - - 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 5e2808597a..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep1Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep2Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep2Test.cs deleted file mode 100644 index c8d888b9a3..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep2Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep3Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep3Test.cs deleted file mode 100644 index 3cc15193c3..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep3Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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.SetNull(arenaSheetAddress); - - 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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep4Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep4Test.cs deleted file mode 100644 index 25b7c1d8d9..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep4Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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.SetNull(arenaSheetAddress); - - 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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep5Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep5Test.cs deleted file mode 100644 index 8a43e545e6..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep5Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/HackAndSlashSweep6Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep6Test.cs deleted file mode 100644 index 014f088d3c..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep6Test.cs +++ /dev/null @@ -1,873 +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 HackAndSlashSweep6Test - { - 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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep6Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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 HackAndSlashSweep6 - { - actionPoint = avatarState.actionPoint, - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - apStoneCount = apStoneCount, - worldId = worldId, - stageId = stageId, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = _random.Seed, - }); - - 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 HackAndSlashSweep6 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep6 - { - 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, - RandomSeed = 0, - })); - } - - [Theory] - [InlineData(1, 999)] - [InlineData(2, 50)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep6 - { - 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, - RandomSeed = 0, - })); - } - - [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 HackAndSlashSweep6 - { - 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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 HackAndSlashSweep6 - { - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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 HackAndSlashSweep6 - { - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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 HackAndSlashSweep6 - { - 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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 HackAndSlashSweep6 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 999999, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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 HackAndSlashSweep6 - { - costumes = costumes, - equipments = equipments, - avatarAddress = _avatarAddress, - actionPoint = 0, - apStoneCount = 0, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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 HackAndSlashSweep6 - { - 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, - RandomSeed = 0, - })); - } - } - - [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 HackAndSlashSweep6 - { - 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, - RandomSeed = 0, - }); - 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/HackAndSlashSweep7Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep7Test.cs deleted file mode 100644 index c60000516e..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep7Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - RandomSeed = 0, - }); - 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 7a05febbee..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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep8Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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, - RandomSeed = 0, - })); - } - } - - [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, - RandomSeed = 0, - }); - 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/HackAndSlashSweep9Test.cs b/.Lib9c.Tests/Action/HackAndSlashSweep9Test.cs deleted file mode 100644 index 53a7dafbed..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashSweep9Test.cs +++ /dev/null @@ -1,985 +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.Rune; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class HackAndSlashSweep9Test - { - 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 IAccount _initialState; - private readonly IRandom _random; - - public HackAndSlashSweep9Test() - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - - IAccount 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 HackAndSlashSweep9 - { - 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, - RandomSeed = _random.Seed, - }); - - 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 HackAndSlashSweep9 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 1, - }; - - var state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [Theory] - [InlineData(100, 1)] - public void Execute_SheetRowNotFoundException(int worldId, int stageId) - { - var action = new HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - - [Theory] - [InlineData(1, 999)] - [InlineData(2, 50)] - public void Execute_SheetRowColumnException(int worldId, int stageId) - { - var action = new HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - - [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 HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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 HackAndSlashSweep9 - { - runeInfos = new List(), - apStoneCount = 1, - avatarAddress = _avatarAddress, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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), - }; - - IAccount 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 HackAndSlashSweep9 - { - runeInfos = new List(), - apStoneCount = apStoneCount, - avatarAddress = _avatarAddress, - worldId = 1, - stageId = 2, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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 HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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 HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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 HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - } - - [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, - }; - - IAccount 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 HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - })); - } - } - - [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 HackAndSlashSweep9 - { - 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, - RandomSeed = 0, - }); - var nextAvatar = nextState.GetAvatarStateV2(_avatarAddress); - Assert.Equal(expectedLevel, nextAvatar.level); - Assert.Equal(expectedExp, nextAvatar.exp); - } - else - { - throw new SheetRowNotFoundException(nameof(StageSheet), stageId); - } - } - - [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 stakingLevel = 1; - 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 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, - RandomSeed = 0, - }); - - var action = new HackAndSlashSweep9 - { - costumes = new List(), - equipments = new List(), - runeInfos = new List() - { - new RuneSlotInfo(slotIndex, runeId), - new RuneSlotInfo(slotIndex2, runeId2), - }, - avatarAddress = _avatarAddress, - actionPoint = avatarState.actionPoint, - apStoneCount = 1, - worldId = worldId, - stageId = stageId, - }; - - Assert.Throws(exception, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - 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 c3f4bc54fc..0000000000 --- a/.Lib9c.Tests/Action/HackAndSlashTest14.cs +++ /dev/null @@ -1,1171 +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 IAccount _initialState; - - public HackAndSlashTest14() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount state = backward ? new Account(MockState.Empty) : _initialState; - if (!backward) - { - state = _initialState - .SetState(_avatarAddress, _avatarState.SerializeV2()) - .SetNull(_avatarAddress.Derive(LegacyInventoryKey)) - .SetNull(_avatarAddress.Derive(LegacyWorldInformationKey)) - .SetNull(_avatarAddress.Derive(LegacyQuestListKey)); - } - - var exec = Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - }; - - IAccount 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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, - RandomSeed = 0, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = random.Seed, - })); - - 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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - BlockIndex = 1, - }; - var nextState = action.Execute(ctx); - var simulator = new StageSimulatorV1( - new TestRandom(ctx.RandomSeed), - 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 a99b0c4764..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement0Test.cs +++ /dev/null @@ -1,477 +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 IAccount _initialState; - - public ItemEnhancement0Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(_sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty), - 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()); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement10Test.cs b/.Lib9c.Tests/Action/ItemEnhancement10Test.cs deleted file mode 100644 index f1258d13a3..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement10Test.cs +++ /dev/null @@ -1,250 +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.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class ItemEnhancement10Test - { - 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 IAccount _initialState; - private IValue _arenaSheetState; - - public ItemEnhancement10Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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()); - } - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _arenaSheetState = _initialState.GetState(arenaSheetAddress); - _initialState = _initialState.SetNull(arenaSheetAddress); - } - - [Theory] - [InlineData(0, 1000, true)] - [InlineData(6, 980, true)] - [InlineData(0, 1000, false)] - [InlineData(6, 980, false)] - public void Execute(int level, 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, - }; - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - - 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 ItemEnhancement10() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(ItemEnhancement10.GetFeeStoreAddress(), _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var grade = resultEquipment.Grade; - var costRow = _tableSheets.EnhancementCostSheetV2 - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == resultEquipment.level); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement9.ResultModel)slot.Result; - - switch ((ItemEnhancement10.EnhancementResult)slotResult.enhancementResult) - { - case ItemEnhancement10.EnhancementResult.GreatSuccess: - var baseAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.Equal((int)(baseAtk + extraAtk), resultEquipment.StatsMap.ATK); - Assert.Equal(preItemUsable.level + 1, resultEquipment.level); - break; - case ItemEnhancement10.EnhancementResult.Success: - var baseMinAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1); - var baseMaxAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraMinAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1); - var extraMaxAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.InRange(resultEquipment.StatsMap.ATK, (int)(baseMinAtk + extraMinAtk), (int)(baseMaxAtk + extraMaxAtk) + 1); - Assert.Equal(preItemUsable.level + 1, resultEquipment.level); - break; - case ItemEnhancement10.EnhancementResult.Fail: - Assert.Equal(preItemUsable.StatsMap.ATK, resultEquipment.StatsMap.ATK); - Assert.Equal(preItemUsable.level, resultEquipment.level); - break; - } - - Assert.Equal(preItemUsable.ItemId, slotResult.preItemUsable.ItemId); - Assert.Equal(preItemUsable.ItemId, resultEquipment.ItemId); - Assert.Equal(costRow.Cost, slotResult.gold); - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, 1); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, 1); - - _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); - } - - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - var arenaSheetAddress = Addresses.GetSheetAddress(); - _initialState = _initialState.SetState(arenaSheetAddress, _arenaSheetState); - var action = new ItemEnhancement10() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement11Test.cs b/.Lib9c.Tests/Action/ItemEnhancement11Test.cs deleted file mode 100644 index 886f8af74e..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement11Test.cs +++ /dev/null @@ -1,241 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Lib9c.Tests.Fixtures.TableCSV; - 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.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static Nekoyume.Action.ItemEnhancement11; - using static SerializeKeys; - - public class ItemEnhancement11Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccount _initialState; - - public ItemEnhancement11Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EnhancementCostSheetV2)] = - EquipmentItemSheetFixture.LegacyEnhancementCostSheetV2; - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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); - var slotAddress = _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty) - .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()); - } - - var costV3SheetAddress = Addresses.GetSheetAddress(); - _initialState = _initialState.SetNull(costV3SheetAddress); - } - - [Theory] - [InlineData(0, 1000, true, 0, 1, EnhancementResult.Success, 0, 0, false)] - [InlineData(6, 980, true, 0, 7, EnhancementResult.Success, 0, 0, false)] - [InlineData(0, 1000, false, 1, 1, EnhancementResult.GreatSuccess, 0, 0, false)] - [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 0, 320, false)] - [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 2, 480, false)] - [InlineData(0, 1000, true, 0, 1, EnhancementResult.Success, 0, 0, true)] - [InlineData(6, 980, true, 0, 7, EnhancementResult.Success, 0, 0, true)] - [InlineData(0, 1000, false, 1, 1, EnhancementResult.GreatSuccess, 0, 0, true)] - [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 0, 320, true)] - [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 2, 480, true)] - public void Execute( - int level, - int expectedGold, - bool backward, - int randomSeed, - int expectedLevel, - EnhancementResult expected, - int monsterCollectLevel, - int expectedCrystal, - bool stake - ) - { - var context = new ActionContext(); - 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, - }; - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - - 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()); - } - - if (monsterCollectLevel > 0) - { - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .First(r => r.Level == monsterCollectLevel).RequiredGold; - if (stake) - { - // StakeState; - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - _initialState = _initialState - .SetState(stakeStateAddress, stakeState.SerializeV2()) - .MintAsset(context, stakeStateAddress, requiredGold * _currency); - } - else - { - var mcAddress = MonsterCollectionState.DeriveAddress(_agentAddress, 0); - _initialState = _initialState.SetState( - mcAddress, - new MonsterCollectionState(mcAddress, monsterCollectLevel, 0).Serialize() - ) - .MintAsset(context, mcAddress, requiredGold * _currency); - } - } - - var action = new ItemEnhancement11() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = randomSeed, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); - - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(1); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal( - (1000 - expectedGold) * _currency, - nextState.GetBalance(feeStoreAddress, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var costRow = _tableSheets.EnhancementCostSheetV2 - .OrderedList - .First(x => x.Grade == 1 && x.Level == level + 1); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ResultModel)slot.Result; - Assert.Equal(expected, slotResult.enhancementResult); - - switch (slotResult.enhancementResult) - { - case EnhancementResult.GreatSuccess: - var baseAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.Equal((int)(baseAtk + extraAtk), resultEquipment.StatsMap.ATK); - break; - case EnhancementResult.Success: - var baseMinAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1); - var baseMaxAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraMinAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1); - var extraMaxAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.InRange(resultEquipment.StatsMap.ATK, baseMinAtk + extraMinAtk, baseMaxAtk + extraMaxAtk + 1); - break; - case EnhancementResult.Fail: - Assert.Equal(preItemUsable.StatsMap.ATK, resultEquipment.StatsMap.ATK); - break; - } - - Assert.Equal(preItemUsable.ItemId, slotResult.preItemUsable.ItemId); - Assert.Equal(preItemUsable.ItemId, resultEquipment.ItemId); - Assert.Equal(costRow.Cost, slotResult.gold); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, slotResult.CRYSTAL); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement12Test.cs b/.Lib9c.Tests/Action/ItemEnhancement12Test.cs deleted file mode 100644 index 44a11e225f..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement12Test.cs +++ /dev/null @@ -1,345 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Lib9c.Tests.Fixtures.TableCSV; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Xunit; - using static SerializeKeys; - - public class ItemEnhancement12Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccount _initialState; - - public ItemEnhancement12Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(EquipmentItemSheet)] = EquipmentItemSheetFixture.LegacyEquipmentItemSheet; - sheets[nameof(EnhancementCostSheetV3)] = - EquipmentItemSheetFixture.LegacyEnhancementCostSheetV3; - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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); - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - )); - - var context = new ActionContext(); - _initialState = new Account(MockState.Empty) - .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 * 100_000_000_000) - .TransferAsset( - context, - Addresses.GoldCurrency, - _agentAddress, - gold.Currency * 3_000_000 - ); - - Assert.Equal( - gold.Currency * 99_997_000_000, - _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency) - ); - Assert.Equal( - gold.Currency * 3_000_000, - _initialState.GetBalance(_agentAddress, gold.Currency) - ); - - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - [Theory] - // from 0 to 0 using one level 0 material - [InlineData(0, false, 0, 0, false, 1)] - [InlineData(0, false, 0, 0, true, 1)] - [InlineData(0, true, 0, 0, false, 1)] - [InlineData(0, true, 0, 0, true, 1)] - // from 0 to 1 using two level 0 material - [InlineData(0, false, 1, 0, false, 3)] - [InlineData(0, false, 1, 0, true, 3)] - [InlineData(0, true, 1, 0, false, 3)] - [InlineData(0, true, 1, 0, true, 3)] - // from 0 to N using multiple level 0 materials - [InlineData(0, false, 2, 0, false, 7)] - [InlineData(0, false, 4, 0, false, 31)] - [InlineData(0, false, 2, 0, true, 7)] - [InlineData(0, false, 4, 0, true, 31)] - [InlineData(0, true, 2, 0, false, 7)] - [InlineData(0, true, 4, 0, false, 31)] - [InlineData(0, true, 2, 0, true, 7)] - [InlineData(0, true, 4, 0, true, 31)] - // from K to K with material(s). Check requiredBlock == 0 - [InlineData(10, false, 10, 0, false, 1)] - [InlineData(10, false, 10, 0, true, 1)] - [InlineData(10, true, 10, 0, false, 1)] - [InlineData(10, true, 10, 0, true, 1)] - // from K to N using one level X material - [InlineData(5, false, 6, 6, false, 1)] - [InlineData(5, false, 6, 6, true, 1)] - [InlineData(5, true, 6, 6, false, 1)] - [InlineData(5, true, 6, 6, true, 1)] - // from K to N using multiple materials - [InlineData(5, false, 7, 4, false, 6)] - [InlineData(5, false, 9, 7, false, 5)] - [InlineData(5, false, 7, 4, true, 6)] - [InlineData(5, false, 9, 7, true, 5)] - [InlineData(5, true, 7, 4, false, 6)] - [InlineData(5, true, 9, 7, false, 5)] - [InlineData(5, true, 7, 4, true, 6)] - [InlineData(5, true, 9, 7, true, 5)] - // from 20 to 21 (just to reach level 21 exp) - [InlineData(20, false, 21, 20, false, 1)] - [InlineData(20, false, 21, 20, true, 1)] - [InlineData(20, true, 21, 20, false, 1)] - [InlineData(20, true, 21, 20, true, 1)] - // from 20 to 21 (over level 21) - [InlineData(20, false, 21, 20, false, 2)] - [InlineData(20, false, 21, 20, true, 2)] - [InlineData(20, true, 21, 20, false, 2)] - [InlineData(20, true, 21, 20, true, 2)] - // from 21 to 21 (no level up) - [InlineData(21, false, 21, 1, false, 1)] - [InlineData(21, false, 21, 21, false, 1)] - [InlineData(21, false, 21, 1, true, 1)] - [InlineData(21, false, 21, 21, true, 1)] - [InlineData(21, true, 21, 1, false, 1)] - [InlineData(21, true, 21, 21, false, 1)] - [InlineData(21, true, 21, 1, true, 1)] - [InlineData(21, true, 21, 21, true, 1)] - // Test: change of exp, change of level, required block, NCG price - public void Execute( - int startLevel, - bool oldStart, - int expectedLevel, - int materialLevel, - bool oldMaterial, - int materialCount) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Id == 10110000); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, startLevel); - if (startLevel == 0) - { - equipment.Exp = (long)row.Exp!; - } - else - { - equipment.Exp = _tableSheets.EnhancementCostSheetV3.OrderedList.First(r => - r.ItemSubType == equipment.ItemSubType && r.Grade == equipment.Grade && - r.Level == equipment.level).Exp; - } - - var startExp = equipment.Exp; - if (oldStart) - { - equipment.Exp = 0L; - } - - _avatarState.inventory.AddItem(equipment, count: 1); - - var expectedTargetRow = _tableSheets.EnhancementCostSheetV3.OrderedList.FirstOrDefault( - r => - r.Grade == equipment.Grade && r.ItemSubType == equipment.ItemSubType && - r.Level == expectedLevel); - var startRow = _tableSheets.EnhancementCostSheetV3.OrderedList.FirstOrDefault(r => - r.Grade == equipment.Grade && r.ItemSubType == equipment.ItemSubType && - r.Level == startLevel); - var expectedCost = (expectedTargetRow?.Cost ?? 0) - (startRow?.Cost ?? 0); - var expectedBlockIndex = - (expectedTargetRow?.RequiredBlockIndex ?? 0) - (startRow?.RequiredBlockIndex ?? 0); - - var expectedExpIncrement = 0L; - var materialIds = new List(); - for (var i = 0; i < materialCount; i++) - { - var materialId = Guid.NewGuid(); - materialIds.Add(materialId); - var material = - (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, materialLevel); - if (materialLevel == 0) - { - material.Exp = (long)row.Exp!; - } - else - { - material.Exp = _tableSheets.EnhancementCostSheetV3.OrderedList.First(r => - r.ItemSubType == material.ItemSubType && r.Grade == material.Grade && - r.Level == material.level).Exp; - } - - expectedExpIncrement += material.Exp; - if (oldMaterial) - { - material.Exp = 0L; - } - - _avatarState.inventory.AddItem(material, count: 1); - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - - 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(startLevel, equipment.level); - - _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 ItemEnhancement12() - { - itemId = default, - materialIds = materialIds, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = 0, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(startExp + expectedExpIncrement, resultEquipment.Exp); - Assert.Equal( - (3_000_000 - expectedCost) * _currency, - nextState.GetBalance(_agentAddress, _currency) - ); - - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(1); - var feeStoreAddress = - Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal( - expectedCost * _currency, - nextState.GetBalance(feeStoreAddress, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement12.ResultModel)slot.Result; - if (startLevel != expectedLevel) - { - var baseMinAtk = (decimal)preItemUsable.StatsMap.BaseATK; - var baseMaxAtk = (decimal)preItemUsable.StatsMap.BaseATK; - var extraMinAtk = (decimal)preItemUsable.StatsMap.AdditionalATK; - var extraMaxAtk = (decimal)preItemUsable.StatsMap.AdditionalATK; - - for (var i = startLevel + 1; i <= expectedLevel; i++) - { - var currentRow = _tableSheets.EnhancementCostSheetV3.OrderedList - .First(x => - x.Grade == 1 && x.ItemSubType == equipment.ItemSubType && x.Level == i); - - baseMinAtk *= currentRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1; - baseMaxAtk *= currentRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1; - extraMinAtk *= currentRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1; - extraMaxAtk *= currentRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1; - } - - Assert.InRange( - resultEquipment.StatsMap.ATK, - baseMinAtk + extraMinAtk, - baseMaxAtk + extraMaxAtk + 1 - ); - } - - Assert.Equal( - expectedBlockIndex + 1, // +1 for execution - resultEquipment.RequiredBlockIndex - ); - Assert.Equal(preItemUsable.ItemId, slotResult.preItemUsable.ItemId); - Assert.Equal(preItemUsable.ItemId, resultEquipment.ItemId); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement13Test.cs b/.Lib9c.Tests/Action/ItemEnhancement13Test.cs deleted file mode 100644 index 9c280c1eed..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement13Test.cs +++ /dev/null @@ -1,381 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using Bencodex.Types; - using Lib9c.Tests.Fixtures.TableCSV; - using Lib9c.Tests.Fixtures.TableCSV.Cost; - using Lib9c.Tests.Fixtures.TableCSV.Item; - using Lib9c.Tests.Util; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Extensions; - using Nekoyume.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - using static SerializeKeys; - - public class ItemEnhancement13Test - { - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private IAccount _initialState; - - public ItemEnhancement13Test() - { - _initialState = new Account(MockState.Empty); - Dictionary sheets; - (_initialState, sheets) = InitializeUtil.InitializeTableSheets( - _initialState, - sheetsOverride: new Dictionary - { - { - "EnhancementCostSheetV3", - EnhancementCostSheetFixtures.V3 - }, - }); - _tableSheets = new TableSheets(sheets); - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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); - var slotAddress = _avatarAddress.Derive(string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - 0 - )); - - var context = new ActionContext(); - _initialState = _initialState - .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 * 100_000_000_000) - .TransferAsset( - context, - Addresses.GoldCurrency, - _agentAddress, - gold.Currency * 3_000_000 - ); - - Assert.Equal( - gold.Currency * 99_997_000_000, - _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency) - ); - Assert.Equal( - gold.Currency * 3_000_000, - _initialState.GetBalance(_agentAddress, gold.Currency) - ); - } - - [Theory] - // from 0 to 0 using one level 0 material - [InlineData(0, false, 0, false, 1)] - [InlineData(0, false, 0, true, 1)] - [InlineData(0, true, 0, false, 1)] - [InlineData(0, true, 0, true, 1)] - // from 0 to 1 using two level 0 material - [InlineData(0, false, 0, false, 3)] - [InlineData(0, false, 0, true, 3)] - [InlineData(0, true, 0, false, 3)] - [InlineData(0, true, 0, true, 3)] - // // Duplicated > from 0 to 0 - [InlineData(0, false, 0, false, 3, true)] - [InlineData(0, false, 0, true, 3, true)] - [InlineData(0, true, 0, false, 3, true)] - [InlineData(0, true, 0, true, 3, true)] - // from 0 to N using multiple level 0 materials - [InlineData(0, false, 0, false, 7)] - [InlineData(0, false, 0, false, 31)] - [InlineData(0, false, 0, true, 7)] - [InlineData(0, false, 0, true, 31)] - [InlineData(0, true, 0, false, 7)] - [InlineData(0, true, 0, false, 31)] - [InlineData(0, true, 0, true, 7)] - [InlineData(0, true, 0, true, 31)] - // // Duplicated > from 0 to 0 - [InlineData(0, false, 0, false, 7, true)] - [InlineData(0, false, 0, false, 31, true)] - [InlineData(0, false, 0, true, 7, true)] - [InlineData(0, false, 0, true, 31, true)] - [InlineData(0, true, 0, false, 7, true)] - [InlineData(0, true, 0, false, 31, true)] - [InlineData(0, true, 0, true, 7, true)] - [InlineData(0, true, 0, true, 31, true)] - // from K to K with material(s). Check requiredBlock == 0 - [InlineData(10, false, 0, false, 1)] - [InlineData(10, false, 0, true, 1)] - [InlineData(10, true, 0, false, 1)] - [InlineData(10, true, 0, true, 1)] - // from K to N using one level X material - [InlineData(5, false, 6, false, 1)] - [InlineData(5, false, 6, true, 1)] - [InlineData(5, true, 6, false, 1)] - [InlineData(5, true, 6, true, 1)] - // from K to N using multiple materials - [InlineData(5, false, 4, false, 6)] - [InlineData(5, false, 7, false, 5)] - [InlineData(5, false, 4, true, 6)] - [InlineData(5, false, 7, true, 5)] - [InlineData(5, true, 4, false, 6)] - [InlineData(5, true, 7, false, 5)] - [InlineData(5, true, 4, true, 6)] - [InlineData(5, true, 7, true, 5)] - // // Duplicated: from K to K - [InlineData(5, true, 4, true, 6, true)] - [InlineData(5, true, 7, true, 5, true)] - [InlineData(5, true, 4, false, 6, true)] - [InlineData(5, true, 7, false, 5, true)] - [InlineData(5, false, 4, true, 6, true)] - [InlineData(5, false, 7, true, 5, true)] - [InlineData(5, false, 4, false, 6, true)] - [InlineData(5, false, 7, false, 5, true)] - // from 20 to 21 (just to reach level 21 exp) - [InlineData(20, false, 20, false, 1)] - [InlineData(20, false, 20, true, 1)] - [InlineData(20, true, 20, false, 1)] - [InlineData(20, true, 20, true, 1)] - // from 20 to 21 (over level 21) - [InlineData(20, false, 20, false, 2)] - [InlineData(20, false, 20, true, 2)] - [InlineData(20, true, 20, false, 2)] - [InlineData(20, true, 20, true, 2)] - // from 21 to 21 (no level up) - [InlineData(21, false, 1, false, 1)] - [InlineData(21, false, 21, false, 1)] - [InlineData(21, false, 1, true, 1)] - [InlineData(21, false, 21, true, 1)] - [InlineData(21, true, 1, false, 1)] - [InlineData(21, true, 21, false, 1)] - [InlineData(21, true, 1, true, 1)] - [InlineData(21, true, 21, true, 1)] - public void Execute( - int startLevel, - bool oldStart, - int materialLevel, - bool oldMaterial, - int materialCount, - bool duplicated = false - ) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Id == 10110000); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, startLevel); - if (startLevel == 0) - { - equipment.Exp = (long)row.Exp!; - } - else - { - equipment.Exp = _tableSheets.EnhancementCostSheetV3.OrderedList.First(r => - r.ItemSubType == equipment.ItemSubType && r.Grade == equipment.Grade && - r.Level == equipment.level).Exp; - } - - var startExp = equipment.Exp; - if (oldStart) - { - equipment.Exp = 0L; - } - - _avatarState.inventory.AddItem(equipment, count: 1); - - var startRow = _tableSheets.EnhancementCostSheetV3.OrderedList.FirstOrDefault(r => - r.Grade == equipment.Grade && r.ItemSubType == equipment.ItemSubType && - r.Level == startLevel); - var expectedExpIncrement = 0L; - var materialIds = new List(); - var duplicatedGuid = Guid.NewGuid(); - for (var i = 0; i < materialCount; i++) - { - var materialId = duplicated ? duplicatedGuid : Guid.NewGuid(); - materialIds.Add(materialId); - var material = - (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, materialLevel); - if (materialLevel == 0) - { - material.Exp = (long)row.Exp!; - } - else - { - material.Exp = _tableSheets.EnhancementCostSheetV3.OrderedList.First(r => - r.ItemSubType == material.ItemSubType && r.Grade == material.Grade && - r.Level == material.level).Exp; - } - - if (!(duplicated && i > 0)) - { - expectedExpIncrement += material.Exp; - } - - if (oldMaterial) - { - material.Exp = 0L; - } - - _avatarState.inventory.AddItem(material, count: 1); - } - - var result = new CombinationConsumable5.ResultModel() - { - id = default, - gold = 0, - actionPoint = 0, - recipeId = 1, - materials = new Dictionary(), - itemUsable = equipment, - }; - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - - 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(startLevel, equipment.level); - - _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 ItemEnhancement13 - { - itemId = default, - materialIds = materialIds, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = 0, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var level = resultEquipment.level; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - var expectedTargetRow = _tableSheets.EnhancementCostSheetV3.OrderedList.FirstOrDefault( - r => r.Grade == equipment.Grade && r.ItemSubType == equipment.ItemSubType && - r.Level == level); - var expectedCost = (expectedTargetRow?.Cost ?? 0) - (startRow?.Cost ?? 0); - var expectedBlockIndex = - (expectedTargetRow?.RequiredBlockIndex ?? 0) - (startRow?.RequiredBlockIndex ?? 0); - Assert.Equal(default, resultEquipment.ItemId); - Assert.Equal(startExp + expectedExpIncrement, resultEquipment.Exp); - Assert.Equal( - (3_000_000 - expectedCost) * _currency, - nextState.GetBalance(_agentAddress, _currency) - ); - - var arenaSheet = _tableSheets.ArenaSheet; - var arenaData = arenaSheet.GetRoundByBlockIndex(1); - var feeStoreAddress = - Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - Assert.Equal( - expectedCost * _currency, - nextState.GetBalance(feeStoreAddress, _currency) - ); - Assert.Equal(30, nextAvatarState.mailBox.Count); - - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement13.ResultModel)slot.Result; - if (startLevel != level) - { - var baseMinAtk = (decimal)preItemUsable.StatsMap.BaseATK; - var baseMaxAtk = (decimal)preItemUsable.StatsMap.BaseATK; - var extraMinAtk = (decimal)preItemUsable.StatsMap.AdditionalATK; - var extraMaxAtk = (decimal)preItemUsable.StatsMap.AdditionalATK; - - for (var i = startLevel + 1; i <= level; i++) - { - var currentRow = _tableSheets.EnhancementCostSheetV3.OrderedList - .First(x => - x.Grade == 1 && x.ItemSubType == equipment.ItemSubType && x.Level == i); - - baseMinAtk *= currentRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1; - baseMaxAtk *= currentRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1; - extraMinAtk *= currentRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1; - extraMaxAtk *= currentRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1; - } - - Assert.InRange( - resultEquipment.StatsMap.ATK, - baseMinAtk + extraMinAtk, - baseMaxAtk + extraMaxAtk + 1 - ); - } - - Assert.Equal( - expectedBlockIndex + 1, // +1 for execution - resultEquipment.RequiredBlockIndex - ); - Assert.Equal(preItemUsable.ItemId, slotResult.preItemUsable.ItemId); - Assert.Equal(preItemUsable.ItemId, resultEquipment.ItemId); - Assert.Equal(expectedCost, slotResult.gold); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement2Test.cs b/.Lib9c.Tests/Action/ItemEnhancement2Test.cs deleted file mode 100644 index 8ec68124a8..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement2Test.cs +++ /dev/null @@ -1,442 +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 IAccount _initialState; - - public ItemEnhancement2Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 Account(MockState.Empty), - 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()); - } - } -} diff --git a/.Lib9c.Tests/Action/ItemEnhancement3Test.cs b/.Lib9c.Tests/Action/ItemEnhancement3Test.cs deleted file mode 100644 index 591cc95854..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 IAccount _initialState; - - public ItemEnhancement3Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 1657699325..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 IAccount _initialState; - - public ItemEnhancement4Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 308ba37edc..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 IAccount _initialState; - - public ItemEnhancement5Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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 f27f28c0f1..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 IAccount _initialState; - - public ItemEnhancement6Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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/ItemEnhancement7Test.cs b/.Lib9c.Tests/Action/ItemEnhancement7Test.cs deleted file mode 100644 index df073d09a8..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement7Test.cs +++ /dev/null @@ -1,169 +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 ItemEnhancement7Test - { - 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 IAccount _initialState; - - public ItemEnhancement7Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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 ItemEnhancement7() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - 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 cc8c01bc43..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement8Test.cs +++ /dev/null @@ -1,169 +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 IAccount _initialState; - - public ItemEnhancement8Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = _random.Seed, - }); - - 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/ItemEnhancement9Test.cs b/.Lib9c.Tests/Action/ItemEnhancement9Test.cs deleted file mode 100644 index 4be765dc0c..0000000000 --- a/.Lib9c.Tests/Action/ItemEnhancement9Test.cs +++ /dev/null @@ -1,194 +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.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Xunit; - using static SerializeKeys; - - public class ItemEnhancement9Test - { - 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 IAccount _initialState; - - public ItemEnhancement9Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _random = new TestRandom(); - _tableSheets = new TableSheets(sheets); - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, 1000, true)] - [InlineData(6, 980, true)] - [InlineData(0, 1000, false)] - [InlineData(6, 980, false)] - public void Execute(int level, 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, - }; - var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); - - 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 ItemEnhancement9() - { - itemId = default, - materialId = materialId, - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - var nextState = action.Execute(new ActionContext() - { - PreviousState = _initialState, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); - var resultEquipment = (Equipment)slotState.Result.itemUsable; - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - 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.EnhancementCostSheetV2 - .OrderedList - .FirstOrDefault(x => x.Grade == grade && x.Level == resultEquipment.level); - var stateDict = (Dictionary)nextState.GetState(slotAddress); - var slot = new CombinationSlotState(stateDict); - var slotResult = (ItemEnhancement9.ResultModel)slot.Result; - - switch ((ItemEnhancement9.EnhancementResult)slotResult.enhancementResult) - { - case ItemEnhancement9.EnhancementResult.GreatSuccess: - var baseAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.Equal((int)(baseAtk + extraAtk), resultEquipment.StatsMap.ATK); - Assert.Equal(preItemUsable.level + 1, resultEquipment.level); - break; - case ItemEnhancement9.EnhancementResult.Success: - var baseMinAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1); - var baseMaxAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraMinAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1); - var extraMaxAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.InRange(resultEquipment.StatsMap.ATK, (int)(baseMinAtk + extraMinAtk), (int)(baseMaxAtk + extraMaxAtk) + 1); - Assert.Equal(preItemUsable.level + 1, resultEquipment.level); - break; - case ItemEnhancement9.EnhancementResult.Fail: - Assert.Equal(preItemUsable.StatsMap.ATK, resultEquipment.StatsMap.ATK); - Assert.Equal(preItemUsable.level, resultEquipment.level); - break; - } - - Assert.Equal(preItemUsable.ItemId, slotResult.preItemUsable.ItemId); - Assert.Equal(preItemUsable.ItemId, resultEquipment.ItemId); - Assert.Equal(costRow.Cost, slotResult.gold); - } - } -} diff --git a/.Lib9c.Tests/Action/JoinArena2Test.cs b/.Lib9c.Tests/Action/JoinArena2Test.cs deleted file mode 100644 index 10947477f0..0000000000 --- a/.Lib9c.Tests/Action/JoinArena2Test.cs +++ /dev/null @@ -1,510 +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.Item; - using Nekoyume.Model.State; - using Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class JoinArena2Test - { - private readonly Dictionary _sheets; - private readonly TableSheets _tableSheets; - private readonly Address _signer; - private readonly Address _signer2; - private readonly Address _avatarAddress; - private readonly Address _avatar2Address; - private readonly IRandom _random; - private readonly Currency _currency; - private IAccount _state; - - public JoinArena2Test(ITestOutputHelper outputHelper) - { - _random = new TestRandom(); - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _state = new Account(MockState.Empty); - - _signer = new PrivateKey().Address; - _avatarAddress = _signer.Derive("avatar"); - var sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(sheets); - var rankingMapAddress = new PrivateKey().Address; - var agentState = new AgentState(_signer); - var avatarState = new AvatarState( - _avatarAddress, - _signer, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - GameConfig.RequireClearedStageLevel.ActionsInRankingBoard), - }; - agentState.avatarAddresses[0] = _avatarAddress; - avatarState.level = GameConfig.RequireClearedStageLevel.ActionsInRankingBoard; - - _signer2 = new PrivateKey().Address; - _avatar2Address = _signer2.Derive("avatar"); - var agent2State = new AgentState(_signer2); - - var avatar2State = new AvatarState( - _avatar2Address, - _signer2, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - rankingMapAddress) - { - worldInformation = new WorldInformation( - 0, - tableSheets.WorldSheet, - 1), - }; - agent2State.avatarAddresses[0] = _avatar2Address; -#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); -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - _currency = Currency.Legacy("CRYSTAL", 18, null); -#pragma warning restore CS0618 - - _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(_signer2, agent2State.Serialize()) - .SetState(_avatar2Address.Derive(LegacyInventoryKey), avatar2State.inventory.Serialize()) - .SetState(_avatar2Address.Derive(LegacyWorldInformationKey), avatar2State.worldInformation.Serialize()) - .SetState(_avatar2Address.Derive(LegacyQuestListKey), avatar2State.questList.Serialize()) - .SetState(_avatar2Address, avatar2State.SerializeV2()) - .SetState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); - - foreach ((string key, string value) in sheets) - { - _state = _state - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - } - - 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 AvatarState GetAvatarState(AvatarState avatarState, out List equipments, out List costumes) - { - avatarState.level = 999; - (equipments, costumes) = GetDummyItems(avatarState); - - _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()); - - return avatarState; - } - - public AvatarState AddMedal(AvatarState avatarState, ArenaSheet.Row row, int count = 1) - { - var materialSheet = _state.GetSheet(); - foreach (var data in row.Round) - { - if (!data.ArenaType.Equals(ArenaType.Season)) - { - continue; - } - - var itemId = ArenaHelper.GetMedalItemId(data.ChampionshipId, data.Round); - var material = ItemFactory.CreateMaterial(materialSheet, itemId); - avatarState.inventory.AddItem(material, count); - } - - _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()); - - return avatarState; - } - - [Theory] - [InlineData(0, 1, 1, "0")] - [InlineData(4_479_999L, 1, 2, "998001")] - [InlineData(4_480_001L, 1, 2, "998001")] - [InlineData(100, 1, 8, "998001")] - public void Execute(long blockIndex, int championshipId, int round, string balance) - { - var arenaSheet = _state.GetSheet(); - if (!arenaSheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - avatarState = GetAvatarState(avatarState, out var equipments, out var costumes); - avatarState = AddMedal(avatarState, row, 80); - - var context = new ActionContext(); - var state = (balance == "0") - ? _state - : _state.MintAsset(context, _signer, FungibleAssetValue.Parse(_currency, balance)); - - var action = new JoinArena2() - { - championshipId = championshipId, - round = round, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _signer, - RandomSeed = _random.Seed, - BlockIndex = blockIndex, - }); - - // ArenaParticipants - var arenaParticipantsAdr = ArenaParticipants.DeriveAddress(championshipId, round); - var serializedArenaParticipants = (List)state.GetState(arenaParticipantsAdr); - var arenaParticipants = new ArenaParticipants(serializedArenaParticipants); - - Assert.Equal(arenaParticipantsAdr, arenaParticipants.Address); - Assert.Equal(_avatarAddress, arenaParticipants.AvatarAddresses.First()); - - // ArenaAvatarState - var arenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(_avatarAddress); - var serializedArenaAvatarState = (List)state.GetState(arenaAvatarStateAdr); - var arenaAvatarState = new ArenaAvatarState(serializedArenaAvatarState); - - foreach (var guid in arenaAvatarState.Equipments) - { - Assert.Contains(avatarState.inventory.Equipments, x => x.ItemId.Equals(guid)); - } - - foreach (var guid in arenaAvatarState.Costumes) - { - Assert.Contains(avatarState.inventory.Costumes, x => x.ItemId.Equals(guid)); - } - - Assert.Equal(arenaAvatarStateAdr, arenaAvatarState.Address); - - // ArenaScore - var arenaScoreAdr = ArenaScore.DeriveAddress(_avatarAddress, championshipId, round); - var serializedArenaScore = (List)state.GetState(arenaScoreAdr); - var arenaScore = new ArenaScore(serializedArenaScore); - - Assert.Equal(arenaScoreAdr, arenaScore.Address); - Assert.Equal(GameConfig.ArenaScoreDefault, arenaScore.Score); - - // ArenaInformation - var arenaInformationAdr = ArenaInformation.DeriveAddress(_avatarAddress, championshipId, round); - var serializedArenaInformation = (List)state.GetState(arenaInformationAdr); - var arenaInformation = new ArenaInformation(serializedArenaInformation); - - Assert.Equal(arenaInformationAdr, arenaInformation.Address); - Assert.Equal(0, arenaInformation.Win); - Assert.Equal(0, arenaInformation.Lose); - Assert.Equal(ArenaInformation.MaxTicketCount, arenaInformation.Ticket); - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException($"{nameof(JoinArena1)} : {row.ChampionshipId} / {round}"); - } - - Assert.Equal(0 * _currency, state.GetBalance(_signer, _currency)); - } - - [Theory] - [InlineData(9999)] - public void Execute_SheetRowNotFoundException(int championshipId) - { - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - avatarState = GetAvatarState(avatarState, out var equipments, out var costumes); - var state = _state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new JoinArena2() - { - championshipId = championshipId, - round = 1, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - })); - } - - [Theory] - [InlineData(123)] - public void Execute_RoundNotFoundByIdsException(int round) - { - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - avatarState = GetAvatarState(avatarState, out var equipments, out var costumes); - var state = _state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new JoinArena2() - { - championshipId = 1, - round = round, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - BlockIndex = 1, - })); - } - - [Theory] - [InlineData(8)] - public void Execute_NotEnoughMedalException(int round) - { - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - GetAvatarState(avatarState, out var equipments, out var costumes); - var preCurrency = 99800100000 * _currency; - var context = new ActionContext(); - var state = _state.MintAsset(context, _signer, preCurrency); - - var action = new JoinArena2() - { - championshipId = 1, - round = round, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - BlockIndex = 100, - })); - } - - [Theory] - [InlineData(6, 0)] // discounted_entrance_fee - [InlineData(8, 100)] // entrance_fee - public void Execute_NotEnoughFungibleAssetValueException(int round, long blockIndex) - { - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - GetAvatarState(avatarState, out var equipments, out var costumes); - var state = _state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new JoinArena2() - { - championshipId = 1, - round = round, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_ArenaScoreAlreadyContainsException() - { - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - avatarState = GetAvatarState(avatarState, out var equipments, out var costumes); - var state = _state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var action = new JoinArena2() - { - championshipId = 1, - round = 1, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - state = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _signer, - RandomSeed = _random.Seed, - BlockIndex = 1, - }); - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - BlockIndex = 2, - })); - } - - [Fact] - public void Execute_ArenaScoreAlreadyContainsException2() - { - const int championshipId = 1; - const int round = 1; - - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - avatarState = GetAvatarState(avatarState, out var equipments, out var costumes); - var state = _state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var arenaScoreAdr = ArenaScore.DeriveAddress(_avatarAddress, championshipId, round); - var arenaScore = new ArenaScore(_avatarAddress, championshipId, round); - state = state.SetState(arenaScoreAdr, arenaScore.Serialize()); - - var action = new JoinArena2() - { - championshipId = championshipId, - round = round, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_ArenaInformationAlreadyContainsException() - { - const int championshipId = 1; - const int round = 1; - - var avatarState = _state.GetAvatarStateV2(_avatarAddress); - avatarState = GetAvatarState(avatarState, out var equipments, out var costumes); - var state = _state.SetState(_avatarAddress, avatarState.SerializeV2()); - - var arenaInformationAdr = ArenaInformation.DeriveAddress(_avatarAddress, championshipId, round); - var arenaInformation = new ArenaInformation(_avatarAddress, championshipId, round); - state = state.SetState(arenaInformationAdr, arenaInformation.Serialize()); - - var action = new JoinArena2() - { - championshipId = championshipId, - round = round, - costumes = costumes, - equipments = equipments, - runeInfos = new List(), - avatarAddress = _avatarAddress, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = state, - Signer = _signer, - RandomSeed = 0, - BlockIndex = 1, - })); - } - - [Fact] - public void Execute_NotEnoughClearedStageLevelException() - { - var action = new JoinArena2() - { - championshipId = 1, - round = 1, - costumes = new List(), - equipments = new List(), - runeInfos = new List(), - avatarAddress = _avatar2Address, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = _state, - Signer = _signer2, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/JoinArena3Test.cs b/.Lib9c.Tests/Action/JoinArena3Test.cs index 9a8a6e27ae..6c06089b90 100644 --- a/.Lib9c.Tests/Action/JoinArena3Test.cs +++ b/.Lib9c.Tests/Action/JoinArena3Test.cs @@ -273,7 +273,7 @@ public void Execute(long blockIndex, int championshipId, int round, string balan if (!row.TryGetRound(round, out var roundData)) { - throw new RoundNotFoundException($"{nameof(JoinArena1)} : {row.ChampionshipId} / {round}"); + throw new RoundNotFoundException($"{nameof(JoinArena3)} : {row.ChampionshipId} / {round}"); } Assert.Equal(0 * _currency, state.GetBalance(_signer, _currency)); diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle0Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle0Test.cs deleted file mode 100644 index b0154daa62..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle0Test.cs +++ /dev/null @@ -1,596 +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 IAccount _initialState; - - public MimisbrunnrBattle0Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - - 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 8d6a632633..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle10Test.cs +++ /dev/null @@ -1,843 +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 IAccount _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.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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, - RandomSeed = random.Seed, - })); - } - } - - [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.SetNull(Addresses.TableSheet.Derive(key)); - } - } - - 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, - RandomSeed = 0, - 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); - } - - IAccount 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, - RandomSeed = 0, - 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 a4394ca7e0..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle11Test.cs +++ /dev/null @@ -1,740 +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 IAccount _initialState; - private readonly Dictionary _sheets; - - public MimisbrunnrBattle11Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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, - RandomSeed = random.Seed, - })); - } - } - - [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); - } - - IAccount 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, - RandomSeed = 0, - 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 7d5bcd621f..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle12Test.cs +++ /dev/null @@ -1,857 +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 IAccount _initialState; - private readonly Dictionary _sheets; - - public MimisbrunnrBattle12Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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, - RandomSeed = random.Seed, - })); - } - } - - [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); - } - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - BlockIndex = ActionObsoleteConfig.V100301ExecutedBlockIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle2Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle2Test.cs deleted file mode 100644 index ed9cc4dc4c..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle2Test.cs +++ /dev/null @@ -1,596 +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 IAccount _initialState; - - public MimisbrunnrBattle2Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - - 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 c8875f223c..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle3Test.cs +++ /dev/null @@ -1,597 +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 IAccount _initialState; - - public MimisbrunnrBattle3Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - - 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 1dd8a35604..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle4Test.cs +++ /dev/null @@ -1,586 +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 IAccount _initialState; - - public MimisbrunnrBattle4Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle5Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle5Test.cs deleted file mode 100644 index 1c99ef85f5..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle5Test.cs +++ /dev/null @@ -1,571 +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 IAccount _initialState; - - public MimisbrunnrBattle5Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle6Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle6Test.cs deleted file mode 100644 index f24a425d30..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle6Test.cs +++ /dev/null @@ -1,728 +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 IAccount _initialState; - - public MimisbrunnrBattle6Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle7Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle7Test.cs deleted file mode 100644 index 388930919d..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle7Test.cs +++ /dev/null @@ -1,728 +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 IAccount _initialState; - - public MimisbrunnrBattle7Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle8Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle8Test.cs deleted file mode 100644 index 365fdaf029..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle8Test.cs +++ /dev/null @@ -1,679 +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 IAccount _initialState; - - public MimisbrunnrBattle8Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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); - } - - IAccount 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, - RandomSeed = 0, - 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); - } - } -} diff --git a/.Lib9c.Tests/Action/MimisbrunnrBattle9Test.cs b/.Lib9c.Tests/Action/MimisbrunnrBattle9Test.cs deleted file mode 100644 index 3077801864..0000000000 --- a/.Lib9c.Tests/Action/MimisbrunnrBattle9Test.cs +++ /dev/null @@ -1,730 +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 IAccount _initialState; - - public MimisbrunnrBattle9Test() - { - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - - var privateKey = new PrivateKey(); - _agentAddress = privateKey.PublicKey.Address; - 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 Account(MockState.Empty) - .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, - RandomSeed = 0, - 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, - RandomSeed = 0, - }); - }); - } - - [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 Account(MockState.Empty), - 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, - RandomSeed = 0, - }); - }); - } - - [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, RandomSeed = 0, - }); - }); - } - - /// - /// 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, - RandomSeed = 0, - }); - } - - [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, - RandomSeed = random.Seed, - })); - } - } - - [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); - } - - IAccount 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, - RandomSeed = 0, - 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/Raid1Test.cs b/.Lib9c.Tests/Action/Raid1Test.cs deleted file mode 100644 index c7be2c6550..0000000000 --- a/.Lib9c.Tests/Action/Raid1Test.cs +++ /dev/null @@ -1,565 +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().Address; - _avatarAddress = new PrivateKey().Address; -#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(); - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - 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, - RandomSeed = 0, - 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); - - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - 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); - - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = 0, - Signer = _agentAddress, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid2Test.cs b/.Lib9c.Tests/Action/Raid2Test.cs deleted file mode 100644 index 486e1cee0d..0000000000 --- a/.Lib9c.Tests/Action/Raid2Test.cs +++ /dev/null @@ -1,526 +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().Address; - _avatarAddress = new PrivateKey().Address; -#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(); - IAccount state = new Account(MockState.Empty) - .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().Address.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, - RandomSeed = randomSeed, - 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, - RandomSeed = 0, - 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); - - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - 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 66f54c728f..0000000000 --- a/.Lib9c.Tests/Action/Raid3Test.cs +++ /dev/null @@ -1,528 +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().Address; - _avatarAddress = new PrivateKey().Address; -#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(); - IAccount state = new Account(MockState.Empty) - .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().Address.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, - RandomSeed = randomSeed, - 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, - RandomSeed = 0, - 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); - - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - 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 7a45eb3c70..0000000000 --- a/.Lib9c.Tests/Action/Raid4Test.cs +++ /dev/null @@ -1,634 +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().Address; - _avatarAddress = new PrivateKey().Address; -#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(); - IAccount state = new Account(MockState.Empty) - .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().Address.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, - RandomSeed = randomSeed, - 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, - RandomSeed = 0, - }); - } - - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - RandomSeed = 0, - 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); - - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - 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); - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - Signer = _agentAddress, - }; - - IAccount 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 61418cbcc2..0000000000 --- a/.Lib9c.Tests/Action/Raid5Test.cs +++ /dev/null @@ -1,636 +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().Address; - _avatarAddress = new PrivateKey().Address; -#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, -10368, 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(); - IAccount state = new Account(MockState.Empty) - .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().Address.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, - RandomSeed = randomSeed, - 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, - RandomSeed = 0, - }); - } - - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - RandomSeed = 0, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid5 - { - 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); - - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - 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 Raid5 - { - 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); - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - Signer = _agentAddress, - }; - - IAccount nextState; - var exception = Record.Exception(() => nextState = action.Execute(ctx)); - Assert.Null(exception); - } - } -} diff --git a/.Lib9c.Tests/Action/Raid6Test.cs b/.Lib9c.Tests/Action/Raid6Test.cs deleted file mode 100644 index b377efedec..0000000000 --- a/.Lib9c.Tests/Action/Raid6Test.cs +++ /dev/null @@ -1,636 +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 Raid6Test - { - private readonly Dictionary _sheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly TableSheets _tableSheets; - private readonly Currency _goldCurrency; - - public Raid6Test() - { - _sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(_sheets); - _agentAddress = new PrivateKey().Address; - _avatarAddress = new PrivateKey().Address; -#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, -10368, 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 Raid6 - { - 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(); - IAccount state = new Account(MockState.Empty) - .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().Address.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, - RandomSeed = randomSeed, - 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 RaidSimulator( - 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, - RandomSeed = 0, - }); - } - - Assert.Throws(exc, () => action.Execute(new ActionContext - { - BlockIndex = blockIndex + executeOffset, - PreviousState = state, - RandomSeed = 0, - Signer = _agentAddress, - })); - } - } - - [Fact] - public void Execute_With_Reward() - { - var action = new Raid6 - { - 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); - - IAccount state = new Account(MockState.Empty) - .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 RaidSimulator( - 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, - RandomSeed = randomSeed, - 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 Raid6 - { - 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); - IAccount state = new Account(MockState.Empty) - .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, - RandomSeed = randomSeed, - Signer = _agentAddress, - }; - - IAccount 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 45a2f1c30a..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle0Test.cs +++ /dev/null @@ -1,419 +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 IAccount _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 Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle10Test.cs b/.Lib9c.Tests/Action/RankingBattle10Test.cs deleted file mode 100644 index f7d07c4f81..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle10Test.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 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 IAccount _initialState; - - public RankingBattle10Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle11Test.cs b/.Lib9c.Tests/Action/RankingBattle11Test.cs deleted file mode 100644 index ff9b1aba9a..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle11Test.cs +++ /dev/null @@ -1,742 +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.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 RankingBattle11Test - { - private const int ArenaIndex = RankingBattle11.UpdateTargetWeeklyArenaIndex - 1; - private readonly TableSheets _tableSheets; - private readonly Address _agent1Address; - private readonly Address _avatar1Address; - private readonly Address _avatar2Address; - private readonly Address _weeklyArenaAddress; - private readonly IAccount _initialState; - private IValue _arenaSheetState; - - public RankingBattle11Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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(ArenaIndex); - 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(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) - .SetState(_weeklyArenaAddress, weeklyArenaState.Serialize()); - - var arenaSheetAddress = Addresses.GetSheetAddress(); - _arenaSheetState = _initialState.GetState(arenaSheetAddress); - _initialState = _initialState.SetNull(arenaSheetAddress); - - 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().Address; - 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(ArenaIndex); - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.level = 10; - var prevScore = previousWeeklyState[_avatar1Address].Score; - - 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); - var weeklyArenaAddress = - WeeklyArenaState.DeriveAddress(RankingBattle11.UpdateTargetWeeklyArenaIndex); - 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()) - .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()) - .SetState( - weeklyArenaAddress, - new WeeklyArenaState(RankingBattle11.UpdateTargetWeeklyArenaIndex).Serialize()); - - var action = new RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = RankingBattle11.UpdateTargetBlockIndex, - }); - - Assert.True(nextState.TryGetState(weeklyArenaAddress.Derive(_avatar1Address.ToByteArray()), out Dictionary rawArenaInfo)); - Assert.True(nextState.TryGetState(weeklyArenaAddress.Derive(_avatar2Address.ToByteArray()), out Dictionary rawEnemyInfo)); - Assert.True(nextState.TryGetState(weeklyArenaAddress.Derive("address_list"), out List rawAddressList)); - var nextWeekly = nextState.GetWeeklyArenaState(weeklyArenaAddress); - Assert.Empty(nextWeekly.Map); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextArenaInfo = new ArenaInfo(rawArenaInfo); - var addressList = rawAddressList.ToList(StateExtensions.ToAddress); - - Assert.Contains(_avatar1Address, addressList); - Assert.Contains(_avatar2Address, addressList); - 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(), - RankingBattle11.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()); - } - - [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_Backward_Compatible(bool isNew, bool avatarBackward, bool enemyBackward) - { - var previousWeeklyState = _initialState.GetWeeklyArenaState(ArenaIndex); - 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 RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List { costume.ItemId }, - equipmentIds = new List(), - }; - - var nextState = action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - BlockIndex = RankingBattle11.UpdateTargetBlockIndex - 1, - }); - - var nextAvatar1State = nextState.GetAvatarStateV2(_avatar1Address); - var nextWeeklyState = nextState.GetWeeklyArenaState(ArenaIndex); - 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(), - RankingBattle11.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 RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar1Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = _agent1Address, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - break; - } - - var action = new RankingBattle11 - { - avatarAddress = avatarAddress, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = _initialState, - Signer = signer, - RandomSeed = 0, - }); - }); - } - - [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 RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - }); - }); - } - - [Fact] - public void ExecuteThrowWeeklyArenaStateAlreadyEndedException() - { - var previousWeeklyArenaState = _initialState.GetWeeklyArenaState(_weeklyArenaAddress); - previousWeeklyArenaState.Ended = true; - - var previousState = _initialState.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - var action = new RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - }); - }); - } - - [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 RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - }); - }); - } - - [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 RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - }); - }); - } - - [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 state = _initialState; - var avatarState = state.GetAvatarState(_avatar1Address); - avatarState.level = avatarLevel; - var enemyAddress = _avatar2Address; - - var previousWeeklyArenaState = state.GetWeeklyArenaState(_weeklyArenaAddress); - - state = state.SetState( - _weeklyArenaAddress, - previousWeeklyArenaState.Serialize()); - - 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()) - .SetState( - avatarState.address.Derive(LegacyWorldInformationKey), - avatarState.worldInformation.Serialize()) - .SetState( - avatarState.address.Derive(LegacyQuestListKey), - avatarState.questList.Serialize()); - - var action = new RankingBattle11 - { - avatarAddress = avatarState.address, - enemyAddress = enemyAddress, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = costumes, - equipmentIds = equipments, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - RandomSeed = random.Seed, - BlockIndex = 3806324 + 1, // Ranking Battle Action throws NotEnoughAvatarLevelException when BlockIndex is higher than 3806324. - })); - } - } - } - - [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 RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = equipments, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agent1Address, - RandomSeed = 0, - })); - } - - [Fact] - public void Execute_ActionObsoletedException() - { - var previousAvatar1State = _initialState.GetAvatarState(_avatar1Address); - previousAvatar1State.worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - false - ); - var previousState = _initialState.SetState( - _avatar1Address, - previousAvatar1State.Serialize()); - - var arenaSheetAddress = Addresses.GetSheetAddress(); - previousState = previousState.SetState(arenaSheetAddress, _arenaSheetState); - - var action = new RankingBattle11 - { - avatarAddress = _avatar1Address, - enemyAddress = _avatar2Address, - weeklyArenaAddress = _weeklyArenaAddress, - costumeIds = new List(), - equipmentIds = new List(), - }; - - Assert.Throws(() => - { - action.Execute(new ActionContext - { - PreviousState = previousState, - Signer = _agent1Address, - RandomSeed = 0, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle2Test.cs b/.Lib9c.Tests/Action/RankingBattle2Test.cs deleted file mode 100644 index e8aacbdd72..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle2Test.cs +++ /dev/null @@ -1,390 +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 IAccount _initialState; - - public RankingBattle2Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - - 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 769bf4a989..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle3Test.cs +++ /dev/null @@ -1,446 +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 IAccount _initialState; - - public RankingBattle3Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle4Test.cs b/.Lib9c.Tests/Action/RankingBattle4Test.cs deleted file mode 100644 index fc97a4f220..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle4Test.cs +++ /dev/null @@ -1,470 +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 IAccount _initialState; - - public RankingBattle4Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle5Test.cs b/.Lib9c.Tests/Action/RankingBattle5Test.cs deleted file mode 100644 index b7cb46a09f..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle5Test.cs +++ /dev/null @@ -1,500 +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 IAccount _initialState; - - public RankingBattle5Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle6Test.cs b/.Lib9c.Tests/Action/RankingBattle6Test.cs deleted file mode 100644 index ace7575fd6..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle6Test.cs +++ /dev/null @@ -1,500 +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 IAccount _initialState; - - public RankingBattle6Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle7Test.cs b/.Lib9c.Tests/Action/RankingBattle7Test.cs deleted file mode 100644 index d3e82f42ec..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle7Test.cs +++ /dev/null @@ -1,500 +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 IAccount _initialState; - - public RankingBattle7Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle8Test.cs b/.Lib9c.Tests/Action/RankingBattle8Test.cs deleted file mode 100644 index 84c27666dc..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle8Test.cs +++ /dev/null @@ -1,509 +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 IAccount _initialState; - - public RankingBattle8Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RankingBattle9Test.cs b/.Lib9c.Tests/Action/RankingBattle9Test.cs deleted file mode 100644 index 92857c4d43..0000000000 --- a/.Lib9c.Tests/Action/RankingBattle9Test.cs +++ /dev/null @@ -1,509 +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 IAccount _initialState; - - public RankingBattle9Test(ITestOutputHelper outputHelper) - { - _initialState = new Account(MockState.Empty); - - 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().Address; - - 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().Address; - 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, - RandomSeed = 0, - }); - - 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, - RandomSeed = 0, - }); - }); - } - - [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().Address; - avatarAddress = _avatar1Address; - enemyAddress = _avatar2Address; - break; - case 1: - signer = _agent1Address; - avatarAddress = _avatar1Address; - enemyAddress = new PrivateKey().Address; - 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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - }); - }); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination0Test.cs b/.Lib9c.Tests/Action/RapidCombination0Test.cs deleted file mode 100644 index 85456b5fb2..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination0Test.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 RapidCombination0Test - { - private readonly IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination0Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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, 0); - - var tempState = _initialState - .SetState(_avatarAddress, avatarState.Serialize()) - .SetState(slotAddress, slotState.Serialize()); - - var action = new RapidCombination0 - { - 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 RapidCombination0 - { - 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(), - 0); - - 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 RapidCombination0 - { - 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 RapidCombination0 - { - 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 RapidCombination0 - { - 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/RapidCombination2Test.cs b/.Lib9c.Tests/Action/RapidCombination2Test.cs deleted file mode 100644 index 1a80942064..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 IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination2Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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 e690929620..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 IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination3Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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 9874e37b9f..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination4Test.cs +++ /dev/null @@ -1,429 +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 IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination4Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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, - })); - } - - [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/RapidCombination5Test.cs b/.Lib9c.Tests/Action/RapidCombination5Test.cs deleted file mode 100644 index 0f35acdc2a..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination5Test.cs +++ /dev/null @@ -1,494 +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 RapidCombination5Test - { - private readonly IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination5Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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 RapidCombination5 - { - 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 RapidCombination5 - { - 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 RapidCombination5 - { - 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 RapidCombination5 - { - 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 RapidCombination5 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - })); - } - - [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 RapidCombination5 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 1, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RapidCombination6Test.cs b/.Lib9c.Tests/Action/RapidCombination6Test.cs deleted file mode 100644 index d43a6dea84..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination6Test.cs +++ /dev/null @@ -1,683 +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 IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination6Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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, - })); - } - - [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(ItemEnhancement11.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement11.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement11.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/RapidCombination7Test.cs b/.Lib9c.Tests/Action/RapidCombination7Test.cs deleted file mode 100644 index de60d1dc14..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination7Test.cs +++ /dev/null @@ -1,667 +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 RapidCombination7Test - { - private readonly IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination7Test() - { - _initialState = new Account(MockState.Empty); - - 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().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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, - })); - } - - [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(ItemEnhancement11.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement11.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement11.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/RapidCombination8Test.cs b/.Lib9c.Tests/Action/RapidCombination8Test.cs deleted file mode 100644 index 52f0bd6149..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination8Test.cs +++ /dev/null @@ -1,669 +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 Lib9c.Tests.Fixtures.TableCSV; - 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 IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination8Test() - { - _initialState = new Account(MockState.Empty); - - var sheets = TableSheetsImporter.ImportSheets(); - sheets[nameof(GameConfigSheet)] = GameConfigSheetFixtures.Default; - foreach (var (key, value) in sheets) - { - _initialState = _initialState.SetState( - Addresses.TableSheet.Derive(key), - value.Serialize()); - } - - _tableSheets = new TableSheets(sheets); - - _agentAddress = new PrivateKey().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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, - })); - } - - [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(ItemEnhancement11.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement11.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement11.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/RapidCombination9Test.cs b/.Lib9c.Tests/Action/RapidCombination9Test.cs deleted file mode 100644 index 25833ae3d5..0000000000 --- a/.Lib9c.Tests/Action/RapidCombination9Test.cs +++ /dev/null @@ -1,684 +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 Lib9c.Tests.Fixtures.TableCSV; - using Lib9c.Tests.Fixtures.TableCSV.Item; - using Lib9c.Tests.Util; - 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 RapidCombination9Test - { - private readonly IAccount _initialState; - - private readonly TableSheets _tableSheets; - - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - - public RapidCombination9Test() - { - _initialState = new Account(MockState.Empty); - Dictionary sheets; - (_initialState, sheets) = InitializeUtil.InitializeTableSheets( - _initialState, - sheetsOverride: new Dictionary - { - { - "EquipmentItemRecipeSheet", - EquipmentItemRecipeSheetFixtures.Default - }, - { - "EquipmentItemSubRecipeSheet", - EquipmentItemSubRecipeSheetFixtures.V1 - }, - { - "GameConfigSheet", - GameConfigSheetFixtures.Default - }, - }); - _tableSheets = new TableSheets(sheets); - foreach (var (key, value) in sheets) - { - _initialState = - _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - _agentAddress = new PrivateKey().Address; - var agentState = new AgentState(_agentAddress); - - _avatarAddress = new PrivateKey().Address; - 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 RapidCombination9 - { - 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 RapidCombination9 - { - 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 RapidCombination9 - { - 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 RapidCombination9 - { - 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 RapidCombination9 - { - avatarAddress = _avatarAddress, - slotIndex = 0, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - PreviousState = tempState, - Signer = _agentAddress, - BlockIndex = 51, - })); - } - - [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 RapidCombination9 - { - 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(ItemEnhancement11.TryGetRow( - equipment, - _tableSheets.EnhancementCostSheetV2, - out var costRow)); - var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); - equipment.LevelUp( - random, - costRow, - equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement11.ResultModel - { - id = mailId, - preItemUsable = preItemUsable, - itemUsable = equipment, - materialItemIdList = new[] { materialEquipment.NonFungibleId }, - gold = 0, - actionPoint = 0, - enhancementResult = ItemEnhancement11.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 RapidCombination9 - { - 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 64ec2fc034..6c2b81092c 100644 --- a/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs @@ -130,7 +130,7 @@ public IAccount BattleArena( int ticket, long blockIndex) { - var action = new BattleArena6() + var action = new BattleArena() { myAvatarAddress = myAvatarAddress, enemyAvatarAddress = enemyAvatarAddress, diff --git a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs index 4085442cef..2fec0b1f58 100644 --- a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs @@ -98,7 +98,7 @@ public void HackAndSlash() var itemSlotStateAddress = ItemSlotState.DeriveAddress(_avatarAddress, BattleType.Adventure); Assert.Null(_initialState.GetState(itemSlotStateAddress)); - var has = new HackAndSlash21 + var has = new HackAndSlash { StageId = 1, AvatarAddress = _avatarAddress, @@ -155,7 +155,7 @@ public void Raid() avatarState.worldInformation.Serialize() ); - var raid = new Raid6 + var raid = new Raid { AvatarAddress = _avatarAddress, EquipmentIds = new List @@ -228,7 +228,7 @@ public void Arena() var enemyAvatarAddress = avatarAddress.Equals(_avatarAddress) ? _enemyAvatarAddress : _avatarAddress; - var battle = new BattleArena13 + var battle = new BattleArena { myAvatarAddress = avatarAddress, enemyAvatarAddress = enemyAvatarAddress, diff --git a/.Lib9c.Tests/Action/Scenario/ItemCraftTest.cs b/.Lib9c.Tests/Action/Scenario/ItemCraftTest.cs index 22dfef383e..bd81ebf7bd 100644 --- a/.Lib9c.Tests/Action/Scenario/ItemCraftTest.cs +++ b/.Lib9c.Tests/Action/Scenario/ItemCraftTest.cs @@ -217,7 +217,7 @@ public void CraftConsumableTest(int randomSeed, int[] targetItemIdList) { // Do combination action var recipe = recipeList[i]; - var action = new CombinationConsumable8 + var action = new CombinationConsumable { avatarAddress = _avatarAddr, slotIndex = i, diff --git a/.Lib9c.Tests/Action/Scenario/Pet/IncreaseBlockPerHourglassTest.cs b/.Lib9c.Tests/Action/Scenario/Pet/IncreaseBlockPerHourglassTest.cs index aff3856807..253381f44b 100644 --- a/.Lib9c.Tests/Action/Scenario/Pet/IncreaseBlockPerHourglassTest.cs +++ b/.Lib9c.Tests/Action/Scenario/Pet/IncreaseBlockPerHourglassTest.cs @@ -167,7 +167,7 @@ public void RapidCombinationTest_Equipment( }); // Do rapid combination - var rapidAction = new RapidCombination9 + var rapidAction = new RapidCombination { avatarAddress = _avatarAddr, slotIndex = 0, diff --git a/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs index 52bbecc378..104c4d6644 100644 --- a/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs @@ -19,7 +19,7 @@ namespace Lib9c.Tests.Action.Scenario public class RuneScenarioTest { [Fact] - public void Craft_And_Equip() + public void Craft_And_Unlock_And_Equip() { var agentAddress = new PrivateKey().Address; var agentState = new AgentState(agentAddress); @@ -69,13 +69,19 @@ public void Craft_And_Equip() var runeAddress = RuneState.DeriveAddress(avatarAddress, runeId); Assert.Null(initialState.GetState(runeAddress)); + initialState = initialState.MintAsset( + new ActionContext(), + agentAddress, + gameConfigState.RuneSkillSlotCrystalUnlockCost * Currencies.Crystal + ); + var craftAction = new RuneEnhancement { AvatarAddress = avatarAddress, RuneId = runeId, }; - var state = craftAction.Execute(new ActionContext + var prevState = craftAction.Execute(new ActionContext { BlockIndex = 1, PreviousState = initialState, @@ -83,15 +89,33 @@ public void Craft_And_Equip() Signer = agentAddress, }); - var rawRuneState = Assert.IsType(state.GetState(runeAddress)); + var rawRuneState = Assert.IsType(prevState.GetState(runeAddress)); var runeState = new RuneState(rawRuneState); Assert.Equal(1, runeState.Level); Assert.Equal(runeId, runeState.RuneId); var runeSlotStateAddress = RuneSlotState.DeriveAddress(avatarAddress, BattleType.Adventure); - Assert.Null(state.GetState(runeSlotStateAddress)); + Assert.Null(prevState.GetState(runeSlotStateAddress)); + + var unlockAction = new UnlockRuneSlot + { + AvatarAddress = avatarAddress, + SlotIndex = 6, + }; + + var state = unlockAction.Execute(new ActionContext + { + BlockIndex = 1, + PreviousState = prevState, + RandomSeed = 0, + Signer = agentAddress, + }); + + var runeSlotState = new RuneSlotState((List)state.GetState(runeSlotStateAddress)); + Assert.Single(runeSlotState.GetRuneSlot().Where(r => r.RuneSlotType == RuneSlotType.Crystal && !r.IsLock)); + Assert.Single(runeSlotState.GetRuneSlot().Where(r => r.RuneSlotType == RuneSlotType.Crystal && r.IsLock)); - var has = new HackAndSlash21 + var has = new HackAndSlash { StageId = 1, AvatarAddress = avatarAddress, @@ -101,7 +125,7 @@ public void Craft_And_Equip() WorldId = 1, RuneInfos = new List { - new RuneSlotInfo(0, runeId), + new RuneSlotInfo(6, runeId), }, }; @@ -120,7 +144,7 @@ public void Craft_And_Equip() var runeSlotInfo = runeSlot.GetEquippedRuneSlotInfos().Single(); Assert.Equal(runeId, runeSlotInfo.RuneId); - Assert.Equal(0, runeSlotInfo.SlotIndex); + Assert.Equal(6, runeSlotInfo.SlotIndex); } } } diff --git a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs deleted file mode 100644 index 74afc3852c..0000000000 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeReward2ScenarioTest.cs +++ /dev/null @@ -1,931 +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; - - public class StakeAndClaimStakeReward2ScenarioTest - { - private readonly IAccount _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 Account(MockState.Empty); - - 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().Address; - var stakeStateAddress = StakeState.DeriveAddress(_signerAddress); - var agentState = new AgentState(_signerAddress); - _avatarAddress = new PrivateKey().Address; - 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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 98883d9525..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 IAccount _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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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 Stake2(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/StakeAndClaimStakeRewardScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeRewardScenarioTest.cs deleted file mode 100644 index d91a402b83..0000000000 --- a/.Lib9c.Tests/Action/Scenario/StakeAndClaimStakeRewardScenarioTest.cs +++ /dev/null @@ -1,1016 +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; - - // Tests for stake2 and claim_stake_reward{..8} actions - public class StakeAndClaimStakeRewardScenarioTest - { - private const string AgentAddressHex = "0x0000000001000000000100000000010000000001"; - private readonly Address _agentAddr = new Address(AgentAddressHex); - private readonly Address _avatarAddr; - private readonly IAccount _initialStatesWithAvatarStateV2; - private readonly Currency _ncg; - - public StakeAndClaimStakeRewardScenarioTest(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - ( - _, - _, - _avatarAddr, - _, - _initialStatesWithAvatarStateV2) = InitializeUtil.InitializeStates( - agentAddr: _agentAddr); - _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, - null, - null, - 0L, - }; - yield return new object[] - { - 499L, - new[] - { - (400_000, 49), - (500_000, 1), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 799L, - new[] - { - (400_000, 99), - (500_000, 2), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - null, - null, - 0L, - }; - yield return new object[] - { - 4_999L, - new[] - { - (400_000, 624), - (500_000, 8), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 0, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 49_999L, - new[] - { - (400_000, 9_999), - (500_000, 64), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 8, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 499_999L, - new[] - { - (400_000, 99_999), - (500_000, 626), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 83, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 99_999_999L, - new[] - { - (400_000, 19_999_999), - (500_000, 125_001), - }, - ClaimStakeReward2.ObsoletedIndex + 50_400L, - 16_666, - null, - null, - 0L, - }; - - // 지연된 보상수령 확인 - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 499L, - new[] - { - (400_000, 441), - (500_000, 9), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 799L, - new[] - { - (400_000, 891), - (500_000, 18), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - null, - null, - 0L, - }; - yield return new object[] - { - 4_999L, - new[] - { - (400_000, 5_616), - (500_000, 72), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 0, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 49_999L, - new[] - { - (400_000, 89_991), - (500_000, 576), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 72, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 499_999L, - new[] - { - (400_000, 899_991), - (500_000, 5_634), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 747, - null, - null, - 0L, - }; - - // 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, - null, - null, - 0L, - }; - yield return new object[] - { - 4_999_999L, - new[] - { - (400_000, 8_999_991), - (500_000, 56_259), - }, - ClaimStakeReward2.ObsoletedIndex + 500_800L, - 7_497, - null, - null, - 0L, - }; - } - - 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, - string expectedCurrencyAddrHex, - string expectedCurrencyTicker, - long expectedCurrencyAmount) - { - var context = new ActionContext(); - var states = _initialStatesWithAvatarStateV2.MintAsset(context, _agentAddr, _ncg * stakeAmount); - - IAction action = new Stake2(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out var stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - 0 * RuneHelper.StakeRune, - _initialStatesWithAvatarStateV2.GetBalance(_avatarAddr, RuneHelper.StakeRune)); - - action = new ClaimStakeReward8(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = receiveBlockIndex, - }); - - // 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - var avatarState = states.GetAvatarStateV2(_avatarAddr); - foreach (var (itemId, 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)); - - if (!string.IsNullOrEmpty(expectedCurrencyAddrHex)) - { - var addr = new Address(expectedCurrencyAddrHex); - var currency = Currencies.GetMinterlessCurrency(expectedCurrencyTicker); - Assert.Equal( - expectedCurrencyAmount * currency, - states.GetBalance(addr, currency)); - } - } - - [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) - { - var 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 Stake2(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out var stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _ncg * (initialBalance - stakeAmount), - states.GetBalance(_agentAddr, _ncg)); - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - - action = new ClaimStakeReward8(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = newStakeBlockIndex, - }); - - action = new Stake2(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 Stake2(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - Assert.True(states.TryGetStakeState(_agentAddr, out var stakeState)); - Assert.NotNull(stakeState); - Assert.Equal( - _ncg * (initialBalance - stakeAmount), - states.GetBalance(_agentAddr, _ncg)); - Assert.Equal( - _ncg * stakeAmount, - states.GetBalance(stakeState.address, _ncg)); - - action = new ClaimStakeReward8(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex + StakeState.LockupInterval - 1, - }); - - action = new Stake2(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 Stake2(stakeAmount); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - // 1~3회까지 모든 보상을 수령함 - // 201,600 블록 도달 이후 → 지정된 캐릭터 앞으로 이하 보상의 수령이 가능해야 한다. - foreach ((var claimBlockIndex, (int itemId, int amount)[] expectedItems) in claimEvents) - { - action = new ClaimStakeReward8(_avatarAddr); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = claimBlockIndex, - }); - - var avatarState = states.GetAvatarStateV2(_avatarAddr); - foreach (var (itemId, 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 Stake2(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 ClaimStakeReward8(_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 Stake2(500); - states = action.Execute(new ActionContext - { - PreviousState = states, - Signer = _agentAddr, - BlockIndex = ClaimStakeReward2.ObsoletedIndex, - }); - - action = new ClaimStakeReward8(_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/Sell0Test.cs b/.Lib9c.Tests/Action/Sell0Test.cs deleted file mode 100644 index ff77f3d83e..0000000000 --- a/.Lib9c.Tests/Action/Sell0Test.cs +++ /dev/null @@ -1,235 +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 IAccount _initialState; - - public Sell0Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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 fbb4adaf9e..0000000000 --- a/.Lib9c.Tests/Action/Sell10Test.cs +++ /dev/null @@ -1,416 +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 IAccount _initialState; - - public Sell10Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell11Test.cs b/.Lib9c.Tests/Action/Sell11Test.cs deleted file mode 100644 index a2f62134bb..0000000000 --- a/.Lib9c.Tests/Action/Sell11Test.cs +++ /dev/null @@ -1,464 +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 IAccount _initialState; - - public Sell11Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell2Test.cs b/.Lib9c.Tests/Action/Sell2Test.cs deleted file mode 100644 index b745d5300d..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 IAccount _initialState; - - public Sell2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 ctx = new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Signer = _agentAddress, - }; - ctx.SetRandom(random); - var nextState = sellAction.Execute(ctx); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell3Test.cs b/.Lib9c.Tests/Action/Sell3Test.cs deleted file mode 100644 index d91450ac86..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 IAccount _initialState; - - public Sell3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 ctx = new ActionContext - { - BlockIndex = 0, - PreviousState = previousStates, - Signer = _agentAddress, - }; - ctx.SetRandom(random); - var nextState = sellAction.Execute(ctx); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell4Test.cs b/.Lib9c.Tests/Action/Sell4Test.cs deleted file mode 100644 index afec08473b..0000000000 --- a/.Lib9c.Tests/Action/Sell4Test.cs +++ /dev/null @@ -1,338 +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 IAccount _initialState; - - public Sell4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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, - (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell5Test.cs b/.Lib9c.Tests/Action/Sell5Test.cs deleted file mode 100644 index 98f075225b..0000000000 --- a/.Lib9c.Tests/Action/Sell5Test.cs +++ /dev/null @@ -1,388 +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 IAccount _initialState; - - public Sell5Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 = (ITradableItem)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 = (ITradableItem)mail.attachment.itemUsable; - Assert.Equal((ItemUsable)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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell6Test.cs b/.Lib9c.Tests/Action/Sell6Test.cs deleted file mode 100644 index b7701e7606..0000000000 --- a/.Lib9c.Tests/Action/Sell6Test.cs +++ /dev/null @@ -1,690 +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 Sell6Test - { - 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 IAccount _initialState; - - public Sell6Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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 expectedProductId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var productId = new Guid("229e5f8c-fabe-4c04-bab9-45325cfa69a4"); - var shardedShopAddress = ShardedShopState.DeriveAddress( - tradableItem.ItemSubType, - expectedProductId); - if (shopItemExist) - { - tradableItem.RequiredBlockIndex = blockIndex; - Assert.Equal(blockIndex, tradableItem.RequiredBlockIndex); - var shopItem = new ShopItem( - _agentAddress, - _avatarAddress, - productId, - new FungibleAssetValue(currencyState, 1, 0), - 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 Sell6 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = 1, - PreviousState = previousStates, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 = (ITradableItem)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(expectedProductId, nextShopItem.ProductId); - 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 = (ITradableItem)mail.attachment.itemUsable; - Assert.Equal((ItemUsable)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); - } - - [Theory] - [InlineData( - 1617615, - ItemSubType.Hourglass, - "Bb2E9752C66B909a31CA8Db19927e02749d45157", - "B4F6c2D629D287D0ee8ab847B5Ee5761eC530E4d", - 1614868, - 1633615, - 500, - 1250, - 5000, - 1225 - )] - [InlineData( - 1617615, - ItemSubType.Weapon, - "138bF0C6d9534Ef0b51AeFa1e10EFcBeF7eb491b", - "e0bD7637429040Ae0aAa0a2Ec4E4f1b7CEE19166", - 1552871, - 1633615, - 0, - 1, - 10, - 10 - )] - [InlineData( - 1618153, - ItemSubType.Hourglass, - "F28B7D3B537148AF741b979094769F8d9BdF551f", - "06f5bed46Cf84932afBB3B73D232b21D47d48b8B", - 1614910, - 1634153, - 80, - 15, - 800, - 12 - )] - [InlineData( - 1618617, - ItemSubType.Hourglass, - "f189C04126E2E708Cd7D17CD68a7B7f10Bbb6f16", - "F1A005C01E683dBcAb9A306d5cC70D5E57fccFa9", - 1613335, - 1634617, - 1265, - 80, - 12650, - 69657440 - )] - [InlineData( - 1618700, - ItemSubType.Hourglass, - "dDCe3c1416fbD7d0533145bE281FBF5efA90f000", - "3B56f1E3Ea3f37B50CcDA73470d94E312f5883f5", - 1613273, - 1634700, - 2000, - 1, - 20000, - 100010 - )] - [InlineData( - 1618778, - ItemSubType.Hourglass, - "dDCe3c1416fbD7d0533145bE281FBF5efA90f000", - "3B56f1E3Ea3f37B50CcDA73470d94E312f5883f5", - 1613273, - 1634778, - 1500, - 1000, - 15000, - 10000 - )] - [InlineData( - 1618779, - ItemSubType.Hourglass, - "dDCe3c1416fbD7d0533145bE281FBF5efA90f000", - "3B56f1E3Ea3f37B50CcDA73470d94E312f5883f5", - 1613275, - 1634779, - 1300, - 1000, - 13000, - 10000 - )] - [InlineData( - 1618781, - ItemSubType.Hourglass, - "Ff008DD3070405c1B783BfEAbf2CE733646Bb726", - "a4A2cCE75484911F74FD74Ac850446BAb160c610", - 1615455, - 1634781, - 500, - 1000, - 5000, - 10000 - )] - [InlineData( - 1618976, - ItemSubType.Hourglass, - "69449ee94DCe543D4a060AE9E92aa2C122b76B3a", - "59112DB0B61857213EdF15621DC7D2d27B0d5869", - 1618090, - 1634976, - 80, - 4, - 800, - 80842416 - )] - [InlineData( - 1619118, - ItemSubType.Hourglass, - "Bb2E9752C66B909a31CA8Db19927e02749d45157", - "B4F6c2D629D287D0ee8ab847B5Ee5761eC530E4d", - 1614817, - 1635118, - 500, - 1, - 5000, - 20106042 - )] - [InlineData( - 1619326, - ItemSubType.Hourglass, - "B8CB064514cF38e4D50B8EB75E34a928091511B9", - "53b1Bc1564977AAa1ff40A23834482a3722aff2C", - 1615750, - 1635326, - 10000, - 2, - 100000, - 42120802 - )] - [InlineData( - 1619356, - ItemSubType.Hourglass, - "3d08eeC5c7FED047777f0e771A29237400e559BA", - "2F4f646f9Ee52Ff3b9B3880a3166682E7284024B", - 1613346, - 1635356, - 8000, - 80, - 160000, - 800 - )] - [InlineData( - 1625533, - ItemSubType.Hourglass, - "1C9b7B4119E6E2919C35b6899b00257291D2531A", - "DF85d94517828D202641A6C973Cd10F9563BC835", - 1486926, - 1641533, - 0, - 1, - 69, - 249 - )] - [InlineData( - 1625997, - ItemSubType.Hourglass, - "1090DF3A5743dCDA546b33d02d5067765394f146", - "7F9EB9E3A73a6F242cA4Aa2329e4F1cc8263417B", - 1575429, - 1641997, - 9, - 1, - 1500, - 1250 - )] - public void Execute_20210604( - long blockIndex, - ItemSubType itemSubType, - string agentAddressHex, - string avatarAddressHex, - long shopExpiredBlockIndex, - long expectedBlockIndex, - int shopItemCount, - int itemCount, - int shopPrice, - int expectedPrice) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - var sellerAgentAddress = new Address(agentAddressHex); - var sellerAvatarAddress = new Address(avatarAddressHex); - - ITradableItem tradableItem; - switch (itemSubType) - { - case ItemSubType.Weapon: - tradableItem = (ITradableItem)ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.OrderedList.First(row => row.ItemSubType == ItemSubType.Weapon), - Guid.NewGuid(), - 1); - break; - case ItemSubType.Hourglass: - var tradableMaterialRow = _tableSheets.MaterialItemSheet.OrderedList - .First(row => row.ItemSubType == ItemSubType.Hourglass); - tradableItem = ItemFactory.CreateTradableMaterial(tradableMaterialRow); - tradableItem.RequiredBlockIndex = 1; - break; - default: - throw new ArgumentOutOfRangeException(nameof(itemSubType), itemSubType, null); - } - - Assert.Equal(1, 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, expectedPrice, 0); - var productId = new Guid("6f460c1a755d48e4ad6765d5f519dbc8"); - var shardedShopAddress = ShardedShopState.DeriveAddress( - tradableItem.ItemSubType, - productId); - Assert.Equal(1, tradableItem.RequiredBlockIndex); - var shopItem = new ShopItem( - sellerAgentAddress, - sellerAvatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(currencyState, shopPrice, 0), - shopExpiredBlockIndex, - tradableItem, - shopItemCount - ); - - var shardedShopState = new ShardedShopState(shardedShopAddress); - shardedShopState.Register(shopItem); - Assert.Single(shardedShopState.Products); - previousStates = previousStates.SetState( - shardedShopAddress, - shardedShopState.Serialize()); - - var sellAction = new Sell6 - { - sellerAvatarAddress = _avatarAddress, - tradableId = tradableItem.TradableId, - count = itemCount, - price = price, - itemSubType = tradableItem.ItemSubType, - }; - var nextState = sellAction.Execute(new ActionContext - { - BlockIndex = blockIndex, - PreviousState = previousStates, - Signer = _agentAddress, - RandomSeed = 0, - }); - - // Check AvatarState and Inventory - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - Assert.Single(nextAvatarState.inventory.Items); - Assert.True(nextAvatarState.inventory.TryGetTradableItems( - tradableItem.TradableId, - expectedBlockIndex, - itemCount, - out var inventoryItems)); - Assert.Single(inventoryItems); - ITradableItem nextTradableItem = (ITradableItem)inventoryItems.First().item; - Assert.Equal(expectedBlockIndex, nextTradableItem.RequiredBlockIndex); - - // Check ShardedShopState and ShopItem - var nextSerializedShardedShopState = nextState.GetState(shardedShopAddress); - Assert.NotNull(nextSerializedShardedShopState); - var nextShardedShopState = - new ShardedShopState((Dictionary)nextSerializedShardedShopState); - Assert.Equal(2, nextShardedShopState.Products.Count); - Assert.Single(nextShardedShopState.Products.Values.Where(s => s.Equals(shopItem))); - Assert.Single(nextShardedShopState.Products.Values.Where(s => !s.Equals(shopItem))); - ShopItem nextShopItem = nextShardedShopState.Products.Values.First(s => !s.Equals(shopItem)); - - ITradableItem innerShopItem = nextShopItem.TradableFungibleItem; - int fungibleCount = itemCount; - if (itemSubType == ItemSubType.Weapon) - { - innerShopItem = (ITradableItem)nextShopItem.ItemUsable; - fungibleCount = 0; - } - - Assert.Equal(price, nextShopItem.Price); - Assert.Equal(expectedBlockIndex, nextShopItem.ExpiredBlockIndex); - Assert.Equal(_agentAddress, nextShopItem.SellerAgentAddress); - Assert.Equal(_avatarAddress, nextShopItem.SellerAvatarAddress); - Assert.Equal(expectedBlockIndex, innerShopItem.RequiredBlockIndex); - Assert.Equal(fungibleCount, nextShopItem.TradableFungibleItemCount); - - var mailList = nextAvatarState.mailBox.Where(m => m is SellCancelMail).ToList(); - Assert.Single(mailList); - var mail = mailList.First() as SellCancelMail; - Assert.NotNull(mail); - Assert.Equal(expectedBlockIndex, mail.requiredBlockIndex); - - ITradableItem attachmentItem = itemSubType == ItemSubType.Weapon - ? (ITradableItem)mail.attachment.itemUsable - : mail.attachment.tradableFungibleItem; - Assert.Equal(itemSubType == ItemSubType.Weapon, mail.attachment.tradableFungibleItem is null); - Assert.Equal(fungibleCount, mail.attachment.tradableFungibleItemCount); - Assert.Equal(nextTradableItem, attachmentItem); - } - - [Fact] - public void Execute_Throw_InvalidPriceException() - { - var action = new Sell6 - { - 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 Sell6 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = new Account(MockState.Empty), - 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 Sell6 - { - 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 Sell6 - { - sellerAvatarAddress = _avatarAddress, - tradableId = default, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 0, - PreviousState = _initialState, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - - [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 Sell6 - { - sellerAvatarAddress = _avatarAddress, - tradableId = equipmentId, - count = 1, - price = 0 * _currency, - itemSubType = ItemSubType.Food, - }; - - Assert.Throws(() => action.Execute(new ActionContext - { - BlockIndex = 11, - PreviousState = _initialState, - Signer = _agentAddress, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell7Test.cs b/.Lib9c.Tests/Action/Sell7Test.cs deleted file mode 100644 index 5c6be1efc1..0000000000 --- a/.Lib9c.Tests/Action/Sell7Test.cs +++ /dev/null @@ -1,439 +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 IAccount _initialState; - - public Sell7Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell8Test.cs b/.Lib9c.Tests/Action/Sell8Test.cs deleted file mode 100644 index eb2011ddcf..0000000000 --- a/.Lib9c.Tests/Action/Sell8Test.cs +++ /dev/null @@ -1,432 +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 IAccount _initialState; - - public Sell8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/Sell9Test.cs b/.Lib9c.Tests/Action/Sell9Test.cs deleted file mode 100644 index ec6b7de050..0000000000 --- a/.Lib9c.Tests/Action/Sell9Test.cs +++ /dev/null @@ -1,416 +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 IAccount _initialState; - - public Sell9Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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 = (ITradableItem)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, - Signer = _agentAddress, - RandomSeed = 0, - }); - - 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 Account(MockState.Empty), - 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, - RandomSeed = 0, - })); - } - - [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, - RandomSeed = 0, - })); - } - - [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); - - IAccount 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, - RandomSeed = 0, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation0Test.cs b/.Lib9c.Tests/Action/SellCancellation0Test.cs deleted file mode 100644 index 3a066c0c42..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation0Test.cs +++ /dev/null @@ -1,120 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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), - (ITradableItem)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, - RandomSeed = 0, - 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 3e1f383ee3..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation2Test.cs +++ /dev/null @@ -1,138 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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), - (ITradableItem)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, - RandomSeed = 0, - 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 3af69b482c..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation3Test.cs +++ /dev/null @@ -1,180 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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), - (ITradableItem)equipment)); - - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 fb77e8640f..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation4Test.cs +++ /dev/null @@ -1,180 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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), - (ITradableItem)equipment)); - - shopState.Register(new ShopItem( - _agentAddress, - _avatarAddress, - Guid.NewGuid(), - new FungibleAssetValue(goldCurrencyState.Currency, 100, 0), - (ITradableItem)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, - RandomSeed = 0, - 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, - RandomSeed = 0, - 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 b5cd00bdb6..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation5Test.cs +++ /dev/null @@ -1,344 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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, - (ITradableItem)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 _)); - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - Signer = default, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_initialState.GetAvatarState(_avatarAddress)) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - IAccount 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, - (ITradableItem)itemUsable); - - IAccount 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, - RandomSeed = 0, - 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, - (ITradableItem)itemUsable); - shopState.Register(shopItem); - - IAccount 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, - RandomSeed = 0, - 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, - (ITradableItem)itemUsable); - shopState.Register(shopItem); - - IAccount 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, - RandomSeed = 0, - Signer = _agentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation6Test.cs b/.Lib9c.Tests/Action/SellCancellation6Test.cs deleted file mode 100644 index 99c9dea96e..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation6Test.cs +++ /dev/null @@ -1,430 +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 IAccount _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 Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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 = (ITradableItem)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 _) - ); - - IAccount 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, - RandomSeed = 0, - 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, - RandomSeed = 0, - Signer = default, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_initialState.GetAvatarState(_avatarAddress)) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - IAccount 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); - - IAccount 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, - RandomSeed = 0, - 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, - (ITradableItem)itemUsable); - shopState.Register(shopItem); - - IAccount 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, - RandomSeed = 0, - 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, - (ITradableItem)itemUsable); - shopState.Register(shopItem); - - IAccount 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, - RandomSeed = 0, - Signer = _agentAddress, - }) - ); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation7Test.cs b/.Lib9c.Tests/Action/SellCancellation7Test.cs deleted file mode 100644 index 5628546450..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation7Test.cs +++ /dev/null @@ -1,473 +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 Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class SellCancellation7Test - { - private readonly IAccount _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - - public SellCancellation7Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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", 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 backward - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - Guid itemId = new Guid(guid); - Guid orderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - if (itemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = (ITradableItem)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 = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - ShardedShopStateV2 shopState = new ShardedShopStateV2(shardedShopAddress); - Order order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - avatarState.mailBox.Add(expirationMail); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - IAccount 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); - } - - ITradableItem sellItem = order.Sell2(avatarState); - OrderDigest orderDigest = order.Digest2(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - 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); - Assert.True(avatarState.inventory.TryGetTradableItems(itemId, requiredBlockIndex * 2, itemCount, out _)); - - if (backward) - { - 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 sellCancellationAction = new SellCancellation7 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - tradableId = itemId, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }); - - ShardedShopStateV2 nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.Empty(nextShopState.OrderDigestList); - - var nextAvatarState = nextState.GetAvatarStateV2(_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.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(101, nextTradableItem.RequiredBlockIndex); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Empty(nextAvatarState.mailBox.OfType()); - var cancelMail = nextAvatarState.mailBox.OfType().First(); - Assert.Equal(orderId, cancelMail.OrderId); - var nextReceiptList = new OrderDigestListState((Dictionary)nextState.GetState(orderDigestList.Address)); - Assert.Empty(nextReceiptList.OrderDigestList); - - var sellCancelItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)nextState.GetState(Addresses.GetItemAddress(itemId))); - Assert.Equal(101, sellCancelItem.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new SellCancellation7 - { - orderId = default, - sellerAvatarAddress = default, - itemSubType = ItemSubType.Weapon, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - Signer = default, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_initialState.GetAvatarState(_avatarAddress)) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - IAccount prevState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new SellCancellation7 - { - orderId = 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 orderId = 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 = ShardedShopStateV2.DeriveAddress(tradableItem.ItemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - - Order order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - 0, - tradableItem.ItemSubType, - 1 - ); - - IAccount prevState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation7 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = tradableItem.ItemSubType, - tradableId = tradableItem.TradableId, - }; - - ItemDoesNotExistException exc = Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }) - ); - } - - [Theory] - [InlineData(false, true)] - [InlineData(true, false)] - public void Execute_Throw_InvalidAddressException(bool useAgentAddress, bool useAvatarAddress) - { - Guid orderId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemUsable.ItemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - - Address agentAddress = useAgentAddress ? _agentAddress : default; - Address avatarAddress = useAvatarAddress ? _avatarAddress : default; - Order order = OrderFactory.Create( - agentAddress, - avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - itemUsable.ItemId, - 0, - itemUsable.ItemSubType, - 1 - ); - var orderDigest = new OrderDigest( - _agentAddress, - order.StartedBlockIndex, - order.ExpiredBlockIndex, - orderId, - itemUsable.ItemId, - order.Price, - 0, - 0, - itemUsable.Id, - 1 - ); - shopState.Add(orderDigest, 0); - - IAccount prevState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation7 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - tradableId = itemUsable.ItemId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }) - ); - } - - [Fact] - public void PlainValue() - { - var action = new SellCancellation7 - { - sellerAvatarAddress = _avatarAddress, - orderId = Guid.NewGuid(), - itemSubType = ItemSubType.Weapon, - tradableId = Guid.NewGuid(), - }; - - var plainValue = action.PlainValue; - - var action2 = new SellCancellation7(); - action2.LoadPlainValue(plainValue); - - Assert.Equal(plainValue, action2.PlainValue); - } - } -} diff --git a/.Lib9c.Tests/Action/SellCancellation8Test.cs b/.Lib9c.Tests/Action/SellCancellation8Test.cs deleted file mode 100644 index 126064b700..0000000000 --- a/.Lib9c.Tests/Action/SellCancellation8Test.cs +++ /dev/null @@ -1,680 +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 Nekoyume.TableData; - using Serilog; - using Xunit; - using Xunit.Abstractions; - using static Lib9c.SerializeKeys; - - public class SellCancellation8Test - { - private readonly IAccount _initialState; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly GoldCurrencyState _goldCurrencyState; - private readonly TableSheets _tableSheets; - - public SellCancellation8Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - 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", 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; - Guid itemId = new Guid(guid); - Guid orderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - if (itemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = (ITradableItem)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 = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - ShardedShopStateV2 shopState = new ShardedShopStateV2(shardedShopAddress); - Order order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - avatarState.mailBox.Add(expirationMail); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - IAccount 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); - } - - ITradableItem sellItem; - sellItem = order.Sell(avatarState); - OrderDigest orderDigest = order.Digest(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - 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); - Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out var outItem)); - Assert.Equal(itemCount, outItem.count); - - 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 sellCancellationAction = new SellCancellation8 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - tradableId = itemId, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }); - - ShardedShopStateV2 nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.Empty(nextShopState.OrderDigestList); - - var nextAvatarState = nextState.GetAvatarStateV2(_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.False(nextAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - 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(101, nextTradableItem.RequiredBlockIndex); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Empty(nextAvatarState.mailBox.OfType()); - var cancelMail = nextAvatarState.mailBox.OfType().First(); - Assert.Equal(orderId, cancelMail.OrderId); - var nextReceiptList = new OrderDigestListState((Dictionary)nextState.GetState(orderDigestList.Address)); - Assert.Empty(nextReceiptList.OrderDigestList); - - var sellCancelItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)nextState.GetState(Addresses.GetItemAddress(itemId))); - Assert.Equal(101, sellCancelItem.RequiredBlockIndex); - } - - [Fact] - public void Execute_Throw_FailedLoadStateException() - { - var action = new SellCancellation8 - { - orderId = default, - sellerAvatarAddress = default, - itemSubType = ItemSubType.Weapon, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = _initialState, - RandomSeed = 0, - Signer = default, - }) - ); - } - - [Fact] - public void Execute_Throw_NotEnoughClearedStageLevelException() - { - var avatarState = new AvatarState(_initialState.GetAvatarState(_avatarAddress)) - { - worldInformation = new WorldInformation( - 0, - _tableSheets.WorldSheet, - 0 - ), - }; - - IAccount prevState = _initialState.SetState(_avatarAddress, avatarState.Serialize()); - - var action = new SellCancellation8 - { - orderId = 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 orderId = 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 = ShardedShopStateV2.DeriveAddress(tradableItem.ItemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - - Order order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - 0, - tradableItem.ItemSubType, - 1 - ); - var orderDigest = new OrderDigest( - _agentAddress, - order.StartedBlockIndex, - order.ExpiredBlockIndex, - orderId, - tradableItem.TradableId, - order.Price, - 0, - 0, - 0, - 1 - ); - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - orderDigestList.Add(orderDigest); - - IAccount prevState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(orderDigestList.Address, orderDigestList.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation8 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = tradableItem.ItemSubType, - tradableId = tradableItem.TradableId, - }; - - ItemDoesNotExistException exc = Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }) - ); - } - - [Theory] - [InlineData(false, true)] - [InlineData(true, false)] - public void Execute_Throw_InvalidAddressException(bool useAgentAddress, bool useAvatarAddress) - { - Guid orderId = Guid.NewGuid(); - ItemUsable itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - Guid.NewGuid(), - Sell6.ExpiredBlockIndex); - Address shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemUsable.ItemSubType, orderId); - var shopState = new ShardedShopStateV2(shardedShopAddress); - - Address agentAddress = useAgentAddress ? _agentAddress : default; - Address avatarAddress = useAvatarAddress ? _avatarAddress : default; - Order order = OrderFactory.Create( - agentAddress, - avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - itemUsable.ItemId, - 0, - itemUsable.ItemSubType, - 1 - ); - var orderDigest = new OrderDigest( - _agentAddress, - order.StartedBlockIndex, - order.ExpiredBlockIndex, - orderId, - itemUsable.ItemId, - order.Price, - 0, - 0, - itemUsable.Id, - 1 - ); - shopState.Add(orderDigest, 0); - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - orderDigestList.Add(orderDigest); - - IAccount prevState = _initialState - .SetState(Order.DeriveAddress(orderId), order.Serialize()) - .SetState(orderDigestList.Address, orderDigestList.Serialize()) - .SetState(shardedShopAddress, shopState.Serialize()); - - var action = new SellCancellation8 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemUsable.ItemSubType, - tradableId = itemUsable.ItemId, - }; - - Assert.Throws(() => action.Execute(new ActionContext() - { - BlockIndex = 0, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }) - ); - } - - [Fact] - public void PlainValue() - { - var action = new SellCancellation8 - { - sellerAvatarAddress = _avatarAddress, - orderId = Guid.NewGuid(), - itemSubType = ItemSubType.Weapon, - tradableId = Guid.NewGuid(), - }; - - var plainValue = action.PlainValue; - - var action2 = new SellCancellation8(); - action2.LoadPlainValue(plainValue); - - Assert.Equal(plainValue, action2.PlainValue); - } - - [Theory] - [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.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_ReconfigureFungibleItem( - ItemType itemType, - string guid, - int itemCount, - int inventoryCount, - int expectedCount, - bool fromPreviousAction - ) - { - var avatarState = _initialState.GetAvatarState(_avatarAddress); - ITradableItem tradableItem; - Guid itemId = new Guid(guid); - Guid orderId = Guid.NewGuid(); - ItemSubType itemSubType; - const long requiredBlockIndex = Order.ExpirationInterval; - if (itemType == ItemType.Equipment) - { - var itemUsable = ItemFactory.CreateItemUsable( - _tableSheets.EquipmentItemSheet.First, - itemId, - requiredBlockIndex); - tradableItem = (ITradableItem)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 = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - ShardedShopStateV2 shopState = new ShardedShopStateV2(shardedShopAddress); - Order order = OrderFactory.Create( - _agentAddress, - _avatarAddress, - orderId, - new FungibleAssetValue(_goldCurrencyState.Currency, 100, 0), - tradableItem.TradableId, - requiredBlockIndex, - itemSubType, - itemCount - ); - - var expirationMail = new OrderExpirationMail( - 101, - orderId, - order.ExpiredBlockIndex, - orderId - ); - avatarState.mailBox.Add(expirationMail); - - var orderDigestList = new OrderDigestListState(OrderDigestListState.DeriveAddress(_avatarAddress)); - IAccount 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); - } - - ITradableItem sellItem; - sellItem = order.Sell(avatarState); - OrderDigest orderDigest = order.Digest(avatarState, _tableSheets.CostumeStatSheet); - shopState.Add(orderDigest, requiredBlockIndex); - orderDigestList.Add(orderDigest); - - 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); - Assert.True(avatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out var outItem)); - Assert.Equal(itemCount, outItem.count); - - 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 sellCancellationAction = new SellCancellation8 - { - orderId = orderId, - sellerAvatarAddress = _avatarAddress, - itemSubType = itemSubType, - tradableId = itemId, - }; - var nextState = sellCancellationAction.Execute(new ActionContext - { - BlockIndex = 101, - PreviousState = prevState, - RandomSeed = 0, - Signer = _agentAddress, - }); - - ShardedShopStateV2 nextShopState = new ShardedShopStateV2((Dictionary)nextState.GetState(shardedShopAddress)); - Assert.Empty(nextShopState.OrderDigestList); - - var nextAvatarState = nextState.GetAvatarStateV2(_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.False(nextAvatarState.inventory.TryGetLockedItem(new OrderLock(orderId), out _)); - 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(101, nextTradableItem.RequiredBlockIndex); - Assert.Equal(30, nextAvatarState.mailBox.Count); - Assert.Empty(nextAvatarState.mailBox.OfType()); - var cancelMail = nextAvatarState.mailBox.OfType().First(); - Assert.Equal(orderId, cancelMail.OrderId); - var nextReceiptList = new OrderDigestListState((Dictionary)nextState.GetState(orderDigestList.Address)); - Assert.Empty(nextReceiptList.OrderDigestList); - - var sellCancelItem = (ITradableItem)ItemFactory.Deserialize((Dictionary)nextState.GetState(Addresses.GetItemAddress(itemId))); - Assert.Equal(101, sellCancelItem.RequiredBlockIndex); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAsset2Test.cs b/.Lib9c.Tests/Action/TransferAsset2Test.cs deleted file mode 100644 index 8bf8a7c724..0000000000 --- a/.Lib9c.Tests/Action/TransferAsset2Test.cs +++ /dev/null @@ -1,348 +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 Account( - 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 - ); - IAccount nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - 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 Account( - 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, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var prevState = new Account( - 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, - BlockIndex = 1, - }); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 380001, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new Account( - 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, - 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 Account( - 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, - 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 Account( - 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, - 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().Address); - var prevState = new Account( - 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, - BlockIndex = 1, - }); - }); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [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 Account(MockState.Empty), - Signer = _sender, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAsset3Test.cs b/.Lib9c.Tests/Action/TransferAsset3Test.cs deleted file mode 100644 index cb74a2f454..0000000000 --- a/.Lib9c.Tests/Action/TransferAsset3Test.cs +++ /dev/null @@ -1,379 +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 Account(mockState); - var action = new TransferAsset3( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - IAccount nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - 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 Account( - 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, - 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 Account( - 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, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new Account( - 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, - 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 Account( - 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, - 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 Account( - 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, - 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().Address); - var prevState = new Account( - 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, - BlockIndex = 1, - }); - }); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [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 Account( - 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, - 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/TransferAsset4Test.cs b/.Lib9c.Tests/Action/TransferAsset4Test.cs deleted file mode 100644 index 1877f65a4d..0000000000 --- a/.Lib9c.Tests/Action/TransferAsset4Test.cs +++ /dev/null @@ -1,301 +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.Helper; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Xunit; - - public class TransferAsset4Test - { - 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 TransferAsset4(_sender, _recipient, _currency * 100, new string(' ', 100))); - } - - [Fact] - public void Execute() - { - var contractAddress = _sender.Derive(nameof(RequestPledge)); - var patronAddress = new PrivateKey().Address; - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset4( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - IAccount nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - - Assert.Equal(_currency * 900, nextState.GetBalance(_sender, _currency)); - Assert.Equal(_currency * 110, nextState.GetBalance(_recipient, _currency)); - } - - [Fact] - public void Execute_Throw_InvalidTransferSignerException() - { - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10) - .SetBalance(_sender, Currencies.Mead * 1)); - var action = new TransferAsset4( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - // 송금자가 직접 사인하지 않으면 실패해야 합니다. - Signer = _recipient, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void Execute_Throw_InvalidTransferRecipientException() - { - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_sender, Currencies.Mead * 1)); - // Should not allow TransferAsset with same sender and recipient. - var action = new TransferAsset4( - sender: _sender, - recipient: _sender, - amount: _currency * 100 - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - var prevState = new Account( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset4( - sender: _sender, - recipient: _recipient, - amount: _currency * 100000 - ); - - InsufficientBalanceException exc = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - }); - - Assert.Equal(_sender, exc.Address); - Assert.Equal(_currency, exc.Balance.Currency); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void Execute_Throw_InvalidTransferMinterException(bool minterAsSender) - { - Address minter = minterAsSender ? _sender : _recipient; -#pragma warning disable CS0618 - // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 - var currencyBySender = Currency.Legacy("NCG", 2, minter); -#pragma warning restore CS0618 - var prevState = new Account( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, currencyBySender * 1000) - .SetBalance(_recipient, currencyBySender * 10) - .SetBalance(_sender, Currencies.Mead * 1)); - var action = new TransferAsset4( - sender: _sender, - recipient: _recipient, - amount: currencyBySender * 100 - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { minter }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void PlainValue(string memo) - { - var action = new TransferAsset4(_sender, _recipient, _currency * 100, memo); - Dictionary plainValue = (Dictionary)action.PlainValue; - Dictionary values = (Dictionary)plainValue["values"]; - - Assert.Equal((Text)"transfer_asset4", 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 TransferAsset4(); - 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 Account( - MockState.Empty - .SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetBalance(_sender, crystal * 1000) - .SetBalance(_sender, Currencies.Mead * 1)); - var action = new TransferAsset4( - sender: _sender, - recipient: _recipient, - amount: 1000 * crystal - ); - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - })); - } - - [Fact] - public void LoadPlainValue_ThrowsMemoLengthOverflowException() - { - var action = new TransferAsset4(); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_asset3") - .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 TransferAsset4(_sender, _recipient, _currency * 100, memo); - - using var ms = new MemoryStream(); - formatter.Serialize(ms, action); - - ms.Seek(0, SeekOrigin.Begin); - var deserialized = (TransferAsset4)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/TransferAssetTest0.cs b/.Lib9c.Tests/Action/TransferAssetTest0.cs deleted file mode 100644 index 46cfb33ed6..0000000000 --- a/.Lib9c.Tests/Action/TransferAssetTest0.cs +++ /dev/null @@ -1,302 +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 TransferAsset0(_sender, _recipient, _currency * 100, new string(' ', 100))); - } - - [Fact] - public void Execute() - { - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAsset0( - sender: _sender, - recipient: _recipient, - amount: _currency * 100 - ); - IAccount nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - 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 Account( - 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, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var prevState = new Account( - 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, - BlockIndex = 1, - }); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 380001, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new Account( - 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, - 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 Account( - 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, - 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 Account( - 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, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [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 c792511094..0000000000 --- a/.Lib9c.Tests/Action/TransferAssets0Test.cs +++ /dev/null @@ -1,452 +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 Account(mockState); - var action = new TransferAssets0( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - (_recipient2, _currency * 100), - } - ); - IAccount nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - 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 Account( - 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, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void ExecuteWithInvalidRecipient() - { - var prevState = new Account( - 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, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void ExecuteWithInsufficientBalance() - { - var prevState = new Account( - 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, - 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 Account( - 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, - 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 Account( - 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, - 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().Address); - var prevState = new Account( - 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, - BlockIndex = 1, - }); - }); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [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 Account(MockState.Empty), - Signer = _sender, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void Execute_Throw_InvalidTransferCurrencyException() - { - var crystal = CrystalCalculator.CRYSTAL; - var prevState = new Account( - 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, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/TransferAssets2Test.cs b/.Lib9c.Tests/Action/TransferAssets2Test.cs deleted file mode 100644 index 1bd683d9de..0000000000 --- a/.Lib9c.Tests/Action/TransferAssets2Test.cs +++ /dev/null @@ -1,364 +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 Libplanet.Types.Assets; - using Nekoyume; - using Nekoyume.Action; - using Nekoyume.Helper; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Xunit; - - public class TransferAssets2Test - { - 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 TransferAssets2( - _sender, - new List<(Address, FungibleAssetValue)>() - { - (_recipient, _currency * 100), - }, - new string(' ', 100) - ) - ); - } - - [Fact] - public void Execute() - { - var contractAddress = _sender.Derive(nameof(RequestPledge)); - var patronAddress = new PrivateKey().Address; - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAssets2( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - (_recipient2, _currency * 100), - } - ); - IAccount nextState = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - 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)); - Assert.Equal(Currencies.Mead * 0, nextState.GetBalance(_sender, Currencies.Mead)); - Assert.Equal(Currencies.Mead * 0, nextState.GetBalance(patronAddress, Currencies.Mead)); - } - - [Fact] - public void Execute_Throw_InvalidTransferSignerException() - { - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAssets2( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - } - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - // 송금자가 직접 사인하지 않으면 실패해야 합니다. - Signer = _recipient, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _recipient); - Assert.Equal(exc.TxSigner, _recipient); - } - - [Fact] - public void Execute_Throw_InvalidTransferRecipientException() - { - var prevState = new Account( - MockState.Empty - .SetBalance(_sender, _currency * 1000)); - // Should not allow TransferAsset with same sender and recipient. - var action = new TransferAssets2( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_sender, _currency * 100), - } - ); - - var exc = Assert.Throws(() => - { - _ = action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - }); - - Assert.Equal(exc.Sender, _sender); - Assert.Equal(exc.Recipient, _sender); - } - - [Fact] - public void Execute_Throw_InsufficientBalanceException() - { - var prevState = new Account( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, _currency * 1000) - .SetBalance(_recipient, _currency * 10)); - var action = new TransferAssets2( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100000), - } - ); - - InsufficientBalanceException exc = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - }); - - Assert.Equal(_sender, exc.Address); - Assert.Equal(_currency, exc.Balance.Currency); - } - - [Fact] - public void Execute_Throw_InvalidTransferMinterException() - { -#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 Account( - MockState.Empty - .SetState(_recipient, new AgentState(_recipient).Serialize()) - .SetBalance(_sender, currencyBySender * 1000) - .SetBalance(_recipient, currencyBySender * 10)); - var action = new TransferAssets2( - sender: _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, currencyBySender * 100), - } - ); - var ex = Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = 1, - }); - }); - - Assert.Equal(new[] { _sender }, ex.Minters); - Assert.Equal(_sender, ex.Sender); - Assert.Equal(_recipient, ex.Recipient); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void PlainValue(string memo) - { - var action = new TransferAssets2( - _sender, - new List<(Address, FungibleAssetValue)> - { - (_recipient, _currency * 100), - }, - memo - ); - - Dictionary plainValue = (Dictionary)action.PlainValue; - Dictionary values = (Dictionary)plainValue["values"]; - - var recipients = (List)values["recipients"]; - var info = (List)recipients[0]; - Assert.Equal((Text)"transfer_assets2", plainValue["type_id"]); - 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 plainValue = Dictionary.Empty - .Add("type_id", "transfer_assets") - .Add("values", new Dictionary(pairs)); - var action = new TransferAssets2(); - 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 TransferAssets2(); - var plainValue = Dictionary.Empty - .Add("type_id", "transfer_assets") - .Add("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()), - })); - - Assert.Throws(() => action.LoadPlainValue(plainValue)); - } - - [Theory] - [InlineData(null)] - [InlineData("Nine Chronicles")] - public void SerializeWithDotnetAPI(string memo) - { - var formatter = new BinaryFormatter(); - var action = new TransferAssets2( - _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 = (TransferAssets2)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 < TransferAssets2.RecipientsCapacity + 1; i++) - { - recipients.Add((_recipient, _currency * 100)); - } - - var action = new TransferAssets2(_sender, recipients); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = new Account(MockState.Empty), - Signer = _sender, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void Execute_Throw_InvalidTransferCurrencyException() - { - var crystal = CrystalCalculator.CRYSTAL; - var prevState = new Account( - MockState.Empty - .SetState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetBalance(_sender, crystal * 1000)); - var action = new TransferAssets2( - sender: _sender, - recipients: new List<(Address, FungibleAssetValue)> - { - (_recipient, 1000 * crystal), - (_recipient, 100 * _currency), - } - ); - Assert.Throws(() => action.Execute(new ActionContext() - { - PreviousState = prevState, - Signer = _sender, - BlockIndex = TransferAsset3.CrystalTransferringRestrictionStartIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs b/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs index 7f18abbd16..40b896186a 100644 --- a/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs +++ b/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs @@ -231,5 +231,78 @@ public void Execute_SlotIsAlreadyUnlockedException() BlockIndex = blockIndex, })); } + + [Theory] + [InlineData(true, 6)] + [InlineData(false, 6)] + [InlineData(true, 7)] + [InlineData(false, 7)] + public void Execute_CRYSTAL(bool legacyState, int slotIndex) + { + var context = new ActionContext(); + var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex); + var gameConfig = state.GetGameConfigState(); + var cost = slotIndex == 6 + ? gameConfig.RuneStatSlotCrystalUnlockCost + : gameConfig.RuneSkillSlotCrystalUnlockCost; + state = state.MintAsset(context, agentAddress, cost * Currencies.Crystal); + if (legacyState) + { + foreach (var battleType in new[] { BattleType.Adventure, BattleType.Arena, BattleType.Raid }) + { + var runeSlotState = new RuneSlotState(battleType); + var serialized = (List)runeSlotState.Serialize(); + var rawSlots = new List(((List)serialized[1]).Take(6)); + state = state.SetState( + RuneSlotState.DeriveAddress(avatarAddress, battleType), + List.Empty.Add(battleType.Serialize()).Add(rawSlots)); + } + } + + var action = new UnlockRuneSlot() + { + AvatarAddress = avatarAddress, + SlotIndex = slotIndex, + }; + + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = 0, + Signer = agentAddress, + }; + + state = action.Execute(ctx); + var adventureAddr = RuneSlotState.DeriveAddress(avatarAddress, BattleType.Adventure); + if (state.TryGetState(adventureAddr, out List adventureRaw)) + { + var s = new RuneSlotState(adventureRaw); + var slot = s.GetRuneSlot().FirstOrDefault(x => x.Index == slotIndex); + Assert.NotNull(slot); + Assert.False(slot.IsLock); + } + + var arenaAddr = RuneSlotState.DeriveAddress(avatarAddress, BattleType.Arena); + if (state.TryGetState(arenaAddr, out List arenaRaw)) + { + var s = new RuneSlotState(arenaRaw); + var slot = s.GetRuneSlot().FirstOrDefault(x => x.Index == slotIndex); + Assert.NotNull(slot); + Assert.False(slot.IsLock); + } + + var raidAddr = RuneSlotState.DeriveAddress(avatarAddress, BattleType.Raid); + if (state.TryGetState(raidAddr, out List raidRaw)) + { + var s = new RuneSlotState(raidRaw); + var slot = s.GetRuneSlot().FirstOrDefault(x => x.Index == slotIndex); + Assert.NotNull(slot); + Assert.False(slot.IsLock); + } + + var balance = state.GetBalance(agentAddress, Currencies.Crystal); + Assert.Equal("0", balance.GetQuantityString()); + } } } diff --git a/.Lib9c.Tests/Action/UpdateSell0Test.cs b/.Lib9c.Tests/Action/UpdateSell0Test.cs deleted file mode 100644 index 3819505da5..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell0Test.cs +++ /dev/null @@ -1,337 +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 IAccount _initialState; - - public UpdateSell0Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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, - RandomSeed = 0, - 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 Account(MockState.Empty), - 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, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/UpdateSell2Test.cs b/.Lib9c.Tests/Action/UpdateSell2Test.cs deleted file mode 100644 index e34a73d722..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell2Test.cs +++ /dev/null @@ -1,318 +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 IAccount _initialState; - - public UpdateSell2Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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, - RandomSeed = 0, - 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 Account(MockState.Empty), - 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, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/UpdateSell3Test.cs b/.Lib9c.Tests/Action/UpdateSell3Test.cs deleted file mode 100644 index 91e0376e6b..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell3Test.cs +++ /dev/null @@ -1,389 +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 IAccount _initialState; - - public UpdateSell3Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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, - RandomSeed = 0, - 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 Account(MockState.Empty), - 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 Account(MockState.Empty), - 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 15a963f373..0000000000 --- a/.Lib9c.Tests/Action/UpdateSell4Test.cs +++ /dev/null @@ -1,408 +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 IAccount _initialState; - - public UpdateSell4Test(ITestOutputHelper outputHelper) - { - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Verbose() - .WriteTo.TestOutput(outputHelper) - .CreateLogger(); - - _initialState = new Account(MockState.Empty); - 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().Address; - var agentState = new AgentState(_agentAddress); - _avatarAddress = new PrivateKey().Address; - var rankingMapAddress = new PrivateKey().Address; - _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 = (ITradableItem)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, - RandomSeed = 0, - 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 Account(MockState.Empty), - 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 Account(MockState.Empty), - 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.Tests/Lib9c.Tests.csproj b/.Lib9c.Tests/Lib9c.Tests.csproj index fb877391a1..dae09cd2ea 100644 --- a/.Lib9c.Tests/Lib9c.Tests.csproj +++ b/.Lib9c.Tests/Lib9c.Tests.csproj @@ -58,4 +58,8 @@ + + + + diff --git a/.Lib9c.Tests/Model/RuneSlotStateTest.cs b/.Lib9c.Tests/Model/RuneSlotStateTest.cs index 1d389d0419..25ac8d098c 100644 --- a/.Lib9c.Tests/Model/RuneSlotStateTest.cs +++ b/.Lib9c.Tests/Model/RuneSlotStateTest.cs @@ -1,5 +1,6 @@ namespace Lib9c.Tests.Model { + using System.Linq; using Bencodex.Types; using Nekoyume.Model.EnumType; using Nekoyume.Model.State; @@ -16,5 +17,19 @@ public void Serialize() Assert.Equal(state.Serialize(), deserialized.Serialize()); } + + [Fact] + public void Deserialize_Add_Slots() + { + var runeSlotState = new RuneSlotState(BattleType.Adventure); + var serialized = (List)runeSlotState.Serialize(); + var rawSlots = new List(((List)serialized[1]).Take(6)); + serialized = List.Empty.Add(BattleType.Adventure.Serialize()).Add(rawSlots); + var deserialized = new RuneSlotState(serialized); + + var runeSlots = deserialized.GetRuneSlot(); + Assert.Equal(8, runeSlots.Count); + Assert.Equal(2, runeSlots.Count(r => r.RuneSlotType == RuneSlotType.Crystal)); + } } } diff --git a/.Lib9c.Tools/SubCommand/Market.cs b/.Lib9c.Tools/SubCommand/Market.cs index ca680b55da..4c1d66cc05 100644 --- a/.Lib9c.Tools/SubCommand/Market.cs +++ b/.Lib9c.Tools/SubCommand/Market.cs @@ -42,8 +42,8 @@ public void Query( [Option( 'T', Description = "Filter by item type. This implicitly filters out transactions " + - "made with " + nameof(Buy) + " action version prior to " + nameof(Buy5) + - ". This can be applied multiple times (meaning: match any of them). " + + "made with " + nameof(Buy) + "." + + "This can be applied multiple times (meaning: match any of them). " + "The list of available types can be found in " + nameof(ItemSubType) + " enum declared in Lib9c/Model/Item/ItemType.cs file.")] string[] itemType = null, diff --git a/.Libplanet b/.Libplanet index d049d46d1b..81408bf2f1 160000 --- a/.Libplanet +++ b/.Libplanet @@ -1 +1 @@ -Subproject commit d049d46d1b7373a90c74d6d51795f3292fb27c25 +Subproject commit 81408bf2f1fdce9f3674009438e2a038322ec2b6 diff --git a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteBlockChainStates.cs b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteBlockChainStates.cs index 69b86ca756..18e59a09af 100644 --- a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteBlockChainStates.cs +++ b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteBlockChainStates.cs @@ -41,10 +41,7 @@ public ValidatorSet GetValidatorSet(BlockHash? offset) return new RemoteBlockState(_explorerEndpoint, offset).GetValidatorSet(); } - public IAccountState GetAccountState(BlockHash? offset) => - throw new NotImplementedException(); - - public IAccountState GetBlockState(BlockHash? offset) + public IAccountState GetAccountState(BlockHash? offset) { return new RemoteBlockState(_explorerEndpoint, offset); } diff --git a/.gitallowed b/.gitallowed index fc39bc3afa..b8206fbff0 100644 --- a/.gitallowed +++ b/.gitallowed @@ -17,4 +17,12 @@ f8960846e9ae4ad1c23686f74c8e5f80f22336b6f2175be21db82afa8823c92d 210d1374d8f068de657de6b991e63888da9cadbc68e505ac917b35568b5340f8 # For MintAssetsTest -7f5d25371e58c0f3d5a33511450f73c2e0fa4fac32a92e1cbe64d3bf2fef6328 \ No newline at end of file +7f5d25371e58c0f3d5a33511450f73c2e0fa4fac32a92e1cbe64d3bf2fef6328 + +# For IssueTokensFromGarageTest +baa2081d3b485ef2906c95a3965531ec750a74cfaefe91d0c3061865608b426c + +# For Genesis +4582250d0da33b06779a8475d283d5dd210c683b9b999d74d03fac4f58fa6bce +ade4c29773fe83c1a51da6a667a5a26f08848155674637d43fe636b94a320514 +209b22087045ec834f01249c8661c2734cea41ccc5d8c9a273a4c8c0521d22ec \ No newline at end of file diff --git a/Lib9c/Action/BattleArena1.cs b/Lib9c/Action/BattleArena1.cs deleted file mode 100644 index 2259256f26..0000000000 --- a/Lib9c/Action/BattleArena1.cs +++ /dev/null @@ -1,318 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(random); - var log = simulator.SimulateV1(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 082f8a71cc..0000000000 --- a/Lib9c/Action/BattleArena10.cs +++ /dev/null @@ -1,465 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV3(random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 aa5a7182a0..0000000000 --- a/Lib9c/Action/BattleArena11.cs +++ /dev/null @@ -1,463 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV4(random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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/BattleArena12.cs b/Lib9c/Action/BattleArena12.cs deleted file mode 100644 index 14ff41e2bd..0000000000 --- a/Lib9c/Action/BattleArena12.cs +++ /dev/null @@ -1,470 +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/1938 - /// - [Serializable] - [ActionType("battle_arena12")] - [ActionObsolete(ActionObsoleteConfig.V200070ObsoleteIndex)] - public class BattleArena12 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V200070ObsoleteIndex, context); - if (championshipId == 6 && round >= 2 || championshipId > 6) - { - throw new ActionObsoletedException(); - } - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulator(random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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/BattleArena13.cs b/Lib9c/Action/BattleArena13.cs deleted file mode 100644 index 0349f968cf..0000000000 --- a/Lib9c/Action/BattleArena13.cs +++ /dev/null @@ -1,463 +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/2094 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200092ObsoleteIndex)] - [ActionType("battle_arena13")] - public class BattleArena13 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var random = context.GetRandom(); - 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(enemyAvatarAddress, 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 ArenaSimulator(random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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/BattleArena14.cs b/Lib9c/Action/BattleArena14.cs deleted file mode 100644 index e5b67347a9..0000000000 --- a/Lib9c/Action/BattleArena14.cs +++ /dev/null @@ -1,447 +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/2195 - /// - [Serializable] - [ActionType("battle_arena14")] - public class BattleArena14 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - 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."); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); - - var gameConfigState = states.GetGameConfigState(); - avatarState.ValidEquipmentAndCostumeV2(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex, gameConfigState); - - // 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(BattleArena14)}] ChampionshipId({arenaRow.ChampionshipId}) - " + - $"round({round})"); - } - - if (!roundData.IsTheRoundOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleArena14)} : 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(BattleArena14)}] ChampionshipId({roundData.ChampionshipId}) - " + - $"round({roundData.Round})"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena14)}] my avatar address : {myAvatarAddress}"); - } - - if (!arenaParticipants.AvatarAddresses.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleArena14)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena14)}] my avatar address : {myAvatarAddress}"); - } - - var battleArenaInterval = roundData.ArenaType == ArenaType.OffSeason - ? 1 - : gameConfigState.BattleArenaInterval; - if (context.BlockIndex - myArenaAvatarState.LastBattleBlockIndex < battleArenaInterval) - { - throw new CoolDownBlockException( - $"[{nameof(BattleArena14)}] LastBattleBlockIndex : " + - $"{myArenaAvatarState.LastBattleBlockIndex} " + - $"CurrentBlockIndex : {context.BlockIndex}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena14)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaScoreAdr = ArenaScore.DeriveAddress( - myAvatarAddress, - roundData.ChampionshipId, - roundData.Round); - if (!states.TryGetArenaScore(myArenaScoreAdr, out var myArenaScore)) - { - throw new ArenaScoreNotFoundException( - $"[{nameof(BattleArena14)}] 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(BattleArena14)}] 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(BattleArena14)}] 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(BattleArena14)}] 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(BattleArena14)}] " + - $"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(enemyAvatarAddress, 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulator(random); - var log = simulator.Simulate( - myArenaPlayerDigest, - enemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 6d690bbacd..0000000000 --- a/Lib9c/Action/BattleArena2.cs +++ /dev/null @@ -1,337 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(random); - var log = simulator.Simulate(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 df8ddb80d3..0000000000 --- a/Lib9c/Action/BattleArena3.cs +++ /dev/null @@ -1,338 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(random); - var log = simulator.Simulate(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 1d6e9b1433..0000000000 --- a/Lib9c/Action/BattleArena4.cs +++ /dev/null @@ -1,360 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(random); - var log = simulator.Simulate(ExtraMyArenaPlayerDigest, ExtraEnemyArenaPlayerDigest, arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 a36ffd61a0..0000000000 --- a/Lib9c/Action/BattleArena5.cs +++ /dev/null @@ -1,392 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 35ca737097..0000000000 --- a/Lib9c/Action/BattleArena6.cs +++ /dev/null @@ -1,411 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV1(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 3ce8849245..0000000000 --- a/Lib9c/Action/BattleArena7.cs +++ /dev/null @@ -1,466 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV2(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 a60080b5cf..0000000000 --- a/Lib9c/Action/BattleArena8.cs +++ /dev/null @@ -1,472 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV3(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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 02b9d6171a..0000000000 --- a/Lib9c/Action/BattleArena9.cs +++ /dev/null @@ -1,470 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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(); - var random = context.GetRandom(); - for (var i = 0; i < ticket; i++) - { - var simulator = new ArenaSimulatorV3(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - if (log.Result.Equals(ArenaLog.ArenaResult.Win)) - { - winCount++; - } - else - { - defeatCount++; - } - - var reward = RewardSelector.Select( - 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/BattleGrandFinale1.cs b/Lib9c/Action/BattleGrandFinale1.cs deleted file mode 100644 index bc94a48bb1..0000000000 --- a/Lib9c/Action/BattleGrandFinale1.cs +++ /dev/null @@ -1,238 +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.Arena; -using Nekoyume.Exceptions; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.GrandFinale; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.GrandFinale; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Introduced at ... - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType(ActionTypeName)] - public class BattleGrandFinale1 : GameAction, IBattleGrandFinaleV1 - { - private const string ActionTypeName = "battle_grand_finale"; - public const int WinScore = 20; - public const int LoseScore = 1; - public const int DefaultScore = 1000; - public const string ScoreDeriveKey = "grand_finale_score_{0}"; - - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int grandFinaleId; - - public List costumes; - public List equipments; - - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - - Address IBattleGrandFinaleV1.MyAvatarAddress => myAvatarAddress; - Address IBattleGrandFinaleV1.EnemyAvatarAddress => enemyAvatarAddress; - int IBattleGrandFinaleV1.GrandFinaleId => grandFinaleId; - IEnumerable IBattleGrandFinaleV1.Costumes => costumes; - IEnumerable IBattleGrandFinaleV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [GrandFinaleIdKey] = grandFinaleId.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(); - grandFinaleId = plainValue[GrandFinaleIdKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleGrandFinale exec started", addressesHex); - - #region Validate - - 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 _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(GrandFinaleScheduleSheet), - typeof(GrandFinaleParticipantsSheet), - 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 grandFinaleSheet = sheets.GetSheet(); - if (!grandFinaleSheet.TryGetValue(grandFinaleId, out var grandFinaleRow)) - { - throw new SheetRowNotFoundException(nameof(GrandFinaleScheduleSheet), - $"grandFinale Id : {grandFinaleId}"); - } - - if (!grandFinaleRow.IsOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleGrandFinale1)} : block index({context.BlockIndex}) - "); - } - - var grandFinaleParticipantsSheet = sheets.GetSheet(); - if (!grandFinaleParticipantsSheet.TryGetValue(grandFinaleId, out var participantsRow)) - { - throw new SheetRowNotFoundException(nameof(GrandFinaleParticipantsSheet), - $"grandFinale Id : {grandFinaleId}"); - } - - if (!participantsRow.Participants.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleGrandFinale1)}] my avatar address : {myAvatarAddress}"); - } - - if (!participantsRow.Participants.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleGrandFinale1)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleGrandFinale1)}] my avatar address : {myAvatarAddress}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var scoreAddress = myAvatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, ScoreDeriveKey, grandFinaleId)); - if (!states.TryGetState(scoreAddress, out Integer grandFinaleScore)) - { - grandFinaleScore = DefaultScore; - } - - var informationAdr = GrandFinaleInformation.DeriveAddress( - myAvatarAddress, - grandFinaleId); - GrandFinaleInformation grandFinaleInformation; - if (states.TryGetState(informationAdr, out List serialized)) - { - grandFinaleInformation = new GrandFinaleInformation(serialized); - } - else - { - grandFinaleInformation = new GrandFinaleInformation( - myAvatarAddress, - grandFinaleId); - } - - if (grandFinaleInformation.TryGetBattleRecord(enemyAvatarAddress, - out _)) - { - throw new AlreadyFoughtAvatarException( - $"[{nameof(BattleGrandFinale1)}] enemy avatar address : {enemyAvatarAddress}"); - } - - #endregion - - // 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); - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var random = context.GetRandom(); - var simulator = new ArenaSimulatorV2(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - - var win = log.Result.Equals(ArenaLog.ArenaResult.Win); - grandFinaleScore += win ? WinScore : LoseScore; - grandFinaleInformation.UpdateRecord(enemyAvatarAddress, win); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleGrandFinale Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(scoreAddress, grandFinaleScore) - .SetState(informationAdr, grandFinaleInformation.Serialize()); - } - } -} diff --git a/Lib9c/Action/BattleGrandFinale2.cs b/Lib9c/Action/BattleGrandFinale2.cs deleted file mode 100644 index 17214b3fb0..0000000000 --- a/Lib9c/Action/BattleGrandFinale2.cs +++ /dev/null @@ -1,237 +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.Arena; -using Nekoyume.Exceptions; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model; -using Nekoyume.Model.Arena; -using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.GrandFinale; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Nekoyume.TableData.GrandFinale; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1679 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType(ActionTypeName)] - public class BattleGrandFinale2 : GameAction, IBattleGrandFinaleV1 - { - private const string ActionTypeName = "battle_grand_finale2"; - public const int WinScore = 20; - public const int LoseScore = 1; - public const int DefaultScore = 1000; - public const string ScoreDeriveKey = "grand_finale_score_{0}"; - - public Address myAvatarAddress; - public Address enemyAvatarAddress; - public int grandFinaleId; - - public List costumes; - public List equipments; - - // Todo : Remove this ExtraValue after hard fork - public ArenaPlayerDigest ExtraMyArenaPlayerDigest; - public ArenaPlayerDigest ExtraEnemyArenaPlayerDigest; - - Address IBattleGrandFinaleV1.MyAvatarAddress => myAvatarAddress; - Address IBattleGrandFinaleV1.EnemyAvatarAddress => enemyAvatarAddress; - int IBattleGrandFinaleV1.GrandFinaleId => grandFinaleId; - IEnumerable IBattleGrandFinaleV1.Costumes => costumes; - IEnumerable IBattleGrandFinaleV1.Equipments => equipments; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - [MyAvatarAddressKey] = myAvatarAddress.Serialize(), - [EnemyAvatarAddressKey] = enemyAvatarAddress.Serialize(), - [GrandFinaleIdKey] = grandFinaleId.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(); - grandFinaleId = plainValue[GrandFinaleIdKey].ToInteger(); - costumes = ((List)plainValue[CostumesKey]).Select(e => e.ToGuid()).ToList(); - equipments = ((List)plainValue[EquipmentsKey]).Select(e => e.ToGuid()).ToList(); - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var addressesHex = GetSignerAndOtherAddressesHex( - context, - myAvatarAddress, - enemyAvatarAddress); - - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleGrandFinale exec started", addressesHex); - - #region Validate - - 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 _)) - { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - var sheets = states.GetSheets( - containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(GrandFinaleScheduleSheet), - typeof(GrandFinaleParticipantsSheet), - 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 grandFinaleSheet = sheets.GetSheet(); - if (!grandFinaleSheet.TryGetValue(grandFinaleId, out var grandFinaleRow)) - { - throw new SheetRowNotFoundException(nameof(GrandFinaleScheduleSheet), - $"grandFinale Id : {grandFinaleId}"); - } - - if (!grandFinaleRow.IsOpened(context.BlockIndex)) - { - throw new ThisArenaIsClosedException( - $"{nameof(BattleGrandFinale)} : block index({context.BlockIndex}) - "); - } - - var grandFinaleParticipantsSheet = sheets.GetSheet(); - if (!grandFinaleParticipantsSheet.TryGetValue(grandFinaleId, out var participantsRow)) - { - throw new SheetRowNotFoundException(nameof(GrandFinaleParticipantsSheet), - $"grandFinale Id : {grandFinaleId}"); - } - - if (!participantsRow.Participants.Contains(myAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleGrandFinale)}] my avatar address : {myAvatarAddress}"); - } - - if (!participantsRow.Participants.Contains(enemyAvatarAddress)) - { - throw new AddressNotFoundInArenaParticipantsException( - $"[{nameof(BattleGrandFinale)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var myArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(myAvatarAddress); - if (!states.TryGetArenaAvatarState(myArenaAvatarStateAdr, out var myArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleGrandFinale)}] my avatar address : {myAvatarAddress}"); - } - - var enemyArenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(enemyAvatarAddress); - if (!states.TryGetArenaAvatarState( - enemyArenaAvatarStateAdr, - out var enemyArenaAvatarState)) - { - throw new ArenaAvatarStateNotFoundException( - $"[{nameof(BattleArena)}] enemy avatar address : {enemyAvatarAddress}"); - } - - var scoreAddress = myAvatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, ScoreDeriveKey, grandFinaleId)); - if (!states.TryGetState(scoreAddress, out Integer grandFinaleScore)) - { - grandFinaleScore = DefaultScore; - } - - var informationAdr = GrandFinaleInformation.DeriveAddress( - myAvatarAddress, - grandFinaleId); - GrandFinaleInformation grandFinaleInformation; - if (states.TryGetState(informationAdr, out List serialized)) - { - grandFinaleInformation = new GrandFinaleInformation(serialized); - } - else - { - grandFinaleInformation = new GrandFinaleInformation( - myAvatarAddress, - grandFinaleId); - } - - if (grandFinaleInformation.TryGetBattleRecord(enemyAvatarAddress, - out _)) - { - throw new AlreadyFoughtAvatarException( - $"[{nameof(BattleGrandFinale)}] enemy avatar address : {enemyAvatarAddress}"); - } - - #endregion - - // 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); - var arenaSheets = sheets.GetArenaSimulatorSheets(); - var random = context.GetRandom(); - var simulator = new ArenaSimulatorV3(random); - var log = simulator.Simulate( - ExtraMyArenaPlayerDigest, - ExtraEnemyArenaPlayerDigest, - arenaSheets); - - var win = log.Result.Equals(ArenaLog.ArenaResult.Win); - grandFinaleScore += win ? WinScore : LoseScore; - grandFinaleInformation.UpdateRecord(enemyAvatarAddress, win); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}BattleGrandFinale Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(myArenaAvatarStateAdr, myArenaAvatarState.Serialize()) - .SetState(scoreAddress, grandFinaleScore) - .SetState(informationAdr, grandFinaleInformation.Serialize()); - } - } -} diff --git a/Lib9c/Action/Buy0.cs b/Lib9c/Action/Buy0.cs deleted file mode 100644 index 81f3c6cb5e..0000000000 --- a/Lib9c/Action/Buy0.cs +++ /dev/null @@ -1,239 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, 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, 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 ff9132eecf..0000000000 --- a/Lib9c/Action/Buy10.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 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 IAccount 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); - - 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 b93382a654..0000000000 --- a/Lib9c/Action/Buy11.cs +++ /dev/null @@ -1,319 +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 IAccount 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); - - 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 1ea99d5a0f..0000000000 --- a/Lib9c/Action/Buy2.cs +++ /dev/null @@ -1,226 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, 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, 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 043e7c2d38..0000000000 --- a/Lib9c/Action/Buy3.cs +++ /dev/null @@ -1,234 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, 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, 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 03f032a7c4..0000000000 --- a/Lib9c/Action/Buy4.cs +++ /dev/null @@ -1,231 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var buyerMail = new BuyerMail(buyerResult, ctx.BlockIndex, 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, 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/Buy5.cs b/Lib9c/Action/Buy5.cs deleted file mode 100644 index c3629de24d..0000000000 --- a/Lib9c/Action/Buy5.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.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("buy5")] - public class Buy5 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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(); - - var random = ctx.GetRandom(); - foreach (var purchaseInfo in purchaseInfos) - { - Buy7.PurchaseResult purchaseResult = new Buy7.PurchaseResult(purchaseInfo.productId); - Address shardedShopAddress = - ShardedShopState.DeriveAddress(purchaseInfo.itemSubType, purchaseInfo.productId); - Address sellerAgentAddress = purchaseInfo.sellerAgentAddress; - Address sellerAvatarAddress = purchaseInfo.sellerAvatarAddress; - Guid productId = purchaseInfo.productId; - - purchaseResults.Add(purchaseResult); - - if (purchaseInfo.sellerAgentAddress == ctx.Signer) - { - purchaseResult.errorCode = ErrorCodeInvalidAddress; - continue; - } - - if (!states.TryGetState(shardedShopAddress, out Bencodex.Types.Dictionary shopStateDict)) - { - ShardedShopState shardedShopState = new ShardedShopState(shardedShopAddress); - shopStateDict = (Dictionary) shardedShopState.Serialize(); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - Log.Verbose( - "{AddressesHex}Execute Buy; buyer: {Buyer} seller: {Seller}", - addressesHex, - buyerAvatarAddress, - sellerAvatarAddress); - // Find product from ShardedShopState. - List products = (List) shopStateDict[ProductsKey]; - IValue productIdSerialized = productId.Serialize(); - Dictionary productSerialized = products - .Select(p => (Dictionary) p) - .FirstOrDefault(p => p[LegacyProductIdKey].Equals(productIdSerialized)); - - // Since Bencodex 0.4, Dictionary/List are reference types; so their default values - // are not a empty container, but a null reference: - productSerialized = productSerialized ?? Dictionary.Empty; - - bool fromLegacy = false; - if (productSerialized.Equals(Dictionary.Empty)) - { - // Backward compatibility. - IValue rawShop = states.GetState(Addresses.Shop); - if (!(rawShop is null)) - { - Dictionary legacyShopDict = (Dictionary) rawShop; - Dictionary legacyProducts = (Dictionary) legacyShopDict[LegacyProductsKey]; - IKey productKey = (IKey) productId.Serialize(); - // SoldOut - if (!legacyProducts.ContainsKey(productKey)) - { - purchaseResult.errorCode = ErrorCodeItemDoesNotExist; - continue; - } - - productSerialized = (Dictionary) legacyProducts[productKey]; - legacyProducts = (Dictionary) legacyProducts.Remove(productKey); - legacyShopDict = legacyShopDict.SetItem(LegacyProductsKey, legacyProducts); - states = states.SetState(Addresses.Shop, legacyShopDict); - fromLegacy = true; - } - } - - ShopItem shopItem = new ShopItem(productSerialized); - if (!shopItem.SellerAgentAddress.Equals(sellerAgentAddress)) - { - purchaseResult.errorCode = ErrorCodeItemDoesNotExist; - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Item: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (0 < shopItem.ExpiredBlockIndex && shopItem.ExpiredBlockIndex < context.BlockIndex) - { - purchaseResult.errorCode = ErrorCodeShopItemExpired; - continue; - } - - if (!states.TryGetAvatarState(sellerAgentAddress, sellerAvatarAddress, out var sellerAvatarState)) - { - purchaseResult.errorCode = ErrorCodeFailedLoadingState; - continue; - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Get Seller AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - // Check Balance. - FungibleAssetValue buyerBalance = states.GetBalance(context.Signer, states.GetGoldCurrency()); - if (buyerBalance < shopItem.Price) - { - purchaseResult.errorCode = ErrorCodeInsufficientBalance; - continue; - } - - var tax = shopItem.Price.DivRem(100, out _) * TaxRate; - var taxedPrice = shopItem.Price - tax; - - // Transfer tax. - states = states.TransferAsset( - context, - 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); - - INonFungibleItem nonFungibleItem = (INonFungibleItem) shopItem.ItemUsable ?? shopItem.Costume; - if (!sellerAvatarState.inventory.RemoveNonFungibleItem(nonFungibleItem) && !fromLegacy) - { - purchaseResult.errorCode = ErrorCodeItemDoesNotExist; - continue; - } - - nonFungibleItem.RequiredBlockIndex = context.BlockIndex; - - // Send result mail for buyer, seller. - purchaseResult.shopItem = shopItem; - purchaseResult.itemUsable = shopItem.ItemUsable; - purchaseResult.costume = shopItem.Costume; - var buyerMail = new BuyerMail(purchaseResult, ctx.BlockIndex, random.GenerateRandomGuid(), - ctx.BlockIndex); - purchaseResult.id = buyerMail.id; - - var sellerResult = new Buy7.SellerResult - { - shopItem = shopItem, - itemUsable = shopItem.ItemUsable, - costume = shopItem.Costume, - gold = taxedPrice - }; - var sellerMail = new SellerMail(sellerResult, ctx.BlockIndex, random.GenerateRandomGuid(), - ctx.BlockIndex); - sellerResult.id = sellerMail.id; - sellerResults.Add(sellerResult); - - buyerAvatarState.Update(buyerMail); - if (purchaseResult.itemUsable != null) - { - buyerAvatarState.UpdateFromAddItem2(purchaseResult.itemUsable, false); - } - - if (purchaseResult.costume != null) - { - buyerAvatarState.UpdateFromAddCostume(purchaseResult.costume, false); - } - - sellerAvatarState.Update(sellerMail); - - // Update quest. - buyerAvatarState.questList.UpdateTradeQuest(TradeType.Buy, shopItem.Price); - sellerAvatarState.questList.UpdateTradeQuest(TradeType.Sell, shopItem.Price); - - sellerAvatarState.updatedAt = ctx.BlockIndex; - sellerAvatarState.blockIndex = ctx.BlockIndex; - - buyerAvatarState.UpdateQuestRewards2(materialSheet); - sellerAvatarState.UpdateQuestRewards2(materialSheet); - - states = states.SetState(sellerAvatarAddress, sellerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Seller AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetState(shardedShopAddress, shopStateDict); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set ShopState: {Elapsed}", addressesHex, sw.Elapsed); - } - - buyerMultipleResult.purchaseResults = purchaseResults; - sellerMultipleResult.sellerResults = sellerResults; - - buyerAvatarState.updatedAt = ctx.BlockIndex; - buyerAvatarState.blockIndex = ctx.BlockIndex; - - states = states.SetState(buyerAvatarAddress, buyerAvatarState.Serialize()); - sw.Stop(); - Log.Verbose("{AddressesHex}Buy Set Buyer AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}Buy Total Executed Time: {Elapsed}", addressesHex, ended - started); - - return states; - } - } -} diff --git a/Lib9c/Action/Buy6.cs b/Lib9c/Action/Buy6.cs deleted file mode 100644 index 09ac72ca49..0000000000 --- a/Lib9c/Action/Buy6.cs +++ /dev/null @@ -1,332 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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(); - - var random = ctx.GetRandom(); - 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 = (ITradableItem)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, 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, 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 d3342bbd83..0000000000 --- a/Lib9c/Action/Buy8.cs +++ /dev/null @@ -1,298 +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 IAccount 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); - - 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 73aee18888..0000000000 --- a/Lib9c/Action/Buy9.cs +++ /dev/null @@ -1,304 +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 IAccount 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); - - 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/ClaimMonsterCollectionReward0.cs b/Lib9c/Action/ClaimMonsterCollectionReward0.cs deleted file mode 100644 index 52692d5a4b..0000000000 --- a/Lib9c/Action/ClaimMonsterCollectionReward0.cs +++ /dev/null @@ -1,123 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - Address collectionAddress = MonsterCollectionState0.DeriveAddress(context.Signer, collectionRound); - - 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(); - var random = context.GetRandom(); - for (int i = 0; i < rewardLevel; i++) - { - int level = i + 1; - if (level <= monsterCollectionState.RewardLevel) - { - continue; - } - - List rewards = monsterCollectionState.RewardLevelMap[level]; - Guid id = 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, 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 00d2ef3603..0000000000 --- a/Lib9c/Action/ClaimMonsterCollectionReward2.cs +++ /dev/null @@ -1,97 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - Address inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - Address worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); - Address questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - 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"); - } - - var random = context.GetRandom(); - Guid id = 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, 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 350f7a3672..0000000000 --- a/Lib9c/Action/ClaimStakeReward1.cs +++ /dev/null @@ -1,116 +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 IAccount 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); - var random = context.GetRandom(); - 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, 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, 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 d70517af59..0000000000 --- a/Lib9c/Action/ClaimStakeReward3.cs +++ /dev/null @@ -1,302 +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 IAccount ProcessReward( - IActionContext context, - IAccount states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedAmount, - int itemRewardStep, - int runeRewardStep, - List fixedReward, - List regularReward) - { - var stakedCurrency = stakedAmount.Currency; - - // Regular Reward - var random = context.GetRandom(); - 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, 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, random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - - 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 b308bfe3d2..0000000000 --- a/Lib9c/Action/ClaimStakeReward4.cs +++ /dev/null @@ -1,330 +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 IAccount Execute(IActionContext context) - { - CheckObsolete(ObsoleteBlockIndex, context); - context.UseGas(1); - 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 IAccount ProcessReward( - IActionContext context, - IAccount states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedAmount, - int itemRewardStep, - int runeRewardStep, - int currencyRewardStep, - List fixedReward, - List regularReward) - { - var stakedCurrency = stakedAmount.Currency; - - // Regular Reward - var random = context.GetRandom(); - 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, 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, random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward5.cs b/Lib9c/Action/ClaimStakeReward5.cs deleted file mode 100644 index e9c25f8303..0000000000 --- a/Lib9c/Action/ClaimStakeReward5.cs +++ /dev/null @@ -1,330 +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 ClaimStakeReward5 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - private const string ActionTypeText = "claim_stake_reward5"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200060ObsoleteIndex; - - /// - /// 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 ClaimStakeReward5(Address avatarAddress) : this() - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward5() - { - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - - public override IAccount Execute(IActionContext context) - { - CheckObsolete(ObsoleteBlockIndex, context); - context.UseGas(1); - 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.CalculateAccumulatedItemRewardsV3( - context.BlockIndex, - out var itemV1Step, - out var itemV2Step); - stakeState.CalculateAccumulatedRuneRewardsV3( - context.BlockIndex, - out var runeV1Step, - out var runeV2Step); - stakeState.CalculateAccumulatedCurrencyRewardsV2( - 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 IAccount ProcessReward( - IActionContext context, - IAccount states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedAmount, - int itemRewardStep, - int runeRewardStep, - int currencyRewardStep, - List fixedReward, - List regularReward) - { - var stakedCurrency = stakedAmount.Currency; - - // Regular Reward - var random = context.GetRandom(); - 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, 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, random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward6.cs b/Lib9c/Action/ClaimStakeReward6.cs deleted file mode 100644 index 0cd05be353..0000000000 --- a/Lib9c/Action/ClaimStakeReward6.cs +++ /dev/null @@ -1,474 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -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/2071 - /// - [ActionType(ActionTypeText)] - [ActionObsolete(ObsoleteBlockIndex)] - public class ClaimStakeReward6 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - private const string ActionTypeText = "claim_stake_reward6"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200061ObsoleteIndex; - - /// - /// 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,currency_ticker,currency_decimal_places,decimal_rate -1,50,400000,,Item,,,10 -1,50,500000,,Item,,,800 -1,50,20001,,Rune,,,6000 -2,500,400000,,Item,,,8 -2,500,500000,,Item,,,800 -2,500,20001,,Rune,,,6000 -3,5000,400000,,Item,,,5 -3,5000,500000,,Item,,,800 -3,5000,20001,,Rune,,,6000 -4,50000,400000,,Item,,,5 -4,50000,500000,,Item,,,800 -4,50000,20001,,Rune,,,6000 -5,500000,400000,,Item,,,5 -5,500000,500000,,Item,,,800 -5,500000,20001,,Rune,,,6000"; - - 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; - } - } - } - - /// - /// This is the version 2 of the stake reward sheet. - /// The version 2 is used for calculating the reward for the stake - /// that is accumulated before the table patch. - /// - public static class V2 - { - public const int MaxLevel = 7; - - public const string StakeRegularRewardSheetCsv = - @"level,required_gold,item_id,rate,type,currency_ticker -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, -6,5000000,400000,10,Item, -6,5000000,500000,800,Item, -6,5000000,20001,6000,Rune, -6,5000000,800201,100,Item, -7,10000000,400000,10,Item, -7,10000000,500000,800,Item, -7,10000000,20001,6000,Rune, -7,10000000,600201,50,Item, -7,10000000,800201,50,Item, -7,10000000,,100,Currency,GARAGE -"; - - 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 -6,5000000,500000,2 -7,10000000,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; - } - } - } - - internal Address AvatarAddress { get; private set; } - - Address IClaimStakeRewardV1.AvatarAddress => AvatarAddress; - - public ClaimStakeReward6(Address avatarAddress) : this() - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward6() - { - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - 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.CalculateAccumulatedItemRewards( - context.BlockIndex, - out var itemV1Step, - out var itemV2Step, - out var itemV3Step); - stakeState.CalculateAccumulatedRuneRewards( - context.BlockIndex, - out var runeV1Step, - out var runeV2Step, - out var runeV3Step); - stakeState.CalculateAccumulatedCurrencyRewards( - context.BlockIndex, - out var currencyV1Step, - out var currencyV2Step, - out var currencyV3Step); - stakeState.CalculateAccumulatedCurrencyCrystalRewards( - context.BlockIndex, - out var currencyCrystalV1Step, - out var currencyCrystalV2Step, - out var currencyCrystalV3Step); - 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, - currencyCrystalV1Step, - fixedRewardV1, - regularRewardV1); - } - - if (itemV2Step > 0) - { - var v2Level = Math.Min(level, V2.MaxLevel); - var fixedRewardV2 = V2.StakeRegularFixedRewardSheet[v2Level].Rewards; - var regularRewardV2 = V2.StakeRegularRewardSheet[v2Level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV2Step, - runeV2Step, - currencyV2Step, - currencyCrystalV2Step, - fixedRewardV2, - regularRewardV2); - } - - if (itemV3Step > 0) - { - var regularFixedReward = GetRegularFixedRewardInfos(states, level); - var regularReward = sheets.GetSheet()[level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV3Step, - runeV3Step, - currencyV3Step, - currencyCrystalV3Step, - 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 static List GetRegularFixedRewardInfos( - IAccountState states, - int level) - { - return states.TryGetSheet(out var fixedRewardSheet) - ? fixedRewardSheet[level].Rewards - : new List(); - } - - private IAccount ProcessReward( - IActionContext context, - IAccount states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedFav, - int itemRewardStep, - int runeRewardStep, - int currencyRewardStep, - int currencyCrystalRewardStep, - List fixedReward, - List regularReward) - { - // Regular Reward - var random = context.GetRandom(); - foreach (var reward in regularReward) - { - var rateFav = FungibleAssetValue.Parse( - stakedFav.Currency, - reward.DecimalRate.ToString(CultureInfo.InvariantCulture)); - var rewardQuantityForSingleStep = stakedFav.DivRem(rateFav, out _); - if (rewardQuantityForSingleStep <= 0) - { - continue; - } - - switch (reward.Type) - { - case StakeRegularRewardSheet.StakeRewardType.Item: - { - if (itemRewardStep == 0) - { - continue; - } - - var itemRow = itemSheet[reward.ItemId]; - var item = itemRow is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(itemRow, random); - var majorUnit = (int)rewardQuantityForSingleStep * itemRewardStep; - if (majorUnit < 1) - { - continue; - } - - avatarState.inventory.AddItem(item, majorUnit); - break; - } - case StakeRegularRewardSheet.StakeRewardType.Rune: - { - if (runeRewardStep == 0) - { - continue; - } - - var majorUnit = rewardQuantityForSingleStep * runeRewardStep; - if (majorUnit < 1) - { - continue; - } - - var runeReward = RuneHelper.StakeRune * majorUnit; - 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 isCrystal = reward.CurrencyTicker == Currencies.Crystal.Ticker; - if (isCrystal - ? currencyCrystalRewardStep == 0 - : currencyRewardStep == 0) - { - continue; - } - - var rewardCurrency = reward.CurrencyDecimalPlaces == null - ? Currencies.GetMinterlessCurrency(reward.CurrencyTicker) - : Currency.Uncapped( - reward.CurrencyTicker, - Convert.ToByte(reward.CurrencyDecimalPlaces.Value), - minters: null); - var majorUnit = isCrystal - ? rewardQuantityForSingleStep * currencyCrystalRewardStep - : rewardQuantityForSingleStep * currencyRewardStep; - var rewardFav = rewardCurrency * majorUnit; - states = states.MintAsset( - context, - context.Signer, - rewardFav); - break; - } - default: - throw new ArgumentException( - $"Can't handle reward type: {reward.Type}", - nameof(regularReward)); - } - } - - // Fixed Reward - foreach (var reward in fixedReward) - { - var itemRow = itemSheet[reward.ItemId]; - var item = itemRow is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(itemRow, random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward7.cs b/Lib9c/Action/ClaimStakeReward7.cs deleted file mode 100644 index a69f215ab7..0000000000 --- a/Lib9c/Action/ClaimStakeReward7.cs +++ /dev/null @@ -1,475 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -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/2083 - /// - [ActionType(ActionTypeText)] - [ActionObsolete(ObsoleteBlockIndex)] - public class ClaimStakeReward7 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - private const string ActionTypeText = "claim_stake_reward7"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200063ObsoleteIndex; - - /// - /// 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,currency_ticker,currency_decimal_places,decimal_rate -1,50,400000,,Item,,,10 -1,50,500000,,Item,,,800 -1,50,20001,,Rune,,,6000 -2,500,400000,,Item,,,8 -2,500,500000,,Item,,,800 -2,500,20001,,Rune,,,6000 -3,5000,400000,,Item,,,5 -3,5000,500000,,Item,,,800 -3,5000,20001,,Rune,,,6000 -4,50000,400000,,Item,,,5 -4,50000,500000,,Item,,,800 -4,50000,20001,,Rune,,,6000 -5,500000,400000,,Item,,,5 -5,500000,500000,,Item,,,800 -5,500000,20001,,Rune,,,6000"; - - 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; - } - } - } - - /// - /// This is the version 2 of the stake reward sheet. - /// The version 2 is used for calculating the reward for the stake - /// that is accumulated before the table patch. - /// - public static class V2 - { - public const int MaxLevel = 7; - - public const string StakeRegularRewardSheetCsv = - @"level,required_gold,item_id,rate,type,currency_ticker -1,50,400000,10,Item, -1,50,500000,800,Item, -1,50,20001,6000,Rune, -2,500,400000,4,Item, -2,500,500000,600,Item, -2,500,20001,6000,Rune, -3,5000,400000,2,Item, -3,5000,500000,400,Item, -3,5000,20001,6000,Rune, -4,50000,400000,2,Item, -4,50000,500000,400,Item, -4,50000,20001,6000,Rune, -5,500000,400000,2,Item, -5,500000,500000,400,Item, -5,500000,20001,6000,Rune, -6,5000000,400000,2,Item, -6,5000000,500000,400,Item, -6,5000000,20001,6000,Rune, -6,5000000,800201,50,Item, -7,10000000,400000,2,Item, -7,10000000,500000,400,Item, -7,10000000,20001,6000,Rune, -7,10000000,600201,50,Item, -7,10000000,800201,50,Item, -7,10000000,,100,Currency,GARAGE -"; - - 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 -6,5000000,500000,2 -7,10000000,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; - } - } - } - - internal Address AvatarAddress { get; private set; } - - Address IClaimStakeRewardV1.AvatarAddress => AvatarAddress; - - public ClaimStakeReward7(Address avatarAddress) : this() - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward7() - { - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - - public override IAccount Execute(IActionContext context) - { - CheckObsolete(ObsoleteBlockIndex, context); - context.UseGas(1); - 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.CalculateAccumulatedItemRewards( - context.BlockIndex, - out var itemV1Step, - out var itemV2Step, - out var itemV3Step); - stakeState.CalculateAccumulatedRuneRewards( - context.BlockIndex, - out var runeV1Step, - out var runeV2Step, - out var runeV3Step); - stakeState.CalculateAccumulatedCurrencyRewards( - context.BlockIndex, - out var currencyV1Step, - out var currencyV2Step, - out var currencyV3Step); - stakeState.CalculateAccumulatedCurrencyCrystalRewards( - context.BlockIndex, - out var currencyCrystalV1Step, - out var currencyCrystalV2Step, - out var currencyCrystalV3Step); - 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, - currencyCrystalV1Step, - fixedRewardV1, - regularRewardV1); - } - - if (itemV2Step > 0) - { - var v2Level = Math.Min(level, V2.MaxLevel); - var fixedRewardV2 = V2.StakeRegularFixedRewardSheet[v2Level].Rewards; - var regularRewardV2 = V2.StakeRegularRewardSheet[v2Level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV2Step, - runeV2Step, - currencyV2Step, - currencyCrystalV2Step, - fixedRewardV2, - regularRewardV2); - } - - if (itemV3Step > 0) - { - var regularFixedReward = GetRegularFixedRewardInfos(states, level); - var regularReward = sheets.GetSheet()[level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV3Step, - runeV3Step, - currencyV3Step, - currencyCrystalV3Step, - 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 static List GetRegularFixedRewardInfos( - IAccountState states, - int level) - { - return states.TryGetSheet(out var fixedRewardSheet) - ? fixedRewardSheet[level].Rewards - : new List(); - } - - private IAccount ProcessReward( - IActionContext context, - IAccount states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedFav, - int itemRewardStep, - int runeRewardStep, - int currencyRewardStep, - int currencyCrystalRewardStep, - List fixedReward, - List regularReward) - { - // Regular Reward - var random = context.GetRandom(); - foreach (var reward in regularReward) - { - var rateFav = FungibleAssetValue.Parse( - stakedFav.Currency, - reward.DecimalRate.ToString(CultureInfo.InvariantCulture)); - var rewardQuantityForSingleStep = stakedFav.DivRem(rateFav, out _); - if (rewardQuantityForSingleStep <= 0) - { - continue; - } - - switch (reward.Type) - { - case StakeRegularRewardSheet.StakeRewardType.Item: - { - if (itemRewardStep == 0) - { - continue; - } - - var itemRow = itemSheet[reward.ItemId]; - var item = itemRow is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(itemRow, random); - var majorUnit = (int)rewardQuantityForSingleStep * itemRewardStep; - if (majorUnit < 1) - { - continue; - } - - avatarState.inventory.AddItem(item, majorUnit); - break; - } - case StakeRegularRewardSheet.StakeRewardType.Rune: - { - if (runeRewardStep == 0) - { - continue; - } - - var majorUnit = rewardQuantityForSingleStep * runeRewardStep; - if (majorUnit < 1) - { - continue; - } - - var runeReward = RuneHelper.StakeRune * majorUnit; - 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 isCrystal = reward.CurrencyTicker == Currencies.Crystal.Ticker; - if (isCrystal - ? currencyCrystalRewardStep == 0 - : currencyRewardStep == 0) - { - continue; - } - - var rewardCurrency = reward.CurrencyDecimalPlaces == null - ? Currencies.GetMinterlessCurrency(reward.CurrencyTicker) - : Currency.Uncapped( - reward.CurrencyTicker, - Convert.ToByte(reward.CurrencyDecimalPlaces.Value), - minters: null); - var majorUnit = isCrystal - ? rewardQuantityForSingleStep * currencyCrystalRewardStep - : rewardQuantityForSingleStep * currencyRewardStep; - var rewardFav = rewardCurrency * majorUnit; - states = states.MintAsset( - context, - context.Signer, - rewardFav); - break; - } - default: - throw new ArgumentException( - $"Can't handle reward type: {reward.Type}", - nameof(regularReward)); - } - } - - // Fixed Reward - foreach (var reward in fixedReward) - { - var itemRow = itemSheet[reward.ItemId]; - var item = itemRow is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(itemRow, random); - avatarState.inventory.AddItem(item, reward.Count * itemRewardStep); - } - - return states; - } - } -} diff --git a/Lib9c/Action/ClaimStakeReward8.cs b/Lib9c/Action/ClaimStakeReward8.cs deleted file mode 100644 index 3c8f08503a..0000000000 --- a/Lib9c/Action/ClaimStakeReward8.cs +++ /dev/null @@ -1,503 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Globalization; -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/2106 - /// - [ActionType(ActionTypeText)] - [ActionObsolete(ObsoleteBlockIndex)] - public class ClaimStakeReward8 : GameAction, IClaimStakeReward, IClaimStakeRewardV1 - { - private const string ActionTypeText = "claim_stake_reward8"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200080ObsoleteIndex; - - /// - /// 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,currency_ticker,currency_decimal_places,decimal_rate -1,50,400000,,Item,,,10 -1,50,500000,,Item,,,800 -1,50,20001,,Rune,,,6000 -2,500,400000,,Item,,,8 -2,500,500000,,Item,,,800 -2,500,20001,,Rune,,,6000 -3,5000,400000,,Item,,,5 -3,5000,500000,,Item,,,800 -3,5000,20001,,Rune,,,6000 -4,50000,400000,,Item,,,5 -4,50000,500000,,Item,,,800 -4,50000,20001,,Rune,,,6000 -5,500000,400000,,Item,,,5 -5,500000,500000,,Item,,,800 -5,500000,20001,,Rune,,,6000"; - - 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; - } - } - } - - /// - /// This is the version 2 of the stake reward sheet. - /// The version 2 is used for calculating the reward for the stake - /// that is accumulated before the table patch. - /// - public static class V2 - { - public const int MaxLevel = 7; - - public const string StakeRegularRewardSheetCsv = - @"level,required_gold,item_id,rate,type,currency_ticker -1,50,400000,10,Item, -1,50,500000,800,Item, -1,50,20001,6000,Rune, -2,500,400000,4,Item, -2,500,500000,600,Item, -2,500,20001,6000,Rune, -3,5000,400000,2,Item, -3,5000,500000,400,Item, -3,5000,20001,6000,Rune, -4,50000,400000,2,Item, -4,50000,500000,400,Item, -4,50000,20001,6000,Rune, -5,500000,400000,2,Item, -5,500000,500000,400,Item, -5,500000,20001,6000,Rune, -6,5000000,400000,2,Item, -6,5000000,500000,400,Item, -6,5000000,20001,6000,Rune, -6,5000000,800201,50,Item, -7,10000000,400000,2,Item, -7,10000000,500000,400,Item, -7,10000000,20001,6000,Rune, -7,10000000,600201,50,Item, -7,10000000,800201,50,Item, -7,10000000,,100,Currency,GARAGE -"; - - 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 -6,5000000,500000,2 -7,10000000,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; - } - } - } - - internal Address AvatarAddress { get; private set; } - - Address IClaimStakeRewardV1.AvatarAddress => AvatarAddress; - - public ClaimStakeReward8(Address avatarAddress) : this() - { - AvatarAddress = avatarAddress; - } - - public ClaimStakeReward8() - { - } - - protected override IImmutableDictionary PlainValueInternal => - ImmutableDictionary.Empty - .Add(AvatarAddressKey, AvatarAddress.Serialize()); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue[AvatarAddressKey].ToAddress(); - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - 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.CalculateAccumulatedItemRewards( - context.BlockIndex, - out var itemV1Step, - out var itemV2Step, - out var itemV3Step); - stakeState.CalculateAccumulatedRuneRewards( - context.BlockIndex, - out var runeV1Step, - out var runeV2Step, - out var runeV3Step); - stakeState.CalculateAccumulatedCurrencyRewards( - context.BlockIndex, - out var currencyV1Step, - out var currencyV2Step, - out var currencyV3Step); - stakeState.CalculateAccumulatedCurrencyCrystalRewards( - context.BlockIndex, - out var currencyCrystalV1Step, - out var currencyCrystalV2Step, - out var currencyCrystalV3Step); - 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, - currencyCrystalV1Step, - fixedRewardV1, - regularRewardV1); - } - - if (itemV2Step > 0) - { - var v2Level = Math.Min(level, V2.MaxLevel); - var fixedRewardV2 = V2.StakeRegularFixedRewardSheet[v2Level].Rewards; - var regularRewardV2 = V2.StakeRegularRewardSheet[v2Level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV2Step, - runeV2Step, - currencyV2Step, - currencyCrystalV2Step, - fixedRewardV2, - regularRewardV2); - } - - if (itemV3Step > 0) - { - var regularFixedReward = GetRegularFixedRewardInfos(states, level); - var regularReward = sheets.GetSheet()[level].Rewards; - states = ProcessReward( - context, - states, - ref avatarState, - itemSheet, - stakedAmount, - itemV3Step, - runeV3Step, - currencyV3Step, - currencyCrystalV3Step, - 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 static List GetRegularFixedRewardInfos( - IAccountState states, - int level) - { - return states.TryGetSheet(out var fixedRewardSheet) - ? fixedRewardSheet[level].Rewards - : new List(); - } - - private IAccount ProcessReward( - IActionContext context, - IAccount states, - ref AvatarState avatarState, - ItemSheet itemSheet, - FungibleAssetValue stakedFav, - int itemRewardStep, - int runeRewardStep, - int currencyRewardStep, - int currencyCrystalRewardStep, - List fixedReward, - List regularReward) - { - // Regular Reward - var random = context.GetRandom(); - foreach (var reward in regularReward) - { - var rateFav = FungibleAssetValue.Parse( - stakedFav.Currency, - reward.DecimalRate.ToString(CultureInfo.InvariantCulture)); - var rewardQuantityForSingleStep = stakedFav.DivRem(rateFav, out _); - if (rewardQuantityForSingleStep <= 0) - { - continue; - } - - switch (reward.Type) - { - case StakeRegularRewardSheet.StakeRewardType.Item: - { - if (itemRewardStep == 0) - { - continue; - } - - var itemRow = itemSheet[reward.ItemId]; - var item = itemRow is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(itemRow, random); - var majorUnit = (int)rewardQuantityForSingleStep * itemRewardStep; - if (majorUnit < 1) - { - continue; - } - - avatarState.inventory.AddItem(item, majorUnit); - break; - } - case StakeRegularRewardSheet.StakeRewardType.Rune: - { - if (runeRewardStep == 0) - { - continue; - } - - var majorUnit = rewardQuantityForSingleStep * runeRewardStep; - if (majorUnit < 1) - { - continue; - } - - var runeReward = RuneHelper.StakeRune * majorUnit; - 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 isCrystal = reward.CurrencyTicker == Currencies.Crystal.Ticker; - if (isCrystal - ? currencyCrystalRewardStep == 0 - : currencyRewardStep == 0) - { - continue; - } - - // NOTE: prepare reward currency. - Currency rewardCurrency; - // NOTE: this line covers the reward.CurrencyTicker is following cases: - // - Currencies.Crystal.Ticker - // - Currencies.Garage.Ticker - // - lower case is starting with "rune_" or "runestone_" - // - lower case is starting with "soulstone_" - try - { - rewardCurrency = - Currencies.GetMinterlessCurrency(reward.CurrencyTicker); - } - // NOTE: throw exception if reward.CurrencyTicker is null or empty. - catch (ArgumentNullException) - { - throw; - } - // NOTE: handle the case that reward.CurrencyTicker isn't covered by - // Currencies.GetMinterlessCurrency(). - catch (ArgumentException) - { - // NOTE: throw exception if reward.CurrencyDecimalPlaces is null. - if (reward.CurrencyDecimalPlaces is null) - { - throw new ArgumentException( - $"Decimal places of {reward.CurrencyTicker} is null"); - } - - // NOTE: new currency is created as uncapped currency. - rewardCurrency = Currency.Uncapped( - reward.CurrencyTicker, - Convert.ToByte(reward.CurrencyDecimalPlaces.Value), - minters: null); - } - - var majorUnit = isCrystal - ? rewardQuantityForSingleStep * currencyCrystalRewardStep - : rewardQuantityForSingleStep * currencyRewardStep; - var rewardFav = rewardCurrency * majorUnit; - states = states.MintAsset( - context, - context.Signer, - rewardFav); - break; - } - default: - throw new ArgumentException( - $"Can't handle reward type: {reward.Type}", - nameof(regularReward)); - } - } - - // Fixed Reward - foreach (var reward in fixedReward) - { - var itemRow = itemSheet[reward.ItemId]; - var item = itemRow is MaterialItemSheet.Row materialRow - ? ItemFactory.CreateTradableMaterial(materialRow) - : ItemFactory.CreateItem(itemRow, 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 8e0af591f1..0000000000 --- a/Lib9c/Action/CombinationConsumable0.cs +++ /dev/null @@ -1,222 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var itemId = random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - 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 a05ee7422d..0000000000 --- a/Lib9c/Action/CombinationConsumable2.cs +++ /dev/null @@ -1,222 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var itemId = random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - 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 9a86b85513..0000000000 --- a/Lib9c/Action/CombinationConsumable3.cs +++ /dev/null @@ -1,222 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var itemId = random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - 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 41eb8191ba..0000000000 --- a/Lib9c/Action/CombinationConsumable4.cs +++ /dev/null @@ -1,222 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var itemId = random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - 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 3ce1493bf5..0000000000 --- a/Lib9c/Action/CombinationConsumable6.cs +++ /dev/null @@ -1,227 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var itemId = random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - 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 faeaae310f..0000000000 --- a/Lib9c/Action/CombinationConsumable7.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; -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 IAccount 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); - - 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 random = ctx.GetRandom(); - var itemId = random.GenerateRandomGuid(); - var itemUsable = GetFood(consumableItemRow, itemId, requiredBlockIndex); - // 액션 결과 - result.itemUsable = itemUsable; - var mail = new CombinationMail( - result, - ctx.BlockIndex, - 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/CombinationConsumable8.cs b/Lib9c/Action/CombinationConsumable8.cs deleted file mode 100644 index 3260fc4228..0000000000 --- a/Lib9c/Action/CombinationConsumable8.cs +++ /dev/null @@ -1,246 +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.Security.Cryptography; -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 -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/637 - /// Updated at https://github.com/planetarium/lib9c/pull/861 - /// Updated at https://github.com/planetarium/lib9c/pull/957 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200092ObsoleteIndex)] - [ActionType("combination_consumable8")] - public class CombinationConsumable8 : GameAction, ICombinationConsumableV1 - { - public const string AvatarAddressKey = "a"; - public Address avatarAddress; - - public const string SlotIndexKey = "s"; - public int slotIndex; - - public const string RecipeIdKey = "r"; - public int recipeId; - - Address ICombinationConsumableV1.AvatarAddress => avatarAddress; - int ICombinationConsumableV1.RecipeId => recipeId; - int ICombinationConsumableV1.SlotIndex => slotIndex; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - [AvatarAddressKey] = avatarAddress.Serialize(), - [SlotIndexKey] = slotIndex.Serialize(), - [RecipeIdKey] = recipeId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - avatarAddress = plainValue[AvatarAddressKey].ToAddress(); - slotIndex = plainValue[SlotIndexKey].ToInteger(); - recipeId = plainValue[RecipeIdKey].ToInteger(); - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var random = context.GetRandom(); - 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); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Combination exec started", addressesHex); - - if (!states.TryGetAvatarStateV2(context.Signer, avatarAddress, 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.CombinationConsumableAction)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException( - addressesHex, - GameConfig.RequireClearedStageLevel.CombinationConsumableAction, - 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 endBlockIndex = context.BlockIndex; - var requiredFungibleItems = new Dictionary(); - - // Validate RecipeId - var consumableItemRecipeSheet = states.GetSheet(); - if (!consumableItemRecipeSheet.TryGetValue(recipeId, out var recipeRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(ConsumableItemRecipeSheet), - recipeId); - } - // ~Validate RecipeId - - // Validate Recipe ResultEquipmentId - var consumableItemSheet = states.GetSheet(); - if (!consumableItemSheet.TryGetValue(recipeRow.ResultConsumableItemId, out var consumableRow)) - { - throw new SheetRowNotFoundException( - addressesHex, - nameof(consumableItemSheet), - recipeRow.ResultConsumableItemId); - } - // ~Validate Recipe ResultEquipmentId - - // Validate Recipe Material - var materialItemSheet = states.GetSheet(); - for (var i = recipeRow.Materials.Count; i > 0; i--) - { - var materialInfo = recipeRow.Materials[i - 1]; - if (!materialItemSheet.TryGetValue(materialInfo.Id, out var 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 Recipe Material - - costActionPoint += recipeRow.RequiredActionPoint; - 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 var 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 - - // Create Consumable - var consumable = (Consumable) ItemFactory.CreateItemUsable( - consumableRow, - random.GenerateRandomGuid(), - endBlockIndex - ); - // ~Create Consumable - - // Add or Update Consumable - avatarState.blockIndex = context.BlockIndex; - avatarState.updatedAt = context.BlockIndex; - avatarState.UpdateFromCombination(consumable); - avatarState.UpdateQuestRewards(materialItemSheet); - // ~Add or Update Consumable - - // Update Slot - var mailId = random.GenerateRandomGuid(); - var attachmentResult = new CombinationConsumable5.ResultModel - { - id = mailId, - actionPoint = costActionPoint, - materials = requiredFungibleItems.ToDictionary( - e => ItemFactory.CreateMaterial(materialItemSheet, e.Key), - e => e.Value), - itemUsable = consumable, - recipeId = recipeId, - }; - 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}Combination 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()); - } - } -} diff --git a/Lib9c/Action/CombinationEquipment0.cs b/Lib9c/Action/CombinationEquipment0.cs deleted file mode 100644 index a02f40ca6b..0000000000 --- a/Lib9c/Action/CombinationEquipment0.cs +++ /dev/null @@ -1,337 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - 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, 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, 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 4793570328..0000000000 --- a/Lib9c/Action/CombinationEquipment10.cs +++ /dev/null @@ -1,380 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - 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 = 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 e8a7f0b0c1..0000000000 --- a/Lib9c/Action/CombinationEquipment11.cs +++ /dev/null @@ -1,395 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - 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 = 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 bb72fb2b85..0000000000 --- a/Lib9c/Action/CombinationEquipment12.cs +++ /dev/null @@ -1,468 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - 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 = 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 ea4eaeb514..0000000000 --- a/Lib9c/Action/CombinationEquipment13.cs +++ /dev/null @@ -1,653 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - - if (useHammerPoint) - { - if (!equipment.Skills.Any()) - { - AddSkillOption( - agentState, - equipment, - 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 = 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 IAccount UseAssetsBySuperCraft( - IAccount 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 IAccount UseAssetsByNormalCombination( - IAccount 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 51d248b6fa..0000000000 --- a/Lib9c/Action/CombinationEquipment14.cs +++ /dev/null @@ -1,663 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - - if (useHammerPoint) - { - if (!equipment.Skills.Any()) - { - AddSkillOption( - agentState, - equipment, - 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 = 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 IAccount UseAssetsBySuperCraft( - IAccount 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 IAccount UseAssetsByNormalCombination( - IAccount 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 cc626d5296..0000000000 --- a/Lib9c/Action/CombinationEquipment15.cs +++ /dev/null @@ -1,664 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment)ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex, - madeWithMimisbrunnrRecipe: recipeRow.IsMimisBrunnrSubRecipe(subRecipeId)); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - random, - subRecipeRow, - sheets.GetSheet(), - sheets.GetSheet() - ); - endBlockIndex = equipment.RequiredBlockIndex; - - if (useHammerPoint) - { - if (!equipment.Skills.Any()) - { - AddSkillOption( - agentState, - equipment, - 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 = 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 IAccount UseAssetsBySuperCraft( - IAccount 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 IAccount UseAssetsByNormalCombination( - IAccount 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 c8c343ab23..0000000000 --- a/Lib9c/Action/CombinationEquipment2.cs +++ /dev/null @@ -1,328 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - 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, 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, 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 f50da4718f..0000000000 --- a/Lib9c/Action/CombinationEquipment3.cs +++ /dev/null @@ -1,337 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - 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, 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, 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 de5a1f8ef1..0000000000 --- a/Lib9c/Action/CombinationEquipment4.cs +++ /dev/null @@ -1,337 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = AvatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - SlotIndex - ) - ); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - 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, 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, 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 f564c99f32..0000000000 --- a/Lib9c/Action/CombinationEquipment6.cs +++ /dev/null @@ -1,284 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - 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, 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, 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 98dd8b4c91..0000000000 --- a/Lib9c/Action/CombinationEquipment7.cs +++ /dev/null @@ -1,285 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipRow, - 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, 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, 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 226f11ac49..0000000000 --- a/Lib9c/Action/CombinationEquipment8.cs +++ /dev/null @@ -1,376 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - 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 = 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 54e2d682dc..0000000000 --- a/Lib9c/Action/CombinationEquipment9.cs +++ /dev/null @@ -1,378 +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 IAccount 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); - - 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 random = context.GetRandom(); - var equipment = (Equipment) ItemFactory.CreateItemUsable( - equipmentRow, - random.GenerateRandomGuid(), - endBlockIndex); - - if (!(subRecipeRow is null)) - { - AddAndUnlockOption( - agentState, - equipment, - 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 = 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/CreateAvatar10.cs b/Lib9c/Action/CreateAvatar10.cs deleted file mode 100644 index 92da416a38..0000000000 --- a/Lib9c/Action/CreateAvatar10.cs +++ /dev/null @@ -1,299 +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.Extensions; -using Nekoyume.Model.Item; -using Nekoyume.Model.Skill; -using Nekoyume.Model.Stat; -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/2166 - /// Updated at https://github.com/planetarium/lib9c/pull/2166 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200092ObsoleteIndex)] - [ActionType("create_avatar10")] - public class CreateAvatar10 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var random = ctx.GetRandom(); - 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); - - 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(ctx, 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, - 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(random, costRow, true); - } - - avatarState.inventory.AddItem(equipment); - } -#endif - var sheets = ctx.PreviousState.GetSheets(containItemSheet: true, - sheetTypes: new[] {typeof(CreateAvatarItemSheet), typeof(CreateAvatarFavSheet)}); - var itemSheet = sheets.GetItemSheet(); - var createAvatarItemSheet = sheets.GetSheet(); - AddItem(itemSheet, createAvatarItemSheet, avatarState, random); - var createAvatarFavSheet = sheets.GetSheet(); - states = MintAsset(createAvatarFavSheet, avatarState, states, context); - 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()); - } - - public static void AddItem(ItemSheet itemSheet, CreateAvatarItemSheet createAvatarItemSheet, - AvatarState avatarState, IRandom random) - { - foreach (var row in createAvatarItemSheet.Values) - { - var itemId = row.ItemId; - var count = row.Count; - var itemRow = itemSheet[itemId]; - if (itemRow is MaterialItemSheet.Row materialRow) - { - var item = ItemFactory.CreateMaterial(materialRow); - avatarState.inventory.AddItem(item, count); - } - else - { - for (int i = 0; i < count; i++) - { - var item = ItemFactory.CreateItem(itemRow, random); - avatarState.inventory.AddItem(item); - } - } - } - } - - public static IAccount MintAsset(CreateAvatarFavSheet favSheet, - AvatarState avatarState, IAccount states, IActionContext context) - { - foreach (var row in favSheet.Values) - { - var currency = row.Currency; - var targetAddress = row.Target switch - { - CreateAvatarFavSheet.Target.Agent => avatarState.agentAddress, - CreateAvatarFavSheet.Target.Avatar => avatarState.address, - _ => throw new ArgumentOutOfRangeException() - }; - states = states.MintAsset(context, targetAddress, currency * row.Quantity); - } - - return states; - } - } -} diff --git a/Lib9c/Action/CreateAvatar2.cs b/Lib9c/Action/CreateAvatar2.cs deleted file mode 100644 index 7e53d31c4c..0000000000 --- a/Lib9c/Action/CreateAvatar2.cs +++ /dev/null @@ -1,153 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var avatarAddress = ctx.Signer.Derive( - string.Format( - CultureInfo.InvariantCulture, - DeriveFormat, - index - ) - ); - - 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 8fe7191b0f..0000000000 --- a/Lib9c/Action/CreateAvatar3.cs +++ /dev/null @@ -1,155 +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 IAccount 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); - - 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 01520dd17e..0000000000 --- a/Lib9c/Action/CreateAvatar4.cs +++ /dev/null @@ -1,153 +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 IAccount 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); - - 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 a5773f4f66..0000000000 --- a/Lib9c/Action/CreateAvatar5.cs +++ /dev/null @@ -1,153 +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 IAccount 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); - - 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 78371b4432..0000000000 --- a/Lib9c/Action/CreateAvatar6.cs +++ /dev/null @@ -1,154 +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 IAccount 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); - - 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 502ec936e9..0000000000 --- a/Lib9c/Action/CreateAvatar7.cs +++ /dev/null @@ -1,153 +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 IAccount 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); - - 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 54c5f87237..0000000000 --- a/Lib9c/Action/CreateAvatar8.cs +++ /dev/null @@ -1,254 +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 IAccount 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); - - 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(); - var random = context.GetRandom(); - - 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, - 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(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/CreateAvatar9.cs b/Lib9c/Action/CreateAvatar9.cs deleted file mode 100644 index 9c8ebda7e9..0000000000 --- a/Lib9c/Action/CreateAvatar9.cs +++ /dev/null @@ -1,262 +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/1991 - /// Updated at https://github.com/planetarium/lib9c/pull/1991 - /// - [Serializable] - [ActionType("create_avatar9")] - public class CreateAvatar9 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - IRandom random = ctx.GetRandom(); - 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); - - var itemSheetAddress = Addresses.GetSheetAddress(); - var favSheetAddress = Addresses.GetSheetAddress(); - if (states.GetState(itemSheetAddress) is not null || states.GetState(favSheetAddress) is not null) - { - throw new ActionObsoletedException(nameof(CreateAvatar9)); - } - - 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(ctx, 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, - 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(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); - // TODO delete check blockIndex hard-fork this action - // Fix invalid mint crystal balance in internal network. main-net always mint 200_000 - var mintingValue = context.BlockIndex > 7_210_000L ? 200_000 : 600_000; - 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, mintingValue * CrystalCalculator.CRYSTAL); - } - } -} diff --git a/Lib9c/Action/DailyReward0.cs b/Lib9c/Action/DailyReward0.cs deleted file mode 100644 index 7c0bd82384..0000000000 --- a/Lib9c/Action/DailyReward0.cs +++ /dev/null @@ -1,63 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 dc11297689..0000000000 --- a/Lib9c/Action/DailyReward3.cs +++ /dev/null @@ -1,91 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var mail = new DailyRewardMail(result, - ctx.BlockIndex, - 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 606a88db92..0000000000 --- a/Lib9c/Action/DailyReward4.cs +++ /dev/null @@ -1,99 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var mail = new DailyRewardMail(result, - ctx.BlockIndex, - 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 b060069188..0000000000 --- a/Lib9c/Action/DailyReward5.cs +++ /dev/null @@ -1,72 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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 37cef1a0eb..0000000000 --- a/Lib9c/Action/DailyReward6.cs +++ /dev/null @@ -1,96 +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 IAccount 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); - - 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 1cc9401927..0000000000 --- a/Lib9c/Action/EventDungeonBattleV1.cs +++ /dev/null @@ -1,411 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV2( - random, - avatarState, - Foods, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards( - 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 6e459479f6..0000000000 --- a/Lib9c/Action/EventDungeonBattleV2.cs +++ /dev/null @@ -1,413 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV2( - random, - avatarState, - Foods, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards( - 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 df14159656..0000000000 --- a/Lib9c/Action/EventDungeonBattleV3.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.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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV3( - random, - avatarState, - Foods, - runeStates, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards( - 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 b852a2cbb9..0000000000 --- a/Lib9c/Action/EventDungeonBattleV4.cs +++ /dev/null @@ -1,428 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV3( - random, - avatarState, - Foods, - runeStates, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV3.GetWaveRewards( - 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/EventDungeonBattleV5.cs b/Lib9c/Action/EventDungeonBattleV5.cs deleted file mode 100644 index 58bb32109c..0000000000 --- a/Lib9c/Action/EventDungeonBattleV5.cs +++ /dev/null @@ -1,428 +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.V200092ObsoleteIndex)] - [ActionType(ActionTypeText)] - public class EventDungeonBattleV5 : GameAction, IEventDungeonBattleV2 - { - private const string ActionTypeText = "event_dungeon_battle5"; - 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var random = context.GetRandom(); - 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 StageSimulator( - random, - avatarState, - Foods, - runeStates, - new List(), - EventDungeonId, - EventDungeonStageId, - stageRow, - sheets.GetSheet()[EventDungeonStageId], - eventDungeonInfo.IsCleared(EventDungeonStageId), - exp, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulator.GetWaveRewards( - 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 d2b4810fcc..0000000000 --- a/Lib9c/Action/Factory/ClaimStakeRewardFactory.cs +++ /dev/null @@ -1,73 +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 > ClaimStakeReward8.ObsoleteBlockIndex) - { - return new ClaimStakeReward(avatarAddress); - } - - if (blockIndex > ClaimStakeReward7.ObsoleteBlockIndex) - { - return new ClaimStakeReward8(avatarAddress); - } - - if (blockIndex > ClaimStakeReward6.ObsoleteBlockIndex) - { - return new ClaimStakeReward7(avatarAddress); - } - - if (blockIndex > ClaimStakeReward5.ObsoleteBlockIndex) - { - return new ClaimStakeReward6(avatarAddress); - } - - if (blockIndex > ClaimStakeReward4.ObsoleteBlockIndex) - { - return new ClaimStakeReward5(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 ClaimStakeReward5(avatarAddress), - 6 => new ClaimStakeReward6(avatarAddress), - 7 => new ClaimStakeReward7(avatarAddress), - 8 => new ClaimStakeReward8(avatarAddress), - 9 => 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 2d3999cb6b..0000000000 --- a/Lib9c/Action/HackAndSlash0.cs +++ /dev/null @@ -1,296 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 e3af1088a5..0000000000 --- a/Lib9c/Action/HackAndSlash10.cs +++ /dev/null @@ -1,298 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 606c2e4496..0000000000 --- a/Lib9c/Action/HackAndSlash11.cs +++ /dev/null @@ -1,268 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 f34d2784b0..0000000000 --- a/Lib9c/Action/HackAndSlash12.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.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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 ef9f37c91c..0000000000 --- a/Lib9c/Action/HackAndSlash13.cs +++ /dev/null @@ -1,309 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 ba8561c01a..0000000000 --- a/Lib9c/Action/HackAndSlash14.cs +++ /dev/null @@ -1,378 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - 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, - 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 6863a58b13..0000000000 --- a/Lib9c/Action/HackAndSlash15.cs +++ /dev/null @@ -1,312 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var random = context.GetRandom(); - return Execute(context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute(IAccount 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 f5f07c092a..0000000000 --- a/Lib9c/Action/HackAndSlash16.cs +++ /dev/null @@ -1,351 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - - var random = context.GetRandom(); - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute( - IAccount 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 a8c6a0391a..0000000000 --- a/Lib9c/Action/HackAndSlash17.cs +++ /dev/null @@ -1,349 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var random = context.GetRandom(); - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute( - IAccount 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 ff7dc42b6a..0000000000 --- a/Lib9c/Action/HackAndSlash18.cs +++ /dev/null @@ -1,407 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - - CheckObsolete(ActionObsoleteConfig.V100340ObsoleteIndex, context); - - var random = context.GetRandom(); - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute( - IAccount 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(signer); - if (stakedAmount > goldCurrency * 0 && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - 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 e8f7c6944a..0000000000 --- a/Lib9c/Action/HackAndSlash19.cs +++ /dev/null @@ -1,433 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var random = context.GetRandom(); - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute( - IAccount 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(signer); - if (stakedAmount > goldCurrency * 0 && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - 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 7212d8cfc6..0000000000 --- a/Lib9c/Action/HackAndSlash2.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_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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 c159e321d5..0000000000 --- a/Lib9c/Action/HackAndSlash20.cs +++ /dev/null @@ -1,564 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - - var random = context.GetRandom(); - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute( - IAccount 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(signer); - if (stakedAmount > goldCurrency * 0 && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - 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/HackAndSlash21.cs b/Lib9c/Action/HackAndSlash21.cs deleted file mode 100644 index 53f142d4ec..0000000000 --- a/Lib9c/Action/HackAndSlash21.cs +++ /dev/null @@ -1,601 +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.V200092ObsoleteIndex)] - [ActionType("hack_and_slash21")] - public class HackAndSlash21 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var random = context.GetRandom(); - - return Execute( - context.PreviousState, - context.Signer, - context.BlockIndex, - random); - } - - public IAccount Execute( - IAccount 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; - const string source = "HackAndSlash"; - Log.Verbose("{AddressesHex} {Source} from #{BlockIndex} exec started", - addressesHex, source, blockIndex); - - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Get AvatarState", blockIndex, sw.Elapsed.TotalMilliseconds); - - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Get Sheets", blockIndex, sw.Elapsed.TotalMilliseconds); - - sw.Restart(); - var stakingLevel = 0; - StakeActionPointCoefficientSheet actionPointCoefficientSheet = null; - - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(signer); - if (stakedAmount > goldCurrency * 0 && - sheets.TryGetSheet(out actionPointCoefficientSheet)) - { - stakingLevel = actionPointCoefficientSheet.FindLevelByStakedAmount(signer, stakedAmount); - } - - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Check StateState", blockIndex, sw.Elapsed.TotalMilliseconds); - - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Get StageSheet", blockIndex, sw.Elapsed.TotalMilliseconds); - - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Validate World", blockIndex, sw.Elapsed.TotalMilliseconds); - - sw.Restart(); - var equipmentList = avatarState.ValidateEquipmentsV2(Equipments, blockIndex); - var foodIds = avatarState.ValidateConsumable(Foods, blockIndex); - var costumeIds = avatarState.ValidateCostume(Costumes); - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Validate Items", blockIndex, sw.Elapsed.TotalMilliseconds); - sw.Restart(); - 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} {Source} TotalPlayCount: {TotalPlayCount}, " + - "ApStoneCount: {ApStoneCount}, PlayCount by Ap stone: {ApStonePlayCount}, " + - "Ap cost per 1 play: {MinimumCostAp}, " + - "BlockIndex: {BlockIndex}, " + - "PlayCount by action point: {ApPlayCount}, Used AP: {UsedAp}", - addressesHex, - source, - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Unequip items", blockIndex, sw.Elapsed.TotalMilliseconds); - - sw.Restart(); - var questSheet = sheets.GetQuestSheet(); - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Get QuestSheet", blockIndex, sw.Elapsed.TotalMilliseconds); - - // 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Update QuestList", blockIndex, sw.Elapsed.TotalMilliseconds); - } - - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Get skillState", blockIndex, sw.Elapsed.TotalMilliseconds); - - 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)); - } - } - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Update slotState", blockIndex, sw.Elapsed.TotalMilliseconds); - - var stageWaveRow = sheets.GetSheet()[StageId]; - var enemySkillSheet = sheets.GetSheet(); - var costumeStatSheet = sheets.GetSheet(); - var stageCleared = !isNotClearedStage; - var starCount = 0; - for (var i = 0; i < TotalPlayCount; i++) - { - var rewards = StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet); - sw.Restart(); - // First simulating will use Foods and Random Skills. - // Remainder simulating will not use Foods. - var simulator = new StageSimulator( - random, - avatarState, - i == 0 ? Foods : new List(), - runeStates, - i == 0 ? skillsOnWaveStart : new List(), - WorldId, - StageId, - stageRow, - stageWaveRow, - stageCleared, - StageRewardExpHelper.GetExp(avatarState.level, StageId), - simulatorSheets, - enemySkillSheet, - costumeStatSheet, - rewards, - false); - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Initialize Simulator", blockIndex, sw.Elapsed.TotalMilliseconds); - - sw.Restart(); - simulator.Simulate(); - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Simulator.Simulate()", blockIndex, sw.Elapsed.TotalMilliseconds); - - sw.Restart(); - if (simulator.Log.IsClear) - { - if (!stageCleared) - { - avatarState.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - stageCleared = true; - } - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "ClearStage", blockIndex, sw.Elapsed.TotalMilliseconds); - } - - 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(); - } - - starCount += simulator.Log.clearedWaveNumber; - avatarState.Update(simulator); - - sw.Stop(); - Log.Verbose( - "{AddressesHex} {Source} {Process} by simulator({AvatarAddress}); " + - "blockIndex: {BlockIndex} " + - "worldId: {WorldId}, stageId: {StageId}, result: {Result}, " + - "clearWave: {ClearWave}, totalWave: {TotalWave}", - addressesHex, - source, - "Update avatar", - AvatarAddress, - WorldId, - StageId, - simulator.Log.result, - simulator.Log.clearedWaveNumber, - simulator.Log.waveCount - ); - } - sw.Stop(); - Log.Debug("{AddressesHex} {Source} {Process} from #{BlockIndex}: {Elapsed}, Count: {PlayCount}", - addressesHex, source, "loop Simulate", blockIndex, sw.Elapsed.TotalMilliseconds, TotalPlayCount); - - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(starCount, crystalStageBuffSheet); - sw.Restart(); - avatarState.UpdateQuestRewards(materialItemSheet); - avatarState.updatedAt = blockIndex; - avatarState.mailBox.CleanUp(); - sw.Stop(); - Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Update AvatarState", blockIndex, sw.Elapsed.TotalMilliseconds); - - 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} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", - addressesHex, source, "Set States", blockIndex, sw.Elapsed.TotalMilliseconds); - - var totalElapsed = DateTimeOffset.UtcNow - started; - Log.Verbose("{AddressesHex} {Source} HAS {Process}: {Elapsed}, blockIndex: {BlockIndex}", addressesHex, source, "Total Executed Time", totalElapsed.TotalMilliseconds, blockIndex); - return states; - } - - } -} diff --git a/Lib9c/Action/HackAndSlash4.cs b/Lib9c/Action/HackAndSlash4.cs deleted file mode 100644 index 35a1ab97ea..0000000000 --- a/Lib9c/Action/HackAndSlash4.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_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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 ed94cf05f7..0000000000 --- a/Lib9c/Action/HackAndSlash5.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.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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 e32933b2fd..0000000000 --- a/Lib9c/Action/HackAndSlash6.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.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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 bed7b64ed0..0000000000 --- a/Lib9c/Action/HackAndSlash7.cs +++ /dev/null @@ -1,314 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 7310211c6d..0000000000 --- a/Lib9c/Action/HackAndSlash8.cs +++ /dev/null @@ -1,288 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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 2801e507cb..0000000000 --- a/Lib9c/Action/HackAndSlash9.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; -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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - 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/HackAndSlashSweep.cs b/Lib9c/Action/HackAndSlashSweep.cs index a9834e1471..bdc5be485f 100644 --- a/Lib9c/Action/HackAndSlashSweep.cs +++ b/Lib9c/Action/HackAndSlashSweep.cs @@ -311,7 +311,7 @@ public override IAccount Execute(IActionContext context) avatarState.UpdateMonsterMap(stageWaveSheet, stageId); var random = context.GetRandom(); - var rewardItems = HackAndSlashSweep6.GetRewardItems( + var rewardItems = GetRewardItems( random, playCount, stageRow, @@ -340,5 +340,24 @@ public override IAccount Execute(IActionContext context) avatarAddress.Derive(LegacyQuestListKey), avatarState.questList.Serialize()); } + + 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/HackAndSlashSweep1.cs b/Lib9c/Action/HackAndSlashSweep1.cs deleted file mode 100644 index 7e1b94bcde..0000000000 --- a/Lib9c/Action/HackAndSlashSweep1.cs +++ /dev/null @@ -1,200 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - 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 random = context.GetRandom(); - var rewardItems = GetRewardItems(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 f8b1268d96..0000000000 --- a/Lib9c/Action/HackAndSlashSweep2.cs +++ /dev/null @@ -1,204 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - 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 random = context.GetRandom(); - var rewardItems = GetRewardItems(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 b6a50937c1..0000000000 --- a/Lib9c/Action/HackAndSlashSweep3.cs +++ /dev/null @@ -1,267 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - 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 random = context.GetRandom(); - var rewardItems = GetRewardItems(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 4607ef7f24..0000000000 --- a/Lib9c/Action/HackAndSlashSweep4.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_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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - 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 random = context.GetRandom(); - var rewardItems = GetRewardItems(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 9a5fc2a81a..0000000000 --- a/Lib9c/Action/HackAndSlashSweep5.cs +++ /dev/null @@ -1,255 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - 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 random = context.GetRandom(); - var rewardItems = GetRewardItems(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/HackAndSlashSweep6.cs b/Lib9c/Action/HackAndSlashSweep6.cs deleted file mode 100644 index ed76cbea25..0000000000 --- a/Lib9c/Action/HackAndSlashSweep6.cs +++ /dev/null @@ -1,267 +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 -{ - /// - /// Hard forked at https://github.com/planetarium/lib9c/pull/1338 - /// - [Serializable] - [ActionType("hack_and_slash_sweep6")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class HackAndSlashSweep6 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); - var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, 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), - 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(context.Signer); - if (stakedAmount > goldCurrency * 0) - { - 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 random = context.GetRandom(); - var rewardItems = GetRewardItems(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 f243f8a141..0000000000 --- a/Lib9c/Action/HackAndSlashSweep7.cs +++ /dev/null @@ -1,278 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(context.Signer); - if (stakedAmount > goldCurrency * 0) - { - 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 random = context.GetRandom(); - var rewardItems = HackAndSlashSweep6.GetRewardItems( - 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 db7a93bfa1..0000000000 --- a/Lib9c/Action/HackAndSlashSweep8.cs +++ /dev/null @@ -1,347 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(context.Signer); - if (stakedAmount > goldCurrency * 0) - { - 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 random = context.GetRandom(); - var rewardItems = HackAndSlashSweep6.GetRewardItems( - 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/HackAndSlashSweep9.cs b/Lib9c/Action/HackAndSlashSweep9.cs deleted file mode 100644 index 963b806db0..0000000000 --- a/Lib9c/Action/HackAndSlashSweep9.cs +++ /dev/null @@ -1,345 +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/1663 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200092ObsoleteIndex)] - [ActionType("hack_and_slash_sweep9")] - public class HackAndSlashSweep9 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var random = context.GetRandom(); - - 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.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)); - } - } - 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; - var goldCurrency = states.GetGoldCurrency(); - var stakedAmount = states.GetStakedAmount(context.Signer); - if (stakedAmount > goldCurrency * 0) - { - 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( - 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 42f91ff032..0000000000 --- a/Lib9c/Action/ItemEnhancement0.cs +++ /dev/null @@ -1,295 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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 4f51c54830..0000000000 --- a/Lib9c/Action/ItemEnhancement2.cs +++ /dev/null @@ -1,267 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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 5835b47fdb..0000000000 --- a/Lib9c/Action/ItemEnhancement3.cs +++ /dev/null @@ -1,267 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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 a348531873..0000000000 --- a/Lib9c/Action/ItemEnhancement4.cs +++ /dev/null @@ -1,265 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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 746bc49b33..0000000000 --- a/Lib9c/Action/ItemEnhancement5.cs +++ /dev/null @@ -1,265 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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 74c25a2240..0000000000 --- a/Lib9c/Action/ItemEnhancement6.cs +++ /dev/null @@ -1,290 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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 33c89d53aa..0000000000 --- a/Lib9c/Action/ItemEnhancement8.cs +++ /dev/null @@ -1,293 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var mail = new ItemEnhanceMail(result, ctx.BlockIndex, 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/JoinArena2.cs b/Lib9c/Action/JoinArena2.cs deleted file mode 100644 index f7eae1b27d..0000000000 --- a/Lib9c/Action/JoinArena2.cs +++ /dev/null @@ -1,223 +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.Arena; -using Nekoyume.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Arena; -using Nekoyume.Model.EnumType; -using Nekoyume.Model.Rune; -using Nekoyume.Model.State; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - /// - /// Introduced at https://github.com/planetarium/lib9c/pull/1495 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("join_arena2")] - public class JoinArena2 : GameAction, IJoinArenaV1 - { - public Address avatarAddress; - public int championshipId; - public int round; - public List costumes; - public List equipments; - public List runeInfos; - - Address IJoinArenaV1.AvatarAddress => avatarAddress; - int IJoinArenaV1.ChampionshipId => championshipId; - int IJoinArenaV1.Round => round; - IEnumerable IJoinArenaV1.Costumes => costumes; - IEnumerable IJoinArenaV1.Equipments => equipments; - IEnumerable IJoinArenaV1.RuneSlotInfos => runeInfos - .Select(x => x.Serialize()); - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary() - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["championshipId"] = championshipId.Serialize(), - ["round"] = round.Serialize(), - ["costumes"] = new List(costumes - .OrderBy(element => element).Select(e => e.Serialize())), - ["equipments"] = 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) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - championshipId = plainValue["championshipId"].ToInteger(); - round = plainValue["round"].ToInteger(); - 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)); - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}JoinArena exec started", addressesHex); - - if (!states.TryGetAgentAvatarStatesV2(context.Signer, avatarAddress, - out var agentState, out var avatarState, out _)) - { - throw new FailedLoadStateException( - $"[{nameof(JoinArena)}] Aborted as the avatar state of the signer 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( - sheetTypes: new[] - { - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(ArenaSheet), - typeof(RuneListSheet), - }); - - avatarState.ValidEquipmentAndCostume(costumes, equipments, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet(), - context.BlockIndex, addressesHex); - - // update rune slot - var runeSlotStateAddress = RuneSlotState.DeriveAddress(avatarAddress, 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(avatarAddress, 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 sheet = sheets.GetSheet(); - if (!sheet.TryGetValue(championshipId, out var row)) - { - throw new SheetRowNotFoundException( - nameof(ArenaSheet), $"championship Id : {championshipId}"); - } - - if (!row.TryGetRound(round, out var roundData)) - { - throw new RoundNotFoundException( - $"[{nameof(JoinArena)}] ChampionshipId({row.ChampionshipId}) - round({round})"); - } - - // check fee - - var fee = ArenaHelper.GetEntranceFee(roundData, context.BlockIndex, avatarState.level); - if (fee > 0 * CrystalCalculator.CRYSTAL) - { - var crystalBalance = states.GetBalance(context.Signer, CrystalCalculator.CRYSTAL); - if (fee > crystalBalance) - { - throw new NotEnoughFungibleAssetValueException( - $"required {fee}, but balance is {crystalBalance}"); - } - - var arenaAdr = ArenaHelper.DeriveArenaAddress(roundData.ChampionshipId, roundData.Round); - states = states.TransferAsset(context, context.Signer, arenaAdr, fee); - } - - // check medal - if (roundData.ArenaType.Equals(ArenaType.Championship)) - { - var medalCount = ArenaHelper.GetMedalTotalCount(row, avatarState); - if (medalCount < roundData.RequiredMedalCount) - { - throw new NotEnoughMedalException( - $"[{nameof(JoinArena)}] have({medalCount}) < Required Medal Count({roundData.RequiredMedalCount}) "); - } - } - - // create ArenaScore - var arenaScoreAdr = - ArenaScore.DeriveAddress(avatarAddress, roundData.ChampionshipId, roundData.Round); - if (states.TryGetState(arenaScoreAdr, out List _)) - { - throw new ArenaScoreAlreadyContainsException( - $"[{nameof(JoinArena)}] id({roundData.ChampionshipId}) / round({roundData.Round})"); - } - - var arenaScore = new ArenaScore(avatarAddress, roundData.ChampionshipId, roundData.Round); - - // create ArenaInformation - var arenaInformationAdr = - ArenaInformation.DeriveAddress(avatarAddress, roundData.ChampionshipId, roundData.Round); - if (states.TryGetState(arenaInformationAdr, out List _)) - { - throw new ArenaInformationAlreadyContainsException( - $"[{nameof(JoinArena)}] id({roundData.ChampionshipId}) / round({roundData.Round})"); - } - - var arenaInformation = - new ArenaInformation(avatarAddress, roundData.ChampionshipId, roundData.Round); - - // update ArenaParticipants - var arenaParticipantsAdr = ArenaParticipants.DeriveAddress(roundData.ChampionshipId, roundData.Round); - var arenaParticipants = states.GetArenaParticipants(arenaParticipantsAdr, roundData.ChampionshipId, roundData.Round); - arenaParticipants.Add(avatarAddress); - - // update ArenaAvatarState - var arenaAvatarStateAdr = ArenaAvatarState.DeriveAddress(avatarAddress); - var arenaAvatarState = states.GetArenaAvatarState(arenaAvatarStateAdr, avatarState); - arenaAvatarState.UpdateCostumes(costumes); - arenaAvatarState.UpdateEquipment(equipments); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}JoinArena Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetState(arenaScoreAdr, arenaScore.Serialize()) - .SetState(arenaInformationAdr, arenaInformation.Serialize()) - .SetState(arenaParticipantsAdr, arenaParticipants.Serialize()) - .SetState(arenaAvatarStateAdr, arenaAvatarState.Serialize()) - .SetState(context.Signer, agentState.Serialize()); - } - } -} diff --git a/Lib9c/Action/MimisbrunnrBattle0.cs b/Lib9c/Action/MimisbrunnrBattle0.cs deleted file mode 100644 index 49ecdfe3cc..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle0.cs +++ /dev/null @@ -1,330 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 a534172660..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle10.cs +++ /dev/null @@ -1,390 +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 IAccount 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); - - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV2( - random, - avatarState, - foods, - new List(), - worldId, - stageId, - stageRow, - sheets.GetSheet()[stageId], - avatarState.worldInformation.IsStageCleared(stageId), - 0, - simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulatorV2.GetWaveRewards(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 dcfede31df..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle11.cs +++ /dev/null @@ -1,444 +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 IAccount 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); - - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV3( - 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(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 1aa7d4f081..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle12.cs +++ /dev/null @@ -1,443 +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 IAccount 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); - - 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 random = context.GetRandom(); - var simulator = new StageSimulatorV3( - 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(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 1c5f70dd7a..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle2.cs +++ /dev/null @@ -1,330 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 495fbcf1b6..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle4.cs +++ /dev/null @@ -1,334 +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 IAccount 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); - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 030a7d058a..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle5.cs +++ /dev/null @@ -1,296 +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 IAccount 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); - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 9792d22ff3..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle6.cs +++ /dev/null @@ -1,309 +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 IAccount 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); - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 d5fbba984d..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle7.cs +++ /dev/null @@ -1,309 +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 IAccount 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); - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 aa9f3f16d1..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle8.cs +++ /dev/null @@ -1,285 +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 IAccount 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); - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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 3e6cc2a210..0000000000 --- a/Lib9c/Action/MimisbrunnrBattle9.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 -{ - /// - /// 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 IAccount 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); - - 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 random = context.GetRandom(); - var costumeStatSheet = states.GetSheet(); - var simulator = new StageSimulatorV1( - 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/Raid1.cs b/Lib9c/Action/Raid1.cs deleted file mode 100644 index 6bc5454a27..0000000000 --- a/Lib9c/Action/Raid1.cs +++ /dev/null @@ -1,285 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - - 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 random = context.GetRandom(); - var simulator = new RaidSimulatorV1( - row.BossId, - 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(), - 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 ca7d54e13c..0000000000 --- a/Lib9c/Action/Raid2.cs +++ /dev/null @@ -1,298 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - - 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 random = context.GetRandom(); - var simulator = new RaidSimulatorV1( - row.BossId, - 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(), - 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 5cd945daa0..0000000000 --- a/Lib9c/Action/Raid3.cs +++ /dev/null @@ -1,367 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - - 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 random = context.GetRandom(); - var simulator = new RaidSimulatorV2( - row.BossId, - 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(), - 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 beb72d1b19..0000000000 --- a/Lib9c/Action/Raid4.cs +++ /dev/null @@ -1,368 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - 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 random = context.GetRandom(); - var simulator = new RaidSimulatorV2( - row.BossId, - 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(), - 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 04b6f16c6f..0000000000 --- a/Lib9c/Action/Raid5.cs +++ /dev/null @@ -1,369 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - 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 random = context.GetRandom(); - var simulator = new RaidSimulatorV2( - row.BossId, - 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(), - 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/Raid6.cs b/Lib9c/Action/Raid6.cs deleted file mode 100644 index a98e1af7ab..0000000000 --- a/Lib9c/Action/Raid6.cs +++ /dev/null @@ -1,369 +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.V200092ObsoleteIndex)] - [ActionType("raid6")] - public class Raid6 : 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IAccount states = context.PreviousState; - var random = context.GetRandom(); - 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 RaidSimulator( - row.BossId, - 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(), - 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 2775bc6f9f..0000000000 --- a/Lib9c/Action/RankingBattle0.cs +++ /dev/null @@ -1,210 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 f8fcd863d7..0000000000 --- a/Lib9c/Action/RankingBattle10.cs +++ /dev/null @@ -1,334 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 06bca02454..0000000000 --- a/Lib9c/Action/RankingBattle2.cs +++ /dev/null @@ -1,250 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 d70f189e8c..0000000000 --- a/Lib9c/Action/RankingBattle3.cs +++ /dev/null @@ -1,253 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 39525b5566..0000000000 --- a/Lib9c/Action/RankingBattle4.cs +++ /dev/null @@ -1,262 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - // 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 9288cd2a29..0000000000 --- a/Lib9c/Action/RankingBattle5.cs +++ /dev/null @@ -1,279 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 e77fa29109..0000000000 --- a/Lib9c/Action/RankingBattle6.cs +++ /dev/null @@ -1,284 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 1cf7a51d75..0000000000 --- a/Lib9c/Action/RankingBattle7.cs +++ /dev/null @@ -1,284 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 b29e5df390..0000000000 --- a/Lib9c/Action/RankingBattle8.cs +++ /dev/null @@ -1,290 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 91c840b2ac..0000000000 --- a/Lib9c/Action/RankingBattle9.cs +++ /dev/null @@ -1,290 +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 IAccount 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); - - 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 random = ctx.GetRandom(); - var simulator = new RankingSimulatorV1( - 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 4c9b8f57a6..0000000000 --- a/Lib9c/Action/RapidCombination2.cs +++ /dev/null @@ -1,110 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 f851ab8036..0000000000 --- a/Lib9c/Action/RapidCombination3.cs +++ /dev/null @@ -1,110 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var slotAddress = avatarAddress.Derive( - string.Format( - CultureInfo.InvariantCulture, - CombinationSlotState.DeriveFormat, - slotIndex - ) - ); - - 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 f968fe7c6f..0000000000 --- a/Lib9c/Action/RapidCombination4.cs +++ /dev/null @@ -1,119 +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 IAccount 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); - - 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 b2edda19ca..0000000000 --- a/Lib9c/Action/RapidCombination6.cs +++ /dev/null @@ -1,181 +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 IAccount 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); - - 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 0d1e4bcc09..0000000000 --- a/Lib9c/Action/RapidCombination7.cs +++ /dev/null @@ -1,148 +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 IAccount 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); - - 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 f8a5b90441..0000000000 --- a/Lib9c/Action/RapidCombination8.cs +++ /dev/null @@ -1,153 +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 IAccount 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); - - 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/RapidCombination9.cs b/Lib9c/Action/RapidCombination9.cs deleted file mode 100644 index 07950d0f94..0000000000 --- a/Lib9c/Action/RapidCombination9.cs +++ /dev/null @@ -1,185 +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.Helper; -using Nekoyume.Model.Item; -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/1711 - /// - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200092ObsoleteIndex)] - [ActionType("rapid_combination9")] - public class RapidCombination9 : GameAction, IRapidCombinationV1 - { - public Address avatarAddress; - public int slotIndex; - - Address IRapidCombinationV1.AvatarAddress => avatarAddress; - int IRapidCombinationV1.SlotIndex => slotIndex; - - public override IAccount 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); - - 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}"); - } - - int costHourglassCount = 0; - - PetState petState = null; - if (slotState.PetId.HasValue) - { - var petStateAddress = PetState.DeriveAddress(avatarAddress, slotState.PetId.Value); - if (!states.TryGetState(petStateAddress, out List rawState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the {nameof(PetState)} was failed to load."); - } - - petState = new PetState(rawState); - var petOptionSheet = states.GetSheet(); - costHourglassCount = PetHelper.CalculateDiscountedHourglass( - diff, - gameConfigState.HourglassPerBlock, - petState, - petOptionSheet); - } - else - { - costHourglassCount = 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, costHourglassCount)) - { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the player has no enough material ({row.Id} * {costHourglassCount})"); - } - - 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, costHourglassCount); - avatarState.UpdateFromRapidCombinationV2( - (RapidCombination5.ResultModel)slotState.Result, - context.BlockIndex); - - // Update Pet - if (!(petState is null)) - { - petState.Update(context.BlockIndex); - var petStateAddress = PetState.DeriveAddress(avatarAddress, petState.PetId); - states = states.SetState(petStateAddress, petState.Serialize()); - } - - 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/Sell0.cs b/Lib9c/Action/Sell0.cs deleted file mode 100644 index 9b39a0c8ec..0000000000 --- a/Lib9c/Action/Sell0.cs +++ /dev/null @@ -1,151 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = context.GetRandom(); - var productId = random.GenerateRandomGuid(); - - var shopItem = new ShopItem( - ctx.Signer, - sellerAvatarAddress, - productId, - price, - (ITradableItem)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 6302f25cbf..0000000000 --- a/Lib9c/Action/Sell10.cs +++ /dev/null @@ -1,179 +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 IAccount 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); - - 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 982abaa0d2..0000000000 --- a/Lib9c/Action/Sell11.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.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 IAccount 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); - - 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 39c042bb4a..0000000000 --- a/Lib9c/Action/Sell2.cs +++ /dev/null @@ -1,164 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = context.GetRandom(); - var productId = 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, - (ITradableItem)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 bd5344c30a..0000000000 --- a/Lib9c/Action/Sell3.cs +++ /dev/null @@ -1,168 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = context.GetRandom(); - var productId = 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, (ITradableItem)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 bac95c98a8..0000000000 --- a/Lib9c/Action/Sell4.cs +++ /dev/null @@ -1,213 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = context.GetRandom(); - var productId = 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, (ITradableItem)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, 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 d6825f5594..0000000000 --- a/Lib9c/Action/Sell5.cs +++ /dev/null @@ -1,315 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - 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 random = context.GetRandom(); - var productId = 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, - 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 f438a90489..0000000000 --- a/Lib9c/Action/Sell7.cs +++ /dev/null @@ -1,183 +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 IAccount 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); - - 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 eb45c4b79c..0000000000 --- a/Lib9c/Action/Sell8.cs +++ /dev/null @@ -1,175 +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 IAccount 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); - - 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 69c0cc4ad6..0000000000 --- a/Lib9c/Action/Sell9.cs +++ /dev/null @@ -1,174 +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 IAccount 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); - - 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 7414dd1a54..0000000000 --- a/Lib9c/Action/SellCancellation0.cs +++ /dev/null @@ -1,139 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var mail = new SellCancelMail(result, ctx.BlockIndex, 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 ea0782bf68..0000000000 --- a/Lib9c/Action/SellCancellation2.cs +++ /dev/null @@ -1,139 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var mail = new SellCancelMail(result, ctx.BlockIndex, 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 f15ca9adda..0000000000 --- a/Lib9c/Action/SellCancellation3.cs +++ /dev/null @@ -1,151 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var mail = new SellCancelMail(result, ctx.BlockIndex, 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 9dbb415d27..0000000000 --- a/Lib9c/Action/SellCancellation4.cs +++ /dev/null @@ -1,151 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - 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 random = ctx.GetRandom(); - var mail = new SellCancelMail(result, ctx.BlockIndex, 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 80928c2188..0000000000 --- a/Lib9c/Action/SellCancellation5.cs +++ /dev/null @@ -1,193 +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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - Address shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - - 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 random = ctx.GetRandom(); - var mail = new SellCancelMail(result, ctx.BlockIndex, 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 c9593a1399..0000000000 --- a/Lib9c/Action/SellCancellation6.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 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 IAccount Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var shardedShopAddress = ShardedShopState.DeriveAddress(itemSubType, productId); - - 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 = (ITradableItem)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 random = context.GetRandom(); - var mail = new SellCancelMail(result, context.BlockIndex, 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/TransferAsset2.cs b/Lib9c/Action/TransferAsset2.cs deleted file mode 100644 index 8ccdf99b7e..0000000000 --- a/Lib9c/Action/TransferAsset2.cs +++ /dev/null @@ -1,162 +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 IAccount Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - - 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/TransferAsset4.cs b/Lib9c/Action/TransferAsset4.cs deleted file mode 100644 index 0f8bc34a4f..0000000000 --- a/Lib9c/Action/TransferAsset4.cs +++ /dev/null @@ -1,150 +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; -using Lib9c.Abstractions; -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/2143 - /// - [Serializable] - [ActionType(TypeIdentifier)] - [ActionObsolete(ObsoleteBlockIndex)] - public class TransferAsset4 : ActionBase, ISerializable, ITransferAsset, ITransferAssetV1 - { - private const int MemoMaxLength = 80; - public const string TypeIdentifier = "transfer_asset4"; - public const long ObsoleteBlockIndex = ActionObsoleteConfig.V200080ObsoleteIndex; - - public TransferAsset4() - { - } - - public TransferAsset4(Address sender, Address recipient, FungibleAssetValue amount, string memo = null) - { - Sender = sender; - Recipient = recipient; - Amount = amount; - - CheckMemoLength(memo); - Memo = memo; - } - - protected TransferAsset4(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", TypeIdentifier) - .Add("values", new Dictionary(pairs)); - } - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(4); - Address signer = context.Signer; - var state = context.PreviousState; - - var addressesHex = GetSignerAndOtherAddressesHex(context, signer); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}TransferAsset4 exec started", addressesHex); - if (Sender != signer) - { - throw new InvalidTransferSignerException(signer, Sender, Recipient); - } - - if (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 - ); - } - - TransferAsset3.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); - } - - 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/TransferAssets0.cs b/Lib9c/Action/TransferAssets0.cs deleted file mode 100644 index 641c1070d5..0000000000 --- a/Lib9c/Action/TransferAssets0.cs +++ /dev/null @@ -1,186 +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 IAccount Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - - 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 IAccount Transfer( - IActionContext context, IAccount 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/TransferAssets2.cs b/Lib9c/Action/TransferAssets2.cs deleted file mode 100644 index b86c6a137c..0000000000 --- a/Lib9c/Action/TransferAssets2.cs +++ /dev/null @@ -1,164 +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(TypeIdentifier)] - [ActionObsolete(ActionObsoleteConfig.V200090ObsoleteIndex)] - public class TransferAssets2 : ActionBase, ISerializable, ITransferAssets, ITransferAssetsV1 - { - public const string TypeIdentifier = "transfer_assets2"; - public const int RecipientsCapacity = 100; - private const int MemoMaxLength = 80; - - public TransferAssets2() - { - } - - public TransferAssets2(Address sender, List<(Address, FungibleAssetValue)> recipients, string memo = null) - { - Sender = sender; - Recipients = recipients; - - CheckMemoLength(memo); - Memo = memo; - } - - protected TransferAssets2(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", TypeIdentifier) - .Add("values", new Dictionary(pairs)); - } - } - - public override IAccount Execute(IActionContext context) - { - context.UseGas(4); - var state = context.PreviousState; - CheckObsolete(ActionObsoleteConfig.V200090ObsoleteIndex, 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}{ActionName} exec started", addressesHex, TypeIdentifier); - - state = Recipients.Aggregate(state, (current, t) => Transfer(context, current, context.Signer, t.recipient, t.amount, context.BlockIndex)); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}{ActionName} Total Executed Time: {Elapsed}", addressesHex, TypeIdentifier, 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 IAccount Transfer( - IActionContext context, IAccount state, Address signer, Address recipient, FungibleAssetValue amount, long blockIndex) - { - if (Sender != signer) - { - throw new InvalidTransferSignerException(signer, Sender, recipient); - } - - if (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 - ); - } - - TransferAsset3.CheckCrystalSender(currency, blockIndex, Sender); - return state.TransferAsset(context, Sender, recipient, amount); - } - } -} diff --git a/Lib9c/Action/UnlockRuneSlot.cs b/Lib9c/Action/UnlockRuneSlot.cs index 1350193de3..ac619ea924 100644 --- a/Lib9c/Action/UnlockRuneSlot.cs +++ b/Lib9c/Action/UnlockRuneSlot.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using Bencodex.Types; +using Lib9c; using Lib9c.Abstractions; using Libplanet.Action; using Libplanet.Action.State; @@ -74,28 +75,37 @@ public override IAccount Execute(IActionContext context) $"[{nameof(UnlockRuneSlot)}] Index : {SlotIndex}"); } - // note : You will need to modify it later when applying staking unlock. - if (slot.RuneSlotType != RuneSlotType.Ncg) - { - throw new MismatchRuneSlotTypeException( - $"[{nameof(UnlockRuneSlot)}] RuneSlotType : {slot.RuneSlotType}"); - } - var gameConfigState = states.GetGameConfigState(); - var cost = slot.RuneType == RuneType.Stat - ? gameConfigState.RuneStatSlotUnlockCost - : gameConfigState.RuneSkillSlotUnlockCost; - var ncgCurrency = states.GetGoldCurrency(); var arenaSheet = sheets.GetSheet(); var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); + int cost; + Currency currency; + switch (slot.RuneSlotType) + { + case RuneSlotType.Ncg: + cost = slot.RuneType == RuneType.Stat + ? gameConfigState.RuneStatSlotUnlockCost + : gameConfigState.RuneSkillSlotUnlockCost; + currency = states.GetGoldCurrency(); + break; + case RuneSlotType.Crystal: + cost = slot.RuneType == RuneType.Stat + ? gameConfigState.RuneStatSlotCrystalUnlockCost + : gameConfigState.RuneSkillSlotCrystalUnlockCost; + currency = Currencies.Crystal; + break; + default: + throw new MismatchRuneSlotTypeException( + $"[{nameof(UnlockRuneSlot)}] RuneSlotType : {slot.RuneSlotType}"); + } adventureSlotState.Unlock(SlotIndex); arenaSlotState.Unlock(SlotIndex); raidSlotState.Unlock(SlotIndex); return states - .TransferAsset(context, context.Signer, feeStoreAddress, cost * ncgCurrency) + .TransferAsset(context, context.Signer, feeStoreAddress, cost * currency) .SetState(adventureSlotStateAddress, adventureSlotState.Serialize()) .SetState(arenaSlotStateAddress, arenaSlotState.Serialize()) .SetState(raidSlotStateAddress, raidSlotState.Serialize()); diff --git a/Lib9c/Action/UpdateSell0.cs b/Lib9c/Action/UpdateSell0.cs deleted file mode 100644 index 74ecaa4fa6..0000000000 --- a/Lib9c/Action/UpdateSell0.cs +++ /dev/null @@ -1,213 +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 IAccount 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); - - 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 49e45acde4..0000000000 --- a/Lib9c/Action/UpdateSell2.cs +++ /dev/null @@ -1,212 +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 IAccount 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); - - 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 036caa0c55..0000000000 --- a/Lib9c/Action/UpdateSell3.cs +++ /dev/null @@ -1,211 +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 IAccount 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); - - 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 1b6ccb1014..0000000000 --- a/Lib9c/Action/UpdateSell4.cs +++ /dev/null @@ -1,216 +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 IAccount 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); - - 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/EnumType/RuneSlotType.cs b/Lib9c/Model/EnumType/RuneSlotType.cs index 5a829c632e..ddb2804235 100644 --- a/Lib9c/Model/EnumType/RuneSlotType.cs +++ b/Lib9c/Model/EnumType/RuneSlotType.cs @@ -5,5 +5,6 @@ public enum RuneSlotType Default = 1, Ncg = 2, Stake = 3, + Crystal = 4, } } diff --git a/Lib9c/Model/State/GameConfigState.cs b/Lib9c/Model/State/GameConfigState.cs index c63f0e6151..a30d9deb69 100644 --- a/Lib9c/Model/State/GameConfigState.cs +++ b/Lib9c/Model/State/GameConfigState.cs @@ -22,6 +22,8 @@ public class GameConfigState : State public int BattleArenaInterval { get; private set; } public int RuneStatSlotUnlockCost { get; private set; } public int RuneSkillSlotUnlockCost { get; private set; } + public int RuneStatSlotCrystalUnlockCost { get; private set; } + public int RuneSkillSlotCrystalUnlockCost { get; private set; } public int DailyRuneRewardAmount { get; private set; } public int DailyWorldBossInterval { get; private set; } public int WorldBossRequiredInterval { get; private set; } @@ -233,7 +235,14 @@ public GameConfigState(Dictionary serialized) : base(serialized) { RequireCharacterLevel_ConsumableSlot5 = characterConsumableSlot5.ToInteger(); } - + if (serialized.TryGetValue((Text)"rune_stat_slot_crystal_unlock_cost", out var rscc)) + { + RuneStatSlotCrystalUnlockCost = (Integer)rscc; + } + if (serialized.TryGetValue((Text)"rune_skill_slot_crystal_unlock_cost", out var rscc2)) + { + RuneSkillSlotCrystalUnlockCost = (Integer)rscc2; + } } public GameConfigState(string csv) : base(Address) @@ -448,6 +457,20 @@ public override IValue Serialize() RequireCharacterLevel_ConsumableSlot5.Serialize()); } + if (RuneSkillSlotCrystalUnlockCost > 0) + { + values.Add( + (Text)"rune_skill_slot_crystal_unlock_cost", + (Integer)RuneSkillSlotCrystalUnlockCost); + } + + if (RuneStatSlotCrystalUnlockCost > 0) + { + values.Add( + (Text)"rune_stat_slot_crystal_unlock_cost", + (Integer)RuneStatSlotCrystalUnlockCost); + } + #pragma warning disable LAA1002 return new Dictionary(values.Union((Dictionary) base.Serialize())); #pragma warning restore LAA1002 @@ -497,6 +520,12 @@ public void Update(GameConfigSheet.Row row) case "rune_skill_slot_unlock_cost": RuneSkillSlotUnlockCost = TableExtensions.ParseInt(row.Value); break; + case "rune_stat_slot_crystal_unlock_cost": + RuneStatSlotCrystalUnlockCost = TableExtensions.ParseInt(row.Value); + break; + case "rune_skill_slot_crystal_unlock_cost": + RuneSkillSlotCrystalUnlockCost = TableExtensions.ParseInt(row.Value); + break; case "daily_rune_reward_amount": DailyRuneRewardAmount = TableExtensions.ParseInt(row.Value); break; @@ -601,7 +630,6 @@ public void Update(GameConfigSheet.Row row) RequireCharacterLevel_ConsumableSlot5 = TableExtensions.ParseInt(row.Value); break; - } } } diff --git a/Lib9c/Model/State/RuneSlotState.cs b/Lib9c/Model/State/RuneSlotState.cs index f6a0027adf..19dc61bfaf 100644 --- a/Lib9c/Model/State/RuneSlotState.cs +++ b/Lib9c/Model/State/RuneSlotState.cs @@ -28,12 +28,19 @@ public RuneSlotState(BattleType battleType) _slots.Add(new RuneSlot(3, RuneSlotType.Default, RuneType.Skill, false)); _slots.Add(new RuneSlot(4, RuneSlotType.Ncg, RuneType.Skill, true)); _slots.Add(new RuneSlot(5, RuneSlotType.Stake, RuneType.Skill,true)); + _slots.Add(new RuneSlot(6, RuneSlotType.Crystal, RuneType.Stat, true)); + _slots.Add(new RuneSlot(7, RuneSlotType.Crystal, RuneType.Skill,true)); } public RuneSlotState(List serialized) { BattleType = serialized[0].ToEnum(); _slots = ((List)serialized[1]).Select(x => new RuneSlot((List)x)).ToList(); + if (_slots.Count == 6) + { + _slots.Add(new RuneSlot(6, RuneSlotType.Crystal, RuneType.Stat, true)); + _slots.Add(new RuneSlot(7, RuneSlotType.Crystal, RuneType.Skill,true)); + } } public IValue Serialize() diff --git a/Lib9c/Model/State/StakeState.cs b/Lib9c/Model/State/StakeState.cs index 288f9a96f7..59a6d88324 100644 --- a/Lib9c/Model/State/StakeState.cs +++ b/Lib9c/Model/State/StakeState.cs @@ -60,7 +60,7 @@ public void Achieve(int level, int step) // Because we need to make sure that the reward sheet V3 is applied // after the `ClaimStakeReward5` action is deprecated. // And we expect the index will be 7_650_000L. - public const long StakeRewardSheetV3Index = ClaimStakeReward5.ObsoleteBlockIndex + 1; + public const long StakeRewardSheetV3Index = ActionObsoleteConfig.V200060ObsoleteIndex + 1; public long CancellableBlockIndex { get; private set; } public long StartedBlockIndex { get; private set; } diff --git a/Lib9c/TableCSV/GameConfigSheet.csv b/Lib9c/TableCSV/GameConfigSheet.csv index 374d3f0322..29cd53f513 100644 --- a/Lib9c/TableCSV/GameConfigSheet.csv +++ b/Lib9c/TableCSV/GameConfigSheet.csv @@ -8,6 +8,8 @@ required_appraise_block,0 battle_arena_interval,4 rune_stat_slot_unlock_cost,100 rune_skill_slot_unlock_cost,1000 +rune_stat_slot_crystal_unlock_cost,100 +rune_skill_slot_crystal_unlock_cost,1000 daily_rune_reward_amount,1 daily_worldboss_interval,10368 worldboss_required_interval,5 @@ -33,4 +35,4 @@ character_consumable_slot_1,1 character_consumable_slot_2,35 character_consumable_slot_3,100 character_consumable_slot_4,200 -character_consumable_slot_5,350 \ No newline at end of file +character_consumable_slot_5,350