diff --git a/.Lib9c.DevExtensions.Tests/Action/CreateOrReplaceAvatarTest.cs b/.Lib9c.DevExtensions.Tests/Action/CreateOrReplaceAvatarTest.cs index 702f89f8e8..948b3e6400 100644 --- a/.Lib9c.DevExtensions.Tests/Action/CreateOrReplaceAvatarTest.cs +++ b/.Lib9c.DevExtensions.Tests/Action/CreateOrReplaceAvatarTest.cs @@ -9,6 +9,7 @@ using Lib9c.Tests.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -30,7 +31,7 @@ public class CreateOrReplaceAvatarTest public CreateOrReplaceAvatarTest() { - _initialStates = new World(new MockWorldState()); + _initialStates = new World(MockUtil.MockModernWorldState); #pragma warning disable CS0618 var ncgCurrency = Currency.Legacy("NCG", 2, null); diff --git a/.Lib9c.DevExtensions.Tests/Action/FaucetCurrencyTest.cs b/.Lib9c.DevExtensions.Tests/Action/FaucetCurrencyTest.cs index 2205f5c2e9..7b4fd7f1f4 100644 --- a/.Lib9c.DevExtensions.Tests/Action/FaucetCurrencyTest.cs +++ b/.Lib9c.DevExtensions.Tests/Action/FaucetCurrencyTest.cs @@ -2,6 +2,7 @@ using Lib9c.Tests.Action; using Libplanet.Action.State; using Libplanet.Crypto; +using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume.Model.State; using Nekoyume.Module; @@ -30,9 +31,8 @@ public FaucetCurrencyTest(ITestOutputHelper outputHelper) _crystal = Currency.Legacy("CRYSTAL", 18, null); #pragma warning restore CS0618 - _initialState = new World( - new MockWorldState() - .AddBalance(GoldCurrencyState.Address, _ncg * int.MaxValue)); + _initialState = new World(MockWorldState.CreateModern() + .SetBalance(GoldCurrencyState.Address, _ncg * int.MaxValue)); var goldCurrencyState = new GoldCurrencyState(_ncg); _agentAddress = new PrivateKey().Address; diff --git a/.Lib9c.DevExtensions.Tests/Action/FaucetRuneTest.cs b/.Lib9c.DevExtensions.Tests/Action/FaucetRuneTest.cs index ae007716b1..293c5e5bb7 100644 --- a/.Lib9c.DevExtensions.Tests/Action/FaucetRuneTest.cs +++ b/.Lib9c.DevExtensions.Tests/Action/FaucetRuneTest.cs @@ -17,6 +17,7 @@ using Xunit; using Xunit.Abstractions; using static Lib9c.SerializeKeys; +using Libplanet.Mocks; namespace Lib9c.DevExtensions.Tests.Action { @@ -33,7 +34,7 @@ public FaucetRuneTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/AccountExtensions.cs b/.Lib9c.Tests/AccountExtensions.cs deleted file mode 100644 index b1d821da39..0000000000 --- a/.Lib9c.Tests/AccountExtensions.cs +++ /dev/null @@ -1,67 +0,0 @@ -namespace Lib9c.Tests -{ - using Lib9c.Tests.Action; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Libplanet.Store.Trie; - - public static class AccountExtensions - { - private static readonly byte[] _conversionTable = - { - 48, // '0' - 49, // '1' - 50, // '2' - 51, // '3' - 52, // '4' - 53, // '5' - 54, // '6' - 55, // '7' - 56, // '8' - 57, // '9' - 97, // 'a' - 98, // 'b' - 99, // 'c' - 100, // 'd' - 101, // 'e' - 102, // 'f' - }; - - /// - /// A rather shrewed method of removing value from an account. - /// This can be very slow depending on the size of the state. - /// For test backward compatibility only. Should not be used in production. - /// - /// The account to remove the value from. - /// The address of the value to remove. - /// - /// A new instance with the value removed. - /// - public static IAccount SetNull(this IAccount account, Address address) - { - var trie = new MockAccountState().Trie; - var path = ToStateKey(address); - foreach (var kv in account.Trie.IterateValues()) - { - trie = kv.Path.Equals(path) - ? trie - : trie.Set(kv.Path, kv.Value); - } - - return new Account(new AccountState(trie)); - } - - private static KeyBytes ToStateKey(Address address) - { - var addressBytes = address.ByteArray; - byte[] buffer = new byte[addressBytes.Length * 2]; - for (int i = 0; i < addressBytes.Length; i++) - { - buffer[i * 2] = _conversionTable[addressBytes[i] >> 4]; - buffer[i * 2 + 1] = _conversionTable[addressBytes[i] & 0xf]; - } - - return new KeyBytes(buffer); - } - } -} diff --git a/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs b/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs index 569fc4f694..526186afe0 100644 --- a/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs +++ b/.Lib9c.Tests/Action/AccountStateDeltaExtensionsTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Helper; using Nekoyume.Model.Coupons; @@ -46,7 +47,7 @@ public AccountStateDeltaExtensionsTest() public void SetWorldBossKillReward(int level, int expectedRune, int expectedCrystal, Type exc) { var context = new ActionContext(); - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); var rewardInfoAddress = new PrivateKey().Address; var rewardRecord = new WorldBossKillRewardRecord(); for (int i = 0; i < level; i++) @@ -106,7 +107,7 @@ public void SetWorldBossKillReward(int level, int expectedRune, int expectedCrys [Fact] public void SetCouponWallet() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); var guid1 = new Guid("6856AE42-A820-4041-92B0-5D7BAA52F2AA"); var guid2 = new Guid("701BA698-CCB9-4FC7-B88F-7CB8C707D135"); var guid3 = new Guid("910296E7-34E4-45D7-9B4E-778ED61F278B"); @@ -162,7 +163,7 @@ public void Mead(int agentBalance) var mead = Currencies.Mead; var price = RequestPledge.DefaultRefillMead * mead; ActionContext context = new ActionContext(); - IWorld states = new World(new MockWorldState()) + IWorld states = new World(MockUtil.MockModernWorldState) .SetLegacyState( agentContractAddress, List.Empty.Add(patron.Serialize()).Add(true.Serialize())) diff --git a/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs b/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs index 993eef78ad..748db0cfd5 100644 --- a/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs +++ b/.Lib9c.Tests/Action/AccountStateViewExtensionsTest.cs @@ -9,6 +9,7 @@ namespace Lib9c.Tests.Action using Lib9c.Tests.Extensions; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -49,7 +50,7 @@ public AccountStateViewExtensionsTest() [Fact] public void TryGetAvatarStateEmptyAddress() { - var states = new World(new MockWorldState()); + var states = new World(MockUtil.MockModernWorldState); Assert.False(states.TryGetAvatarState(default, default, out _)); } @@ -57,7 +58,7 @@ public void TryGetAvatarStateEmptyAddress() [Fact] public void TryGetAvatarStateAddressKeyNotFoundException() { - var states = new World(new MockWorldState()).SetLegacyState(default, Dictionary.Empty); + var states = new World(MockUtil.MockModernWorldState).SetLegacyState(default, Dictionary.Empty); Assert.False(states.TryGetAvatarState(default, default, out _)); } @@ -65,7 +66,7 @@ public void TryGetAvatarStateAddressKeyNotFoundException() [Fact] public void TryGetAvatarStateKeyNotFoundException() { - var states = new World(new MockWorldState()) + var states = new World(MockUtil.MockModernWorldState) .SetLegacyState( default, Dictionary.Empty @@ -78,7 +79,7 @@ public void TryGetAvatarStateKeyNotFoundException() [Fact] public void TryGetAvatarStateInvalidCastException() { - var states = new World(new MockWorldState()).SetLegacyState(default, default(Text)); + var states = new World(MockUtil.MockModernWorldState).SetLegacyState(default, default(Text)); Assert.False(states.TryGetAvatarState(default, default, out _)); } @@ -86,7 +87,7 @@ public void TryGetAvatarStateInvalidCastException() [Fact] public void TryGetAvatarStateInvalidAddress() { - var states = new World(new MockWorldState()).SetAvatarState(default, _avatarState); + var states = new World(MockUtil.MockModernWorldState).SetAvatarState(default, _avatarState); Assert.False(states.TryGetAvatarState(Addresses.GameConfig, _avatarAddress, out _)); } @@ -94,7 +95,7 @@ public void TryGetAvatarStateInvalidAddress() [Fact] public void GetAvatarStateV2() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); states = states .SetAvatarState(_avatarAddress, _avatarState); @@ -118,12 +119,12 @@ public void GetAvatarStateV2_Throw_FailedLoadStateException(string account) _ => throw new ArgumentException(), }; - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); states = states .SetAvatarState(_avatarAddress, _avatarState); states = states.SetAccount( accountAddress, - states.GetAccount(accountAddress).SetNull(_avatarAddress)); + states.GetAccount(accountAddress).RemoveState(_avatarAddress)); var exc = Assert.Throws(() => states.GetAvatarState(_avatarAddress)); Assert.Contains(account, exc.Message); } @@ -133,7 +134,7 @@ public void GetAvatarStateV2_Throw_FailedLoadStateException(string account) [InlineData(false)] public void TryGetAvatarState(bool backward) { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); if (backward) { states = states @@ -151,7 +152,7 @@ public void TryGetAvatarState(bool backward) [Fact] public void GetStatesAsDict() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); var dict = new Dictionary { { new PrivateKey().Address, Null.Value }, @@ -178,7 +179,7 @@ public void GetStatesAsDict() [Fact] public void GetSheets() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); SheetsExtensionsTest.InitSheets( states, out _, @@ -205,7 +206,7 @@ public void GetSheets() [InlineData(true)] public void GetCrystalCostState(bool exist) { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); int expectedCount = exist ? 1 : 0; FungibleAssetValue expectedCrystal = exist ? 100 * CrystalCalculator.CRYSTAL @@ -238,7 +239,7 @@ public void GetCrystalCostStates(long blockIndex, bool previousWeeklyExist) Address previousCostAddress = Addresses.GetWeeklyCrystalCostAddress(weeklyIndex - 1); Address beforePreviousCostAddress = Addresses.GetWeeklyCrystalCostAddress(weeklyIndex - 2); var crystalCostState = new CrystalCostState(default, 100 * CrystalCalculator.CRYSTAL); - IWorld state = new World(new MockWorldState()) + IWorld state = new World(MockUtil.MockModernWorldState) .SetLegacyState(dailyCostAddress, crystalCostState.Serialize()) .SetLegacyState(weeklyCostAddress, crystalCostState.Serialize()) .SetLegacyState(previousCostAddress, crystalCostState.Serialize()) @@ -268,7 +269,7 @@ public void GetCrystalCostStates(long blockIndex, bool previousWeeklyExist) [Fact] public void GetCouponWallet() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); var guid1 = new Guid("6856AE42-A820-4041-92B0-5D7BAA52F2AA"); var guid2 = new Guid("701BA698-CCB9-4FC7-B88F-7CB8C707D135"); var guid3 = new Guid("910296E7-34E4-45D7-9B4E-778ED61F278B"); diff --git a/.Lib9c.Tests/Action/ActionContextExtensionsTest.cs b/.Lib9c.Tests/Action/ActionContextExtensionsTest.cs index 50dd23f3b0..6af16f6417 100644 --- a/.Lib9c.Tests/Action/ActionContextExtensionsTest.cs +++ b/.Lib9c.Tests/Action/ActionContextExtensionsTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -143,7 +144,7 @@ public static IEnumerable IsPreviewNetTestcases() [MemberData(nameof(IsMainNetTestcases))] public void IsMainNet(GoldCurrencyState goldCurrencyState, bool expected) { - var state = new World(new MockWorldState()).SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + var state = new World(MockUtil.MockModernWorldState).SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); IActionContext context = new ActionContext { PreviousState = state, diff --git a/.Lib9c.Tests/Action/ActionEvaluationTest.cs b/.Lib9c.Tests/Action/ActionEvaluationTest.cs index d6ee4a2392..a897fbbff2 100644 --- a/.Lib9c.Tests/Action/ActionEvaluationTest.cs +++ b/.Lib9c.Tests/Action/ActionEvaluationTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using Lib9c.Formatters; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using MessagePack; using MessagePack.Resolvers; @@ -34,7 +35,7 @@ public ActionEvaluationTest() #pragma warning restore CS0618 _signer = new PrivateKey().Address; _sender = new PrivateKey().Address; - _states = new World(new MockWorldState()) + _states = new World(MockUtil.MockModernWorldState) .SetLegacyState(_signer, (Text)"ANYTHING") .SetLegacyState(default, Dictionary.Empty.Add("key", "value")) .MintAsset(context, _signer, _currency * 10000); diff --git a/.Lib9c.Tests/Action/ActivateAccount0Test.cs b/.Lib9c.Tests/Action/ActivateAccount0Test.cs deleted file mode 100644 index 2066fd1040..0000000000 --- a/.Lib9c.Tests/Action/ActivateAccount0Test.cs +++ /dev/null @@ -1,168 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Collections.Immutable; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Crypto; - using Nekoyume.Action; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Xunit; - - public class ActivateAccount0Test - { - [Fact] - public void Execute() - { - var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; - var privateKey = new PrivateKey(); - (ActivationKey activationKey, PendingActivationState pendingActivation) = - ActivationKey.Create(privateKey, nonce); - var state = new World( - new MockWorldState() - .SetState( - ReservedAddresses.LegacyAccount, - ActivatedAccountsState.Address, - new ActivatedAccountsState().Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, - pendingActivation.address, - pendingActivation.Serialize())); - - ActivateAccount0 action = activationKey.CreateActivateAccount0(nonce); - IWorld nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = default, - BlockIndex = 1, - }); - - var activatedAccounts = new ActivatedAccountsState( - (Dictionary)nextState.GetLegacyState(ActivatedAccountsState.Address) - ); - Assert.Equal( - new[] { default(Address) }.ToImmutableHashSet(), - activatedAccounts.Accounts); - } - - [Fact] - public void ExecuteWithInvalidSignature() - { - var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; - var privateKey = new PrivateKey(); - (ActivationKey activationKey, PendingActivationState pendingActivation) = - ActivationKey.Create(privateKey, nonce); - var state = new World( - new MockWorldState() - .SetState( - ReservedAddresses.LegacyAccount, - ActivatedAccountsState.Address, - new ActivatedAccountsState().Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, - pendingActivation.address, - pendingActivation.Serialize())); - - // 잘못된 논스를 넣습니다. - ActivateAccount0 action = activationKey.CreateActivateAccount0(new byte[] { 0x00, }); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = default, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ExecuteWithNonExistsPending() - { - var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; - var privateKey = new PrivateKey(); - (ActivationKey activationKey, PendingActivationState pendingActivation) = - ActivationKey.Create(privateKey, nonce); - - // state에는 pendingActivation에 해당하는 대기가 없는 상태를 가정합니다. - var state = new World( - new MockWorldState() - .SetState( - ReservedAddresses.LegacyAccount, - ActivatedAccountsState.Address, - new ActivatedAccountsState().Serialize())); - - ActivateAccount0 action = activationKey.CreateActivateAccount0(nonce); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = default, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ExecuteWithNonExistsAccounts() - { - var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; - var privateKey = new PrivateKey(); - (ActivationKey activationKey, PendingActivationState pendingActivation) = - ActivationKey.Create(privateKey, nonce); - - // state가 올바르게 초기화되지 않은 상태를 가정합니다. - var state = new World(new MockWorldState()); - - ActivateAccount0 action = activationKey.CreateActivateAccount0(nonce); - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = default, - BlockIndex = 1, - }); - }); - } - - [Fact] - public void ForbidReusingActivationKey() - { - var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; - var privateKey = new PrivateKey(); - (ActivationKey activationKey, PendingActivationState pendingActivation) = - ActivationKey.Create(privateKey, nonce); - var state = new World( - new MockWorldState() - .SetState( - ReservedAddresses.LegacyAccount, - ActivatedAccountsState.Address, - new ActivatedAccountsState().Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, - pendingActivation.address, - pendingActivation.Serialize())); - - ActivateAccount0 action = activationKey.CreateActivateAccount0(nonce); - IWorld nextState = action.Execute(new ActionContext() - { - PreviousState = state, - Signer = default, - BlockIndex = 1, - }); - - Assert.Throws(() => - { - action.Execute(new ActionContext() - { - PreviousState = nextState, - Signer = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"), - BlockIndex = 2, - }); - }); - } - } -} diff --git a/.Lib9c.Tests/Action/ActivateAccountTest.cs b/.Lib9c.Tests/Action/ActivateAccountTest.cs index 752ea3dbac..0aa531f1bf 100644 --- a/.Lib9c.Tests/Action/ActivateAccountTest.cs +++ b/.Lib9c.Tests/Action/ActivateAccountTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model; using Nekoyume.Model.State; @@ -25,7 +26,7 @@ public void Execute(bool invalid, bool pendingExist, bool alreadyActivated, Type ActivationKey.Create(privateKey, nonce); Address activatedAddress = default(Address).Derive(ActivationKey.DeriveKey); - IWorld state = new World(new MockWorldState()); + IWorld state = new World(MockUtil.MockModernWorldState); if (pendingExist) { diff --git a/.Lib9c.Tests/Action/ActivateCollectionTest.cs b/.Lib9c.Tests/Action/ActivateCollectionTest.cs index 40ecd9fe93..fd48733be3 100644 --- a/.Lib9c.Tests/Action/ActivateCollectionTest.cs +++ b/.Lib9c.Tests/Action/ActivateCollectionTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.Collection; @@ -50,7 +51,7 @@ public ActivateCollectionTest() }; agentState.avatarAddresses.Add(0, _avatarAddress); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetAgentState(_agentAddress, agentState) .SetAvatarState(_avatarAddress, avatarState, true, true, true, true) .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); diff --git a/.Lib9c.Tests/Action/AddActivatedAccountTest.cs b/.Lib9c.Tests/Action/AddActivatedAccountTest.cs index 41c7e4e9fd..9a4240fc54 100644 --- a/.Lib9c.Tests/Action/AddActivatedAccountTest.cs +++ b/.Lib9c.Tests/Action/AddActivatedAccountTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using System; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model; using Nekoyume.Model.State; @@ -19,9 +20,8 @@ public class AddActivatedAccountTest public void Execute(bool isAdmin, long blockIndex, bool alreadyActivated, Type exc) { var admin = new Address("8d9f76aF8Dc5A812aCeA15d8bf56E2F790F47fd7"); - IWorld state = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, new AdminState(admin, 100).Serialize())); + IWorld state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, new AdminState(admin, 100).Serialize()); var newComer = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var activatedAddress = newComer.Derive(ActivationKey.DeriveKey); if (alreadyActivated) diff --git a/.Lib9c.Tests/Action/AddRedeemCodeTest.cs b/.Lib9c.Tests/Action/AddRedeemCodeTest.cs index c9cdb81c6c..7e04a39cdf 100644 --- a/.Lib9c.Tests/Action/AddRedeemCodeTest.cs +++ b/.Lib9c.Tests/Action/AddRedeemCodeTest.cs @@ -2,6 +2,7 @@ namespace Lib9c.Tests.Action { using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; @@ -16,9 +17,8 @@ public void CheckPermission() { var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var initStates = new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize()); - var state = new World(initStates); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); var action = new AddRedeemCode { redeemCsv = "New Value", @@ -68,7 +68,7 @@ public void Execute() { Signer = adminAddress, BlockIndex = 0, - PreviousState = new World(new MockWorldState()) + PreviousState = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.Admin, adminState.Serialize()) .SetLegacyState(Addresses.RedeemCode, new RedeemCodeState(new RedeemCodeListSheet()).Serialize()), }); @@ -90,7 +90,7 @@ public void ExecuteThrowSheetRowValidateException() var sheet = new RedeemCodeListSheet(); sheet.Set(csv); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.RedeemCode, new RedeemCodeState(sheet).Serialize()); var action = new AddRedeemCode diff --git a/.Lib9c.Tests/Action/ApprovePledgeTest.cs b/.Lib9c.Tests/Action/ApprovePledgeTest.cs index fef336d40d..f0aaddfcef 100644 --- a/.Lib9c.Tests/Action/ApprovePledgeTest.cs +++ b/.Lib9c.Tests/Action/ApprovePledgeTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; @@ -20,7 +21,7 @@ public void Execute(int mead) var address = new PrivateKey().Address; var patron = new PrivateKey().Address; var contractAddress = address.Derive(nameof(RequestPledge)); - IWorld states = new World(new MockWorldState()) + IWorld states = new World(MockUtil.MockModernWorldState) .SetLegacyState( contractAddress, List.Empty.Add(patron.Serialize()).Add(false.Serialize()).Add(mead.Serialize()) @@ -62,7 +63,7 @@ public void Execute_Throw_Exception(bool invalidPatron, bool alreadyContract, Ty contract = List.Empty.Add(patron.Serialize()).Add(true.Serialize()); } - IWorld states = new World(new MockWorldState()).SetLegacyState(contractAddress, contract); + IWorld states = new World(MockUtil.MockModernWorldState).SetLegacyState(contractAddress, contract); var action = new ApprovePledge { diff --git a/.Lib9c.Tests/Action/ArenahelperTest.cs b/.Lib9c.Tests/Action/ArenahelperTest.cs index cda73b8043..7f94084a3e 100644 --- a/.Lib9c.Tests/Action/ArenahelperTest.cs +++ b/.Lib9c.Tests/Action/ArenahelperTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -35,7 +36,7 @@ public ArenaHelperTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _state = new World(new MockWorldState()); + _state = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); var tableSheets = new TableSheets(sheets); diff --git a/.Lib9c.Tests/Action/BattleArenaTest.cs b/.Lib9c.Tests/Action/BattleArenaTest.cs index 23397678b2..76b680273e 100644 --- a/.Lib9c.Tests/Action/BattleArenaTest.cs +++ b/.Lib9c.Tests/Action/BattleArenaTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -47,7 +48,7 @@ public BattleArenaTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialStates = new World(new MockWorldState()); + _initialStates = new World(MockUtil.MockModernWorldState); _sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in _sheets) diff --git a/.Lib9c.Tests/Action/BurnAssetTest.cs b/.Lib9c.Tests/Action/BurnAssetTest.cs index 5a05f505c6..5d90e088b0 100644 --- a/.Lib9c.Tests/Action/BurnAssetTest.cs +++ b/.Lib9c.Tests/Action/BurnAssetTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Exceptions; using Nekoyume.Module; @@ -20,17 +21,14 @@ public BurnAssetTest() { _signer = new PrivateKey().Address; _prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_signer, Currencies.Crystal * 100) .SetBalance( _signer.Derive(string.Format( CultureInfo.InvariantCulture, CreateAvatar.DeriveFormat, - 1 - )), - Currencies.DailyRewardRune * 20 - ) - ); + 1)), + Currencies.DailyRewardRune * 20)); } [Fact] diff --git a/.Lib9c.Tests/Action/Buy7Test.cs b/.Lib9c.Tests/Action/Buy7Test.cs index 08543d57c3..508cb5e3b6 100644 --- a/.Lib9c.Tests/Action/Buy7Test.cs +++ b/.Lib9c.Tests/Action/Buy7Test.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -40,7 +41,7 @@ public Buy7Test(ITestOutputHelper outputHelper) .CreateLogger(); var context = new ActionContext(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -444,7 +445,7 @@ public void Execute_Throw_FailedLoadStateException() Assert.Throws(() => action.Execute(new ActionContext() { BlockIndex = 0, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), RandomSeed = 0, Signer = _buyerAgentAddress, }) diff --git a/.Lib9c.Tests/Action/BuyMultipleTest.cs b/.Lib9c.Tests/Action/BuyMultipleTest.cs index 5aae102522..3f212dd6bb 100644 --- a/.Lib9c.Tests/Action/BuyMultipleTest.cs +++ b/.Lib9c.Tests/Action/BuyMultipleTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using System.Numerics; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -35,7 +36,7 @@ public BuyMultipleTest(ITestOutputHelper outputHelper) .CreateLogger(); var context = new ActionContext(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -407,7 +408,7 @@ public void ExecuteThrowInvalidAddressException() Assert.Throws(() => action.Execute(new ActionContext() { BlockIndex = 0, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), RandomSeed = 0, Signer = _buyerAgentAddress, }) @@ -426,7 +427,7 @@ public void ExecuteThrowFailedLoadStateException() Assert.Throws(() => action.Execute(new ActionContext() { BlockIndex = 0, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), RandomSeed = 0, Signer = _buyerAgentAddress, }) diff --git a/.Lib9c.Tests/Action/BuyProductTest.cs b/.Lib9c.Tests/Action/BuyProductTest.cs index 509175c5ee..20327a1570 100644 --- a/.Lib9c.Tests/Action/BuyProductTest.cs +++ b/.Lib9c.Tests/Action/BuyProductTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -46,7 +47,7 @@ public BuyProductTest(ITestOutputHelper outputHelper) .CreateLogger(); var context = new ActionContext(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/Action/BuyTest.cs b/.Lib9c.Tests/Action/BuyTest.cs index 9ad40c8916..ceb1408701 100644 --- a/.Lib9c.Tests/Action/BuyTest.cs +++ b/.Lib9c.Tests/Action/BuyTest.cs @@ -11,6 +11,7 @@ namespace Lib9c.Tests.Action using Lib9c.Tests.TestHelper; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -45,7 +46,7 @@ public BuyTest(ITestOutputHelper outputHelper) .CreateLogger(); var context = new ActionContext(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/Action/CancelProductRegistration0Test.cs b/.Lib9c.Tests/Action/CancelProductRegistrationTest.cs similarity index 94% rename from .Lib9c.Tests/Action/CancelProductRegistration0Test.cs rename to .Lib9c.Tests/Action/CancelProductRegistrationTest.cs index 97b097daaa..c25149aa93 100644 --- a/.Lib9c.Tests/Action/CancelProductRegistration0Test.cs +++ b/.Lib9c.Tests/Action/CancelProductRegistrationTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -17,7 +18,7 @@ namespace Lib9c.Tests.Action using Xunit; using Xunit.Abstractions; - public class CancelProductRegistration0Test + public class CancelProductRegistrationTest { private readonly IWorld _initialState; private readonly Address _agentAddress; @@ -26,14 +27,14 @@ public class CancelProductRegistration0Test private readonly TableSheets _tableSheets; private readonly GameConfigState _gameConfigState; - public CancelProductRegistration0Test(ITestOutputHelper outputHelper) + public CancelProductRegistrationTest(ITestOutputHelper outputHelper) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -84,7 +85,7 @@ public void Execute_Throw_InvalidAddressException( bool invalidAgentAddress ) { - var action = new CancelProductRegistration0 + var action = new CancelProductRegistration { AvatarAddress = _avatarAddress, ProductInfos = new List @@ -159,7 +160,7 @@ public void Execute_Throw_ProductNotFoundException() (List)nexState.GetLegacyState(ProductsState.DeriveAddress(_avatarAddress))); var productId = Assert.Single(productsState.ProductIds); - var action = new CancelProductRegistration0 + var action = new CancelProductRegistration { AvatarAddress = _avatarAddress, ProductInfos = new List @@ -196,12 +197,12 @@ public void Execute_Throw_ProductNotFoundException() public void Execute_Throw_ArgumentOutOfRangeException() { var productInfos = new List(); - for (int i = 0; i < CancelProductRegistration0.Capacity + 1; i++) + for (int i = 0; i < CancelProductRegistration.Capacity + 1; i++) { productInfos.Add(new ItemProductInfo()); } - var action = new CancelProductRegistration0 + var action = new CancelProductRegistration { AvatarAddress = _avatarAddress, ProductInfos = productInfos, diff --git a/.Lib9c.Tests/Action/ChargeActionPointTest.cs b/.Lib9c.Tests/Action/ChargeActionPointTest.cs index 258b691cdd..2b57f745f7 100644 --- a/.Lib9c.Tests/Action/ChargeActionPointTest.cs +++ b/.Lib9c.Tests/Action/ChargeActionPointTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.Item; @@ -46,7 +47,7 @@ public ChargeActionPointTest() }; agent.avatarAddresses.Add(0, _avatarAddress); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.GameConfig, gameConfigState.Serialize()) .SetAgentState(_agentAddress, agent) .SetAvatarState(_avatarAddress, avatarState); diff --git a/.Lib9c.Tests/Action/ClaimItemsTest.cs b/.Lib9c.Tests/Action/ClaimItemsTest.cs index c74ddb646d..d358703d7a 100644 --- a/.Lib9c.Tests/Action/ClaimItemsTest.cs +++ b/.Lib9c.Tests/Action/ClaimItemsTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -36,7 +37,7 @@ public ClaimItemsTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) diff --git a/.Lib9c.Tests/Action/ClaimRaidRewardTest.cs b/.Lib9c.Tests/Action/ClaimRaidRewardTest.cs index d2a07dfead..541bb31d29 100644 --- a/.Lib9c.Tests/Action/ClaimRaidRewardTest.cs +++ b/.Lib9c.Tests/Action/ClaimRaidRewardTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Helper; @@ -20,7 +21,7 @@ public ClaimRaidRewardTest() { var tableCsv = TableSheetsImporter.ImportSheets(); _tableSheets = new TableSheets(tableCsv); - _state = new World(new MockWorldState()); + _state = new World(MockUtil.MockModernWorldState); foreach (var kv in tableCsv) { _state = _state.SetLegacyState(Addresses.GetSheetAddress(kv.Key), kv.Value.Serialize()); diff --git a/.Lib9c.Tests/Action/ClaimWorldBossKillRewardTest.cs b/.Lib9c.Tests/Action/ClaimWorldBossKillRewardTest.cs index ce8f01a076..cafdbc156e 100644 --- a/.Lib9c.Tests/Action/ClaimWorldBossKillRewardTest.cs +++ b/.Lib9c.Tests/Action/ClaimWorldBossKillRewardTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -27,7 +28,7 @@ public void Execute(long blockIndex, Type exc) var tableSheets = new TableSheets(sheets); Address agentAddress = new PrivateKey().Address; Address avatarAddress = new PrivateKey().Address; - IWorld state = new World(new MockWorldState()); + IWorld state = new World(MockUtil.MockModernWorldState); var runeWeightSheet = new RuneWeightSheet(); runeWeightSheet.Set(@"id,boss_id,rank,rune_id,weight diff --git a/.Lib9c.Tests/Action/CombinationConsumableTest.cs b/.Lib9c.Tests/Action/CombinationConsumableTest.cs new file mode 100644 index 0000000000..00116f45b5 --- /dev/null +++ b/.Lib9c.Tests/Action/CombinationConsumableTest.cs @@ -0,0 +1,134 @@ +namespace Lib9c.Tests.Action +{ + using System.Globalization; + using System.Linq; + using Libplanet.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + 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.Module; + using Xunit; + + public class CombinationConsumableTest + { + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private readonly IRandom _random; + private readonly TableSheets _tableSheets; + private IWorld _initialState; + + public CombinationConsumableTest() + { + _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 World(MockUtil.MockModernWorldState) + .SetAgentState(_agentAddress, agentState) + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState( + slotAddress, + new CombinationSlotState( + slotAddress, + GameConfig.RequireClearedStageLevel.CombinationConsumableAction).Serialize()) + .SetLegacyState(GameConfigState.Address, gold.Serialize()); + + foreach (var (key, value) in sheets) + { + _initialState = + _initialState.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + } + + [Fact] + public void Execute() + { + 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); + + IWorld previousState = _initialState.SetAvatarState(_avatarAddress, avatarState); + + var action = new CombinationConsumable + { + 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.GetAvatarState(_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/CombinationEquipmentTest.cs b/.Lib9c.Tests/Action/CombinationEquipmentTest.cs index e790df9d75..fe9b374b27 100644 --- a/.Lib9c.Tests/Action/CombinationEquipmentTest.cs +++ b/.Lib9c.Tests/Action/CombinationEquipmentTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -76,7 +77,7 @@ public CombinationEquipmentTest(ITestOutputHelper outputHelper) _slotAddress, 0); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(_slotAddress, combinationSlotState.Serialize()) .SetLegacyState(GoldCurrencyState.Address, gold.Serialize()); diff --git a/.Lib9c.Tests/Action/Common/MockAccountState.cs b/.Lib9c.Tests/Action/Common/MockAccountState.cs deleted file mode 100644 index ed31addbf5..0000000000 --- a/.Lib9c.Tests/Action/Common/MockAccountState.cs +++ /dev/null @@ -1,219 +0,0 @@ -#nullable enable -namespace Lib9c.Tests.Action -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Numerics; - using System.Security.Cryptography; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Common; - using Libplanet.Crypto; - using Libplanet.Store; - using Libplanet.Store.Trie; - using Libplanet.Types.Assets; - using Libplanet.Types.Consensus; - - /// - /// A mock implementation of with various overloaded methods for - /// improving QoL. - /// - /// - /// All methods are pretty self-explanatory with no side-effects. There are some caveats: - /// - /// - /// Every balance related operation can accept a negative amount. Each behave as expected. - /// That is, adding negative amount would decrease the balance. - /// - /// - /// Negative balance is allowed for all cases. This includes total supply. - /// - /// - /// Total supply is not automatically tracked. That is, changing the balance associated - /// with an does not change the total supply in any way. - /// Total supply must be explicitly set if needed. - /// - /// - /// There are only few restrictions that apply for manipulating this object, mostly - /// pertaining to total supplies: - /// - /// - /// It is not possible to set a total supply amount for a currency that is - /// not trackable. - /// - /// - /// It is not possible to set a total supply amount that is over the currency's - /// capped maximum total supply. - /// - /// - /// - /// - /// - public class MockAccountState : IAccountState - { - private readonly IStateStore _stateStore; - - public MockAccountState() - : this(new TrieStateStore(new MemoryKeyValueStore()), null) - { - } - - public MockAccountState( - IStateStore stateStore, - HashDigest? stateRootHash = null) - { - _stateStore = stateStore; - Trie = stateStore.GetStateRoot(stateRootHash); - } - - public ITrie Trie { get; } - - public IValue? GetState(Address address) => - Trie.Get(MockKeyConverters.ToStateKey(address)); - - public IReadOnlyList GetStates(IReadOnlyList
addresses) => - addresses.Select(GetState).ToList(); - - public FungibleAssetValue GetBalance(Address address, Currency currency) => - Trie.Get(MockKeyConverters.ToFungibleAssetKey(address, currency)) is Integer rawValue - ? FungibleAssetValue.FromRawValue(currency, rawValue) - : currency * 0; - - public FungibleAssetValue GetTotalSupply(Currency currency) - { - if (!currency.TotalSupplyTrackable) - { - var msg = - $"The total supply value of the currency {currency} is not trackable " + - "because it is a legacy untracked currency which might have been" + - "established before the introduction of total supply tracking support."; - throw new TotalSupplyNotTrackableException(msg, currency); - } - - return Trie.Get(MockKeyConverters.ToTotalSupplyKey(currency)) is Integer rawValue - ? FungibleAssetValue.FromRawValue(currency, rawValue) - : currency * 0; - } - - public ValidatorSet GetValidatorSet() => - Trie.Get(MockKeyConverters.ValidatorSetKey) is List list - ? new ValidatorSet(list) - : new ValidatorSet(); - - public MockAccountState SetState(Address address, IValue state) => - new MockAccountState( - _stateStore, - _stateStore.Commit(Trie.Set(MockKeyConverters.ToStateKey(address), state)).Hash); - - public MockAccountState SetBalance( - Address address, FungibleAssetValue amount) => - SetBalance((address, amount.Currency), amount.RawValue); - - public MockAccountState SetBalance( - Address address, Currency currency, BigInteger rawAmount) => - SetBalance((address, currency), rawAmount); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState SetBalance( - (Address Address, Currency Currency) pair, BigInteger rawAmount) => - new MockAccountState( - _stateStore, - _stateStore.Commit( - Trie.Set( - MockKeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency), - new Integer(rawAmount))).Hash); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - public MockAccountState AddBalance(Address address, FungibleAssetValue amount) => - AddBalance((address, amount.Currency), amount.RawValue); - - public MockAccountState AddBalance( - Address address, Currency currency, BigInteger rawAmount) => - AddBalance((address, currency), rawAmount); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState AddBalance( - (Address Address, Currency Currency) pair, BigInteger rawAmount) => - SetBalance( - pair, - (Trie.Get(MockKeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency)) is - Integer amount ? amount : 0) + rawAmount); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - public MockAccountState SubtractBalance( - Address address, FungibleAssetValue amount) => - SubtractBalance((address, amount.Currency), amount.RawValue); - - public MockAccountState SubtractBalance( - Address address, Currency currency, BigInteger rawAmount) => - SubtractBalance((address, currency), rawAmount); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState SubtractBalance( - (Address Address, Currency Currency) pair, BigInteger rawAmount) => - SetBalance( - pair, - (Trie.Get(MockKeyConverters.ToFungibleAssetKey(pair.Address, pair.Currency)) is - Integer amount ? amount : 0) - rawAmount); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - public MockAccountState TransferBalance( - Address sender, Address recipient, FungibleAssetValue amount) => - TransferBalance(sender, recipient, amount.Currency, amount.RawValue); - - public MockAccountState TransferBalance( - Address sender, Address recipient, Currency currency, BigInteger rawAmount) => - SubtractBalance(sender, currency, rawAmount).AddBalance(recipient, currency, rawAmount); - - public MockAccountState SetTotalSupply(FungibleAssetValue amount) => - SetTotalSupply(amount.Currency, amount.RawValue); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState SetTotalSupply(Currency currency, BigInteger rawAmount) => - currency.TotalSupplyTrackable - ? !(currency.MaximumSupply is FungibleAssetValue maximumSupply) || - rawAmount <= maximumSupply.RawValue - ? new MockAccountState( - _stateStore, - _stateStore.Commit( - Trie.Set(MockKeyConverters.ToTotalSupplyKey(currency), new Integer(rawAmount))).Hash) - : throw new ArgumentException( - $"Given {currency}'s total supply is capped at {maximumSupply.RawValue} " + - $"and cannot be set to {rawAmount}.") - : throw new ArgumentException( - $"Given {currency} is not trackable."); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - public MockAccountState AddTotalSupply(FungibleAssetValue amount) => - AddTotalSupply(amount.Currency, amount.RawValue); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState AddTotalSupply(Currency currency, BigInteger rawAmount) => - SetTotalSupply( - currency, - (Trie.Get(MockKeyConverters.ToTotalSupplyKey(currency)) is - Integer amount ? amount : 0) + rawAmount); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - public MockAccountState SubtractTotalSupply(FungibleAssetValue amount) => - SubtractTotalSupply(amount.Currency, amount.RawValue); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState SubtractTotalSupply(Currency currency, BigInteger rawAmount) => - SetTotalSupply( - currency, - (Trie.Get(MockKeyConverters.ToTotalSupplyKey(currency)) is - Integer amount ? amount : 0) - rawAmount); -#pragma warning restore SA1118 // Parameter should not span multiple lines - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState SetValidator(Validator validator) => - new MockAccountState( - _stateStore, - _stateStore.Commit( - Trie.Set(MockKeyConverters.ValidatorSetKey, GetValidatorSet().Update(validator).Bencoded)).Hash); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - } -} diff --git a/.Lib9c.Tests/Action/Common/MockKeyConverters.cs b/.Lib9c.Tests/Action/Common/MockKeyConverters.cs deleted file mode 100644 index 44d0239eac..0000000000 --- a/.Lib9c.Tests/Action/Common/MockKeyConverters.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using Libplanet.Crypto; - using Libplanet.Store.Trie; - using Libplanet.Types.Assets; - - internal static class MockKeyConverters - { - // "___" - internal static readonly KeyBytes ValidatorSetKey = - new KeyBytes(new byte[] { _underScore, _underScore, _underScore }); - - private const byte _underScore = 95; // '_' - - private static readonly byte[] _conversionTable = - { - 48, // '0' - 49, // '1' - 50, // '2' - 51, // '3' - 52, // '4' - 53, // '5' - 54, // '6' - 55, // '7' - 56, // '8' - 57, // '9' - 97, // 'a' - 98, // 'b' - 99, // 'c' - 100, // 'd' - 101, // 'e' - 102, // 'f' - }; - - // $"{ByteUtil.Hex(address.ByteArray)}" - internal static KeyBytes ToStateKey(Address address) - { - var addressBytes = address.ByteArray; - byte[] buffer = new byte[addressBytes.Length * 2]; - for (int i = 0; i < addressBytes.Length; i++) - { - buffer[i * 2] = _conversionTable[addressBytes[i] >> 4]; - buffer[i * 2 + 1] = _conversionTable[addressBytes[i] & 0xf]; - } - - return new KeyBytes(buffer); - } - - // $"_{ByteUtil.Hex(address.ByteArray)}_{ByteUtil.Hex(currency.Hash.ByteArray)}" - internal static KeyBytes ToFungibleAssetKey(Address address, Currency currency) - { - var addressBytes = address.ByteArray; - var currencyBytes = currency.Hash.ByteArray; - byte[] buffer = new byte[addressBytes.Length * 2 + currencyBytes.Length * 2 + 2]; - - buffer[0] = _underScore; - for (int i = 0; i < addressBytes.Length; i++) - { - buffer[1 + i * 2] = _conversionTable[addressBytes[i] >> 4]; - buffer[1 + i * 2 + 1] = _conversionTable[addressBytes[i] & 0xf]; - } - - var offset = addressBytes.Length * 2; - buffer[offset + 1] = _underScore; - for (int i = 0; i < currencyBytes.Length; i++) - { - buffer[offset + 2 + i * 2] = _conversionTable[currencyBytes[i] >> 4]; - buffer[offset + 2 + i * 2 + 1] = _conversionTable[currencyBytes[i] & 0xf]; - } - - return new KeyBytes(buffer); - } - - internal static KeyBytes ToFungibleAssetKey( - (Address Address, Currency Currency) pair) => - ToFungibleAssetKey(pair.Address, pair.Currency); - - // $"__{ByteUtil.Hex(currency.Hash.ByteArray)}" - internal static KeyBytes ToTotalSupplyKey(Currency currency) - { - var currencyBytes = currency.Hash.ByteArray; - byte[] buffer = new byte[currencyBytes.Length * 2 + 2]; - - buffer[0] = _underScore; - buffer[1] = _underScore; - - for (int i = 0; i < currencyBytes.Length; i++) - { - buffer[2 + i * 2] = _conversionTable[currencyBytes[i] >> 4]; - buffer[2 + i * 2 + 1] = _conversionTable[currencyBytes[i] & 0xf]; - } - - return new KeyBytes(buffer); - } - } -} diff --git a/.Lib9c.Tests/Action/Common/MockWorldState.cs b/.Lib9c.Tests/Action/Common/MockWorldState.cs deleted file mode 100644 index a19f62ffe1..0000000000 --- a/.Lib9c.Tests/Action/Common/MockWorldState.cs +++ /dev/null @@ -1,164 +0,0 @@ -namespace Lib9c.Tests.Action -{ - using System.Numerics; - using System.Security.Cryptography; - using Bencodex.Types; - using Libplanet.Action.State; - using Libplanet.Common; - using Libplanet.Crypto; - using Libplanet.Store; - using Libplanet.Store.Trie; - using Libplanet.Types.Assets; - using Libplanet.Types.Consensus; - - public class MockWorldState : IWorldState - { - private readonly IStateStore _stateStore; - - public MockWorldState() - : this(new TrieStateStore(new MemoryKeyValueStore()), null) - { - } - - public MockWorldState( - IStateStore stateStore, - HashDigest? stateRootHash = null) - { - _stateStore = stateStore; - Trie = stateStore.GetStateRoot(stateRootHash); - Legacy = false; - } - - public ITrie Trie { get; } - - public bool Legacy { get; private set; } - - public IAccountState GetAccountState(Address address) => GetMockAccountState(address); - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockAccountState GetMockAccountState(Address address) - => Legacy - ? new MockAccountState(_stateStore, Trie.Hash) - : new MockAccountState( - _stateStore, - Trie.Get(MockKeyConverters.ToStateKey(address)) is { } stateRootNotNull - ? new HashDigest(stateRootNotNull) - : null); -#pragma warning restore SA1118 // Parameter should not span multiple lines - -#pragma warning disable SA1118 // Parameter should not span multiple lines - public MockWorldState SetAccountState(Address address, IAccountState accountState) - => Legacy - ? new MockWorldState(_stateStore, accountState.Trie.Hash) - : new MockWorldState( - _stateStore, - _stateStore.Commit( - Trie.Set( - MockKeyConverters.ToStateKey(address), - new Binary(accountState.Trie.Hash.ByteArray))).Hash); -#pragma warning restore SA1118 // Parameter should not span multiple lines - - public MockWorldState SetState(Address accountAddress, Address address, IValue state) - => SetAccountState(accountAddress, GetMockAccountState(accountAddress).SetState(address, state)); - - public MockWorldState SetBalance( - Address address, - FungibleAssetValue amount) - => SetBalance((address, amount.Currency), amount.RawValue); - - public MockWorldState SetBalance( - Address address, Currency currency, BigInteger rawAmount) - => SetBalance((address, currency), rawAmount); - - public MockWorldState SetBalance( - (Address Address, Currency Currency) pair, - BigInteger rawAmount) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .SetBalance((pair.Address, pair.Currency), rawAmount)); - - public MockWorldState AddBalance( - Address address, FungibleAssetValue amount) - => AddBalance((address, amount.Currency), amount.RawValue); - - public MockWorldState AddBalance( - Address address, Currency currency, BigInteger rawAmount) - => AddBalance((address, currency), rawAmount); - - public MockWorldState AddBalance( - (Address Address, Currency Currency) pair, - BigInteger rawAmount) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .AddBalance(pair, rawAmount)); - - public MockWorldState SubtractBalance( - Address address, FungibleAssetValue amount) - => SubtractBalance((address, amount.Currency), amount.RawValue); - - public MockWorldState SubtractBalance( - Address address, Currency currency, BigInteger rawAmount) - => SubtractBalance((address, currency), rawAmount); - - public MockWorldState SubtractBalance( - (Address Address, Currency Currency) pair, - BigInteger rawAmount) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .SubtractBalance(pair, rawAmount)); - - public MockWorldState TransferBalance( - Address sender, - Address recipient, - FungibleAssetValue amount) => - TransferBalance( - sender, - recipient, - amount.Currency, - amount.RawValue); - - public MockWorldState TransferBalance( - Address sender, - Address recipient, - Currency currency, - BigInteger rawAmount) - => SubtractBalance(sender, currency, rawAmount) - .AddBalance(recipient, currency, rawAmount); - - public MockWorldState SetTotalSupply(FungibleAssetValue amount) - => SetTotalSupply(amount.Currency, amount.RawValue); - - public MockWorldState SetTotalSupply(Currency currency, BigInteger rawAmount) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .SetTotalSupply(currency, rawAmount)); - - public MockWorldState AddTotalSupply(FungibleAssetValue amount) - => AddTotalSupply(amount.Currency, amount.RawValue); - - public MockWorldState AddTotalSupply(Currency currency, BigInteger rawAmount) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .AddTotalSupply(currency, rawAmount)); - - public MockWorldState SubtractTotalSupply(FungibleAssetValue amount) - => SubtractTotalSupply(amount.Currency, amount.RawValue); - - public MockWorldState SubtractTotalSupply(Currency currency, BigInteger rawAmount) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .SubtractTotalSupply(currency, rawAmount)); - - public MockWorldState SetValidator(Validator validator) - => SetAccountState( - ReservedAddresses.LegacyAccount, - GetMockAccountState(ReservedAddresses.LegacyAccount) - .SetValidator(validator)); - } -} diff --git a/.Lib9c.Tests/Action/Coupons/IssueCouponsTest.cs b/.Lib9c.Tests/Action/Coupons/IssueCouponsTest.cs index 340c00d8c1..77999b3573 100644 --- a/.Lib9c.Tests/Action/Coupons/IssueCouponsTest.cs +++ b/.Lib9c.Tests/Action/Coupons/IssueCouponsTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Coupons using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Action.Coupons; using Nekoyume.Model.Coupons; @@ -19,7 +20,7 @@ public class IssueCouponsTest [Fact] public void Execute() { - IWorld state = new World(new MockWorldState()) + IWorld state = new World(MockUtil.MockModernWorldState) .SetLegacyState( AdminState.Address, new AdminState(CouponsFixture.AgentAddress1, 1) diff --git a/.Lib9c.Tests/Action/Coupons/RedeemCouponTest.cs b/.Lib9c.Tests/Action/Coupons/RedeemCouponTest.cs index c06a3390f5..21261a98da 100644 --- a/.Lib9c.Tests/Action/Coupons/RedeemCouponTest.cs +++ b/.Lib9c.Tests/Action/Coupons/RedeemCouponTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action.Coupons using System.Linq; using Libplanet.Action; using Libplanet.Action.State; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Action.Coupons; @@ -21,7 +22,7 @@ public void Execute() { IRandom random = new TestRandom(); var sheets = TableSheetsImporter.ImportSheets(); - IWorld state = new World(new MockWorldState()) + IWorld state = new World(MockUtil.MockModernWorldState) .SetLegacyState( Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() @@ -42,7 +43,7 @@ public void Execute() .Derive(SerializeKeys.AvatarAddressKey) .Derive("avatar-states-0"); - var agent1Avatar0State = CreateAvatar0.CreateAvatarState( + var agent1Avatar0State = CreateAvatar.CreateAvatarState( "agent1avatar0", agent1Avatar0Address, new ActionContext @@ -53,7 +54,7 @@ public void Execute() }, state.GetSheet(), default); - var agent1Avatar1State = CreateAvatar0.CreateAvatarState( + var agent1Avatar1State = CreateAvatar.CreateAvatarState( "agent1avatar1", agent1Avatar1Address, new ActionContext @@ -64,7 +65,7 @@ public void Execute() }, state.GetSheet(), default); - var agent2Avatar0State = CreateAvatar0.CreateAvatarState( + var agent2Avatar0State = CreateAvatar.CreateAvatarState( "agent2avatar0", agent2Avatar0Address, new ActionContext diff --git a/.Lib9c.Tests/Action/Coupons/TransferCouponsTest.cs b/.Lib9c.Tests/Action/Coupons/TransferCouponsTest.cs index 4b8fae9ae8..7986ce2f52 100644 --- a/.Lib9c.Tests/Action/Coupons/TransferCouponsTest.cs +++ b/.Lib9c.Tests/Action/Coupons/TransferCouponsTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.Coupons using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Action.Coupons; using Nekoyume.Model.Coupons; @@ -18,7 +19,7 @@ public class TransferCouponsTest [Fact] public void Execute() { - IWorld state = new World(new MockWorldState()); + IWorld state = new World(MockUtil.MockModernWorldState); IRandom random = new TestRandom(); var coupon1 = new Coupon(CouponsFixture.Guid1, CouponsFixture.RewardSet1); diff --git a/.Lib9c.Tests/Action/CreateAvatarTest.cs b/.Lib9c.Tests/Action/CreateAvatarTest.cs new file mode 100644 index 0000000000..b6e4b39ae4 --- /dev/null +++ b/.Lib9c.Tests/Action/CreateAvatarTest.cs @@ -0,0 +1,281 @@ +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.Mocks; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Helper; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.TableData; + using Xunit; + using static Lib9c.SerializeKeys; + + public class CreateAvatarTest + { + private readonly Address _agentAddress; + private readonly TableSheets _tableSheets; + + public CreateAvatarTest() + { + _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 CreateAvatar() + { + index = 0, + hair = 0, + ear = 0, + lens = 0, + tail = 0, + name = "test", + }; + + var sheets = TableSheetsImporter.ImportSheets(); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState( + Addresses.GameConfig, + new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() + ); + + foreach (var (key, value) in sheets) + { + state = state.SetLegacyState(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, + CreateAvatar.DeriveFormat, + 0 + ) + ); + Assert.True(nextState.TryGetAvatarState( + default, + avatarAddress, + out var nextAvatarState) + ); + var agentState = nextState.GetAgentState(default); + Assert.NotNull(agentState); + 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 CreateAvatar() + { + index = 0, + hair = 0, + ear = 0, + lens = 0, + tail = 0, + name = nickName, + }; + + var state = new World(MockUtil.MockModernWorldState); + + Assert.Throws(() => action.Execute(new ActionContext() + { + PreviousState = state, + Signer = agentAddress, + BlockIndex = 0, + }) + ); + } + + [Fact] + public void ExecuteThrowInvalidAddressException() + { + var avatarAddress = _agentAddress.Derive( + string.Format( + CultureInfo.InvariantCulture, + CreateAvatar.DeriveFormat, + 0 + ) + ); + + var avatarState = new AvatarState( + avatarAddress, + _agentAddress, + 0, + _tableSheets.GetAvatarSheets(), + new GameConfigState(), + default + ); + + var action = new CreateAvatar() + { + index = 0, + hair = 0, + ear = 0, + lens = 0, + tail = 0, + name = "test", + }; + + var state = new World(MockUtil.MockModernWorldState).SetAvatarState(avatarAddress, avatarState); + + 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 World(MockUtil.MockModernWorldState).SetAgentState(_agentAddress, agentState); + var action = new CreateAvatar() + { + 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, + CreateAvatar.DeriveFormat, + 0 + ) + ); + agentState.avatarAddresses[index] = avatarAddress; + var state = new World(MockUtil.MockModernWorldState).SetAgentState(_agentAddress, agentState); + + var action = new CreateAvatar() + { + 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 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"); + CreateAvatar.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 = CreateAvatar.MintAsset(createAvatarFavSheet, avatarState, new World(MockUtil.MockModernWorldState), 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/CreatePendingActivationTest.cs b/.Lib9c.Tests/Action/CreatePendingActivationTest.cs index c2bc9dff45..9478aa2f28 100644 --- a/.Lib9c.Tests/Action/CreatePendingActivationTest.cs +++ b/.Lib9c.Tests/Action/CreatePendingActivationTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model.State; using Nekoyume.Module; @@ -21,9 +22,8 @@ public void Execute() var action = new CreatePendingActivation(pendingActivation); var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var state = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize())); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); var actionContext = new ActionContext() { BlockIndex = 1, @@ -49,9 +49,8 @@ public void CheckPermission() var action = new CreatePendingActivation(pendingActivation); var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var state = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize())); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); Assert.Throws( () => action.Execute(new ActionContext() diff --git a/.Lib9c.Tests/Action/CreatePendingActivationsTest.cs b/.Lib9c.Tests/Action/CreatePendingActivationsTest.cs index 5367bfa212..efd37f9663 100644 --- a/.Lib9c.Tests/Action/CreatePendingActivationsTest.cs +++ b/.Lib9c.Tests/Action/CreatePendingActivationsTest.cs @@ -4,8 +4,9 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; - using Nekoyume.Model.State; + using Nekoyume.Model.State; using Nekoyume.Module; using Xunit; @@ -28,9 +29,8 @@ PendingActivationState CreatePendingActivation() var action = new CreatePendingActivations(activations); var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var state = new World( - new MockWorldState().SetState( - ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize())); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); var actionContext = new ActionContext() { BlockIndex = 1, @@ -79,9 +79,8 @@ public void CheckPermission() var action = new CreatePendingActivations(); var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var state = new World( - new MockWorldState().SetState( - ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize())); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); Assert.Throws( () => action.Execute(new ActionContext() diff --git a/.Lib9c.Tests/Action/CreatePledgeTest.cs b/.Lib9c.Tests/Action/CreatePledgeTest.cs index 8a5fb64501..9799227cf9 100644 --- a/.Lib9c.Tests/Action/CreatePledgeTest.cs +++ b/.Lib9c.Tests/Action/CreatePledgeTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using System.Collections.Generic; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; @@ -35,7 +36,7 @@ public void Execute(bool admin, Type exc) var agentAddress = new PrivateKey().Address; var pledgeAddress = agentAddress.GetPledgeAddress(); var context = new ActionContext(); - IWorld states = new World(new MockWorldState()) + IWorld states = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.Admin, adminState.Serialize()) .MintAsset(context, patronAddress, 4 * 500 * mead); diff --git a/.Lib9c.Tests/Action/DailyRewardTest.cs b/.Lib9c.Tests/Action/DailyRewardTest.cs index dd24648622..c8fc505a1b 100644 --- a/.Lib9c.Tests/Action/DailyRewardTest.cs +++ b/.Lib9c.Tests/Action/DailyRewardTest.cs @@ -2,6 +2,7 @@ namespace Lib9c.Tests.Action { using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Helper; @@ -26,7 +27,7 @@ public DailyRewardTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -92,7 +93,7 @@ public void Execute(bool legacy) [Fact] public void Execute_Throw_FailedLoadStateException() => - Assert.Throws(() => ExecuteInternal(new World(new MockWorldState()))); + Assert.Throws(() => ExecuteInternal(new World(MockUtil.MockModernWorldState))); [Theory] [InlineData(0, 0, true)] diff --git a/.Lib9c.Tests/Action/EndPledgeTest.cs b/.Lib9c.Tests/Action/EndPledgeTest.cs index 82b7c2e589..6f72fa720d 100644 --- a/.Lib9c.Tests/Action/EndPledgeTest.cs +++ b/.Lib9c.Tests/Action/EndPledgeTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model.State; using Nekoyume.Module; @@ -19,7 +20,7 @@ public void Execute(int balance) var patron = new PrivateKey().Address; var agent = new PrivateKey().Address; var context = new ActionContext(); - IWorld states = new World(new MockWorldState()) + IWorld states = new World(MockUtil.MockModernWorldState) .SetLegacyState(agent.GetPledgeAddress(), List.Empty.Add(patron.Serialize()).Add(true.Serialize())); var mead = Currencies.Mead; if (balance > 0) @@ -52,7 +53,7 @@ public void Execute_Throw_Exception(bool invalidSigner, bool invalidAgent, Type Address patron = new PrivateKey().Address; Address agent = new PrivateKey().Address; List contract = List.Empty.Add(patron.Serialize()).Add(true.Serialize()); - IWorld states = new World(new MockWorldState()).SetLegacyState(agent.GetPledgeAddress(), contract); + IWorld states = new World(MockUtil.MockModernWorldState).SetLegacyState(agent.GetPledgeAddress(), contract); var action = new EndPledge { diff --git a/.Lib9c.Tests/Action/EventConsumableItemCraftsTest.cs b/.Lib9c.Tests/Action/EventConsumableItemCraftsTest.cs index dcf9a59df5..72fddc9d3d 100644 --- a/.Lib9c.Tests/Action/EventConsumableItemCraftsTest.cs +++ b/.Lib9c.Tests/Action/EventConsumableItemCraftsTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model; @@ -24,7 +25,7 @@ public class EventConsumableItemCraftsTest public EventConsumableItemCraftsTest() { - _initialStates = new World(new MockWorldState()); + _initialStates = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/Action/EventDungeonBattleTest.cs b/.Lib9c.Tests/Action/EventDungeonBattleTest.cs new file mode 100644 index 0000000000..65113fb626 --- /dev/null +++ b/.Lib9c.Tests/Action/EventDungeonBattleTest.cs @@ -0,0 +1,493 @@ +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.Mocks; + 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.Module; + using Nekoyume.TableData; + using Nekoyume.TableData.Event; + using Xunit; + + public class EventDungeonBattleTest + { + private readonly Currency _ncgCurrency; + private readonly TableSheets _tableSheets; + + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private IWorld _initialStates; + + public EventDungeonBattleTest() + { + _initialStates = new World(MockUtil.MockModernWorldState); + +#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.SetLegacyState( + GoldCurrencyState.Address, + new GoldCurrencyState(_ncgCurrency).Serialize()); + var sheets = TableSheetsImporter.ImportSheets(); + foreach (var (key, value) in sheets) + { + _initialStates = _initialStates + .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + + _tableSheets = new TableSheets(sheets); + + _agentAddress = new PrivateKey().Address; + _avatarAddress = _agentAddress.Derive("avatar"); + + 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 + .SetAgentState(_agentAddress, agentState) + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(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.GetLegacyState(eventDungeonInfoAddr)); + Assert.Equal( + scheduleRow.DungeonTicketsMax - 1, + eventDungeonInfo.RemainingTickets); + + contextBlockIndex = scheduleRow.DungeonEndBlockIndex; + nextStates = Execute( + _initialStates, + eventScheduleId, + eventDungeonId, + eventDungeonStageId, + blockIndex: contextBlockIndex); + eventDungeonInfo = + new EventDungeonInfo(nextStates.GetLegacyState(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.SetLegacyState( + Addresses.GetSheetAddress(), + sb.ToString().Serialize()); + + var eventDungeonInfoAddr = + EventDungeonInfo.DeriveAddress(_avatarAddress, eventDungeonId); + var eventDungeonInfo = new EventDungeonInfo( + remainingTickets: 0, + numberOfTicketPurchases: numberOfTicketPurchases); + previousStates = previousStates.SetLegacyState( + 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.GetLegacyState(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 + .SetLegacyState(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 + .SetLegacyState(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.SetLegacyState( + 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.GetLegacyState(eventDungeonInfoAddr)); + Assert.Equal( + scheduleRow.DungeonTicketsMax - 1, + eventDungeonInfo.RemainingTickets); + + contextBlockIndex = scheduleRow.DungeonEndBlockIndex; + nextStates = Execute( + _initialStates, + eventScheduleId, + eventDungeonId, + eventDungeonStageId, + blockIndex: contextBlockIndex); + eventDungeonInfo = + new EventDungeonInfo(nextStates.GetLegacyState(eventDungeonInfoAddr)); + Assert.Equal( + scheduleRow.DungeonTicketsMax - 1, + eventDungeonInfo.RemainingTickets); + } + + private IWorld Execute( + IWorld 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.GetAvatarState(_avatarAddress); + var equipments = + Doomfist.GetAllParts(_tableSheets, previousAvatarState.level); + foreach (var equipment in equipments) + { + previousAvatarState.inventory.AddItem(equipment, iLock: null); + } + + var action = new EventDungeonBattle + { + 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.GetAvatarState(_avatarAddress); + var expectExp = scheduleRow.GetStageExp( + eventDungeonStageId.ToEventDungeonStageNumber()); + Assert.Equal( + previousAvatarState.exp + expectExp, + nextAvatarState.exp); + + return nextStates; + } + } +} diff --git a/.Lib9c.Tests/Action/EventMaterialItemCraftsTest.cs b/.Lib9c.Tests/Action/EventMaterialItemCraftsTest.cs index e29c0b5779..82a9cc4169 100644 --- a/.Lib9c.Tests/Action/EventMaterialItemCraftsTest.cs +++ b/.Lib9c.Tests/Action/EventMaterialItemCraftsTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Exceptions; @@ -27,7 +28,7 @@ public class EventMaterialItemCraftsTest public EventMaterialItemCraftsTest() { - _initialStates = new World(new MockWorldState()); + _initialStates = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/Action/Grinding0Test.cs b/.Lib9c.Tests/Action/Grinding0Test.cs deleted file mode 100644 index 47b983cc6a..0000000000 --- a/.Lib9c.Tests/Action/Grinding0Test.cs +++ /dev/null @@ -1,254 +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.Model.Item; - using Nekoyume.Model.Mail; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.TableData; - using Nekoyume.TableData.Crystal; - using Xunit; - using static Lib9c.SerializeKeys; - - public class Grinding0Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly AgentState _agentState; - private readonly AvatarState _avatarState; - private readonly Currency _crystalCurrency; - private readonly Currency _ncgCurrency; - private readonly IWorld _initialState; - - public Grinding0Test() - { - _random = new TestRandom(); - var 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 - _crystalCurrency = Currency.Legacy("CRYSTAL", 18, null); -#pragma warning restore CS0618 - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - - _agentState = new AgentState(_agentAddress); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - _agentState.avatarAddresses.Add(0, _avatarAddress); - -#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 - var goldCurrencyState = new GoldCurrencyState(_ncgCurrency); - - _initialState = new World(new MockWorldState()) - .SetLegacyState( - Addresses.GetSheetAddress(), - _tableSheets.CrystalMonsterCollectionMultiplierSheet.Serialize()) - .SetLegacyState( - Addresses.GetSheetAddress(), - _tableSheets.CrystalEquipmentGrindingSheet.Serialize()) - .SetLegacyState( - Addresses.GetSheetAddress(), - _tableSheets.MaterialItemSheet.Serialize()) - .SetLegacyState( - Addresses.GetSheetAddress(), - _tableSheets.StakeRegularRewardSheet.Serialize()) - .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) - .SetLegacyState(Addresses.GameConfig, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(true, true, 120, false, false, true, 1, 0, false, false, false, 0, 10, 1, null)] - [InlineData(true, true, 120, false, false, true, 1, 2, false, false, false, 0, 40, 1, null)] - // Multiply by StakeState. - [InlineData(true, true, 120, false, false, true, 1, 2, false, true, false, 0, 40, 1, null)] - [InlineData(true, true, 120, false, false, true, 1, 0, false, true, false, 2, 15, 1, null)] - // Multiply by legacy MonsterCollectionState. - [InlineData(true, true, 120, false, false, true, 1, 2, false, false, true, 0, 40, 1, null)] - [InlineData(true, true, 120, false, false, true, 1, 0, false, false, true, 2, 15, 1, null)] - // Charge AP. - [InlineData(true, true, 0, true, true, true, 1, 0, false, false, false, 0, 10, 1, null)] - // Invalid equipment count. - [InlineData(true, true, 120, false, false, true, 1, 2, false, false, true, 0, 200, 0, typeof(InvalidItemCountException))] - [InlineData(true, true, 120, false, false, true, 1, 2, false, false, true, 0, 200, 11, typeof(InvalidItemCountException))] - // AgentState not exist. - [InlineData(false, true, 120, false, false, false, 1, 0, false, false, false, 0, 0, 1, typeof(FailedLoadStateException))] - // AvatarState not exist. - [InlineData(true, false, 120, false, false, false, 1, 0, false, false, false, 0, 0, 1, typeof(FailedLoadStateException))] - // Required more ActionPoint. - [InlineData(true, true, 0, false, false, false, 1, 0, false, false, false, 0, 0, 1, typeof(NotEnoughActionPointException))] - // Failed Charge AP. - [InlineData(true, true, 0, true, false, false, 1, 0, false, false, false, 0, 100, 1, typeof(NotEnoughMaterialException))] - // Equipment not exist. - [InlineData(true, true, 120, false, false, false, 1, 0, false, false, false, 0, 0, 1, typeof(ItemDoesNotExistException))] - // Locked equipment. - [InlineData(true, true, 120, false, false, true, 100, 0, false, false, false, 0, 0, 1, typeof(RequiredBlockIndexException))] - public void Execute( - bool agentExist, - bool avatarExist, - int ap, - bool chargeAp, - bool apStoneExist, - bool equipmentExist, - long requiredBlockIndex, - int itemLevel, - bool equipped, - bool stake, - bool monsterCollect, - int monsterCollectLevel, - int totalAsset, - int equipmentCount, - Type exc - ) - { - var context = new ActionContext(); - var state = _initialState; - if (agentExist) - { - state = state.SetAgentState(_agentAddress, _agentState); - } - - if (avatarExist) - { - _avatarState.actionPoint = ap; - - if (equipmentExist) - { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, requiredBlockIndex, itemLevel); - equipment.equipped = equipped; - _avatarState.inventory.AddItem(equipment, count: 1); - } - else - { - var row = _tableSheets.ConsumableItemSheet.Values.First(r => r.Grade == 1); - var consumable = (Consumable)ItemFactory.CreateItemUsable(row, default, requiredBlockIndex, itemLevel); - _avatarState.inventory.AddItem(consumable, count: 1); - } - - if (chargeAp && apStoneExist) - { - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - var apStone = ItemFactory.CreateMaterial(row); - _avatarState.inventory.AddItem(apStone); - } - - state = state - .SetAvatarState(_avatarAddress, _avatarState); - - Assert.Equal(0 * _crystalCurrency, state.GetBalance(_avatarAddress, _crystalCurrency)); - } - - if (stake || monsterCollect) - { - // StakeState; - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .FirstOrDefault(r => r.Level == monsterCollectLevel)?.RequiredGold ?? 0; - - if (stake) - { - state = state.SetLegacyState(stakeStateAddress, stakeState.SerializeV2()); - - if (requiredGold > 0) - { - state = state.MintAsset( - context, - stakeStateAddress, - requiredGold * _ncgCurrency - ); - } - } - - if (monsterCollect) - { - var mcAddress = MonsterCollectionState.DeriveAddress(_agentAddress, 0); - state = state.SetLegacyState( - mcAddress, - new MonsterCollectionState(mcAddress, monsterCollectLevel, 1).Serialize() - ); - - if (requiredGold > 0) - { - state = state.MintAsset(context, mcAddress, requiredGold * _ncgCurrency); - } - } - } - - var equipmentIds = new List(); - for (int i = 0; i < equipmentCount; i++) - { - equipmentIds.Add(default); - } - - Assert.Equal(equipmentCount, equipmentIds.Count); - - var action = new Grinding0 - { - AvatarAddress = _avatarAddress, - EquipmentIds = equipmentIds, - ChargeAp = chargeAp, - }; - - if (exc is null) - { - var nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - var nextAvatarState = nextState.GetAvatarState(_avatarAddress); - FungibleAssetValue asset = totalAsset * _crystalCurrency; - - Assert.Equal(asset, nextState.GetBalance(_agentAddress, _crystalCurrency)); - Assert.False(nextAvatarState.inventory.HasNonFungibleItem(default)); - Assert.Equal(115, nextAvatarState.actionPoint); - - var mail = nextAvatarState.mailBox.OfType().First(i => i.id.Equals(action.Id)); - - Assert.Equal(1, mail.ItemCount); - Assert.Equal(asset, mail.Asset); - - var row = _tableSheets.MaterialItemSheet.Values.First(r => - r.ItemSubType == ItemSubType.ApStone); - Assert.False(nextAvatarState.inventory.HasItem(row.Id)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/GrindingTest.cs b/.Lib9c.Tests/Action/GrindingTest.cs index 9a8004fa4f..892e1c71c3 100644 --- a/.Lib9c.Tests/Action/GrindingTest.cs +++ b/.Lib9c.Tests/Action/GrindingTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -61,7 +62,7 @@ public GrindingTest() #pragma warning restore CS0618 var goldCurrencyState = new GoldCurrencyState(_ncgCurrency); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState( Addresses.GetSheetAddress(), _tableSheets.CrystalMonsterCollectionMultiplierSheet.Serialize()) diff --git a/.Lib9c.Tests/Action/HackAndSlashRandomBuffTest.cs b/.Lib9c.Tests/Action/HackAndSlashRandomBuffTest.cs index d1d8d3b39d..69894b45dd 100644 --- a/.Lib9c.Tests/Action/HackAndSlashRandomBuffTest.cs +++ b/.Lib9c.Tests/Action/HackAndSlashRandomBuffTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -64,7 +65,7 @@ public HackAndSlashRandomBuffTest() _weeklyArenaState = new WeeklyArenaState(0); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) .SetAgentState(_agentAddress, agentState) .SetAvatarState(_avatarAddress, _avatarState) diff --git a/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs b/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs new file mode 100644 index 0000000000..154542a581 --- /dev/null +++ b/.Lib9c.Tests/Action/HackAndSlashSweepTest.cs @@ -0,0 +1,713 @@ +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.Mocks; + 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.Module; + using Nekoyume.TableData; + using Xunit; + + public class HackAndSlashSweepTest + { + private readonly Dictionary _sheets; + 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 IWorld _initialState; + private readonly IRandom _random; + + public HackAndSlashSweepTest() + { + _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, + }; + 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 World(MockUtil.MockModernWorldState) + .SetLegacyState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) + .SetAgentState(_agentAddress, agentState) + .SetAvatarState(_avatarAddress, _avatarState) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()) + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()); + + foreach (var (key, value) in _sheets) + { + _initialState = _initialState + .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + + foreach (var address in _avatarState.combinationSlotAddresses) + { + var slotState = new CombinationSlotState( + address, + GameConfig.RequireClearedStageLevel.CombinationEquipmentAction); + _initialState = _initialState.SetLegacyState(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); + } + + [Fact] + public void Execute_FailedLoadStateException() + { + var action = new HackAndSlashSweep + { + runeInfos = new List(), + apStoneCount = 1, + avatarAddress = _avatarAddress, + worldId = 1, + stageId = 1, + }; + + IWorld state = new World(MockUtil.MockModernWorldState); + + 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 HackAndSlashSweep + { + runeInfos = new List(), + apStoneCount = 1, + avatarAddress = _avatarAddress, + worldId = worldId, + stageId = stageId, + }; + + var state = _initialState.SetLegacyState( + _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 HackAndSlashSweep + { + runeInfos = new List(), + apStoneCount = 1, + avatarAddress = _avatarAddress, + worldId = worldId, + stageId = stageId, + }; + + var state = _initialState.SetLegacyState( + _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)] + [InlineData(1, 49, 2, 51)] + public void Execute_InvalidStageException(int clearedWorldId, int clearedStageId, int worldId, int stageId) + { + var action = new HackAndSlashSweep + { + 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 + .SetLegacyState( + _avatarAddress.Derive("world_ids"), + List.Empty.Add(worldId.Serialize()) + ) + .SetAvatarState(_avatarAddress, _avatarState); + + Assert.Throws(() => action.Execute(new ActionContext() + { + PreviousState = state, + Signer = _agentAddress, + RandomSeed = 0, + })); + } + + [Theory] + [InlineData(GameConfig.MimisbrunnrWorldId, 10000001, false)] + [InlineData(GameConfig.MimisbrunnrWorldId, 10000001, true)] + // Unlock CRYSTAL first. + [InlineData(2, 51, false)] + public void Execute_InvalidWorldException(int worldId, 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), + }; + + IWorld state = _initialState.SetAvatarState(_avatarAddress, avatarState); + + if (unlockedIdsExist) + { + state = state.SetLegacyState( + _avatarAddress.Derive("world_ids"), + List.Empty.Add(worldId.Serialize()) + ); + } + + var action = new HackAndSlashSweep + { + runeInfos = new List(), + apStoneCount = 1, + avatarAddress = _avatarAddress, + worldId = worldId, + stageId = stageId, + }; + + Assert.Throws(() => action.Execute(new ActionContext() + { + PreviousState = state, + Signer = _agentAddress, + RandomSeed = 0, + })); + } + + [Fact] + public void Execute_UsageLimitExceedException() + { + 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), + }; + + IWorld state = _initialState.SetAvatarState(_avatarAddress, avatarState); + + var action = new HackAndSlashSweep + { + runeInfos = new List(), + apStoneCount = 99, + avatarAddress = _avatarAddress, + worldId = 1, + stageId = 2, + }; + + Assert.Throws(() => action.Execute(new ActionContext() + { + PreviousState = state, + Signer = _agentAddress, + RandomSeed = 0, + })); + } + + [Theory] + [InlineData(3, 2)] + [InlineData(7, 5)] + public void Execute_NotEnoughMaterialException(int useApStoneCount, int holdingApStoneCount) + { + 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); + + IWorld state = _initialState.SetAvatarState(_avatarAddress, avatarState); + + 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 HackAndSlashSweep + { + 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, + })); + } + } + + [Fact] + public void Execute_NotEnoughActionPointException() + { + 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, + }; + + IWorld state = _initialState.SetAvatarState(_avatarAddress, avatarState); + + 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 HackAndSlashSweep + { + 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, + })); + } + } + + [Fact] + public void Execute_PlayCountIsZeroException() + { + 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, + }; + + IWorld state = _initialState.SetAvatarState(_avatarAddress, avatarState); + + 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 HackAndSlashSweep + { + 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, + })); + } + } + + [Fact] + public void Execute_NotEnoughCombatPointException() + { + 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, + }; + + IWorld state = _initialState.SetAvatarState(_avatarAddress, avatarState); + + var stageSheet = _initialState.GetSheet(); + var (expectedLevel, expectedExp) = (0, 0L); + int stageId = 24; + 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 HackAndSlashSweep + { + costumes = new List(), + equipments = new List(), + runeInfos = new List(), + avatarAddress = _avatarAddress, + actionPoint = avatarState.actionPoint, + apStoneCount = 1, + worldId = 1, + 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 + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(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 HackAndSlashSweep + { + 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.GetAvatarState(_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 + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(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 HackAndSlashSweep + { + 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/HackAndSlashTest.cs b/.Lib9c.Tests/Action/HackAndSlashTest.cs index b99f062702..291a3f853c 100644 --- a/.Lib9c.Tests/Action/HackAndSlashTest.cs +++ b/.Lib9c.Tests/Action/HackAndSlashTest.cs @@ -9,6 +9,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -79,7 +80,7 @@ public HackAndSlashTest() var currency = Currency.Legacy("NCG", 2, null); #pragma warning restore CS0618 var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); _initialState = _initialState .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .SetLegacyState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) @@ -411,10 +412,10 @@ public void Execute_Throw_FailedLoadStateException(bool empty) }; IWorld state = empty - ? new World(new MockWorldState()) + ? new World(MockUtil.MockModernWorldState) : _initialState .SetAvatarState(_avatarAddress, _avatarState) - .SetAccount(Addresses.Inventory, new Account(new MockAccountState())); + .SetAccount(Addresses.Inventory, new Account(MockUtil.MockAccountState)); var exec = Assert.Throws(() => action.Execute(new ActionContext { diff --git a/.Lib9c.Tests/Action/InitializeStatesTest.cs b/.Lib9c.Tests/Action/InitializeStatesTest.cs index 07d776ef0f..7af13e7d5f 100644 --- a/.Lib9c.Tests/Action/InitializeStatesTest.cs +++ b/.Lib9c.Tests/Action/InitializeStatesTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -64,7 +65,7 @@ public void Execute() BlockIndex = 0, Signer = minterKey.Address, Miner = default, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); var addresses = new List
() @@ -131,7 +132,7 @@ public void ExecuteWithAuthorizedMinersState() BlockIndex = 0, Miner = default, Signer = minterKey.Address, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); var fetchedState = new AuthorizedMinersState( @@ -180,7 +181,7 @@ public void ExecuteWithActivateAdminKey() BlockIndex = 0, Miner = default, Signer = minterKey.Address, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); var fetchedState = new ActivatedAccountsState( @@ -232,7 +233,7 @@ public void ExecuteWithCredits() BlockIndex = 0, Miner = default, Signer = minterKey.Address, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); var fetchedState = new CreditsState( @@ -278,7 +279,7 @@ public void ExecuteWithoutAdminState() BlockIndex = 0, Miner = default, Signer = minterKey.Address, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); var fetchedState = new ActivatedAccountsState( @@ -321,7 +322,7 @@ public void ExecuteWithoutInitialSupply() BlockIndex = 0, Miner = default, Signer = minterKey.Address, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); Assert.Equal(0 * ncg, genesisState.GetBalance(GoldCurrencyState.Address, ncg)); @@ -362,7 +363,7 @@ public void ExecuteWithAssetMinters() BlockIndex = 0, Miner = default, Signer = minterKey.Address, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }); var assetMinters = Assert.IsType(genesisState.GetLegacyState(Addresses.AssetMinters)); diff --git a/.Lib9c.Tests/Action/IssueTokensFromGarageTest.cs b/.Lib9c.Tests/Action/IssueTokensFromGarageTest.cs index 7ec5e15d7d..ef79bed692 100644 --- a/.Lib9c.Tests/Action/IssueTokensFromGarageTest.cs +++ b/.Lib9c.Tests/Action/IssueTokensFromGarageTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -36,9 +37,8 @@ public IssueTokensFromGarageTest() var garageBalanceAddr = Addresses.GetGarageBalanceAddress(_signer); _prevState = new World( - new MockWorldState() - .SetBalance(garageBalanceAddr, Currencies.Crystal * 1000) - ); + MockWorldState.CreateModern() + .SetBalance(garageBalanceAddr, Currencies.Crystal * 1000)); IEnumerable materials = _tableSheets.MaterialItemSheet.OrderedList! .Take(3) diff --git a/.Lib9c.Tests/Action/ItemEnhancementTest.cs b/.Lib9c.Tests/Action/ItemEnhancementTest.cs new file mode 100644 index 0000000000..82a2f2468b --- /dev/null +++ b/.Lib9c.Tests/Action/ItemEnhancementTest.cs @@ -0,0 +1,367 @@ +namespace Lib9c.Tests.Action +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using Bencodex.Types; + using Lib9c.Tests.Fixtures.TableCSV.Cost; + using Lib9c.Tests.Util; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Mocks; + 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.Module; + using Xunit; + + public class ItemEnhancementTest + { + private readonly TableSheets _tableSheets; + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private readonly AvatarState _avatarState; + private readonly Currency _currency; + private IWorld _initialState; + + public ItemEnhancementTest() + { + _initialState = new World(MockUtil.MockModernWorldState); + 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.SetLegacyState(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 + .SetAgentState(_agentAddress, agentState) + .SetAvatarState(_avatarAddress, _avatarState) + .SetLegacyState(slotAddress, new CombinationSlotState(slotAddress, 0).Serialize()) + .SetLegacyState(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.SetAvatarState(_avatarAddress, _avatarState); + + var action = new ItemEnhancement + { + 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.GetLegacyState(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/JoinArena1Test.cs b/.Lib9c.Tests/Action/JoinArena1Test.cs index 199df55e88..3ef350f8fc 100644 --- a/.Lib9c.Tests/Action/JoinArena1Test.cs +++ b/.Lib9c.Tests/Action/JoinArena1Test.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -46,7 +47,7 @@ public JoinArena1Test(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _state = new World(new MockWorldState()); + _state = new World(MockUtil.MockModernWorldState); _signer = new PrivateKey().Address; _avatarAddress = _signer.Derive("avatar"); diff --git a/.Lib9c.Tests/Action/JoinArena3Test.cs b/.Lib9c.Tests/Action/JoinArena3Test.cs index 9a4ee71222..e5231ea72e 100644 --- a/.Lib9c.Tests/Action/JoinArena3Test.cs +++ b/.Lib9c.Tests/Action/JoinArena3Test.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -47,7 +48,7 @@ public JoinArena3Test(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _state = new World(new MockWorldState()); + _state = new World(MockUtil.MockModernWorldState); _signer = new PrivateKey().Address; _avatarAddress = _signer.Derive("avatar"); diff --git a/.Lib9c.Tests/Action/MarketValidationTest.cs b/.Lib9c.Tests/Action/MarketValidationTest.cs index 4889ee45cf..2c367db871 100644 --- a/.Lib9c.Tests/Action/MarketValidationTest.cs +++ b/.Lib9c.Tests/Action/MarketValidationTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using System.Collections.Generic; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Battle; @@ -23,7 +24,7 @@ public class MarketValidationTest public MarketValidationTest() { - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, new GoldCurrencyState(Gold).Serialize()); } @@ -245,7 +246,7 @@ public void Validate_ProductInfo(params ProductInfosMember[] validateMembers) Assert.Throws(validateMember.Exc, () => buyProduct.Execute(actionContext)); - var cancelRegister = new CancelProductRegistration0 + var cancelRegister = new CancelProductRegistration { AvatarAddress = AvatarAddress, ProductInfos = new List() { productInfo }, diff --git a/.Lib9c.Tests/Action/MigrateAgentAvatarTest.cs b/.Lib9c.Tests/Action/MigrateAgentAvatarTest.cs index befb0fe72d..956b5b3574 100644 --- a/.Lib9c.Tests/Action/MigrateAgentAvatarTest.cs +++ b/.Lib9c.Tests/Action/MigrateAgentAvatarTest.cs @@ -8,6 +8,7 @@ using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -62,21 +63,17 @@ public void MigrateAgentAvatar(int legacyAvatarVersion, bool alreadyMigrated) gameConfigState, default); - MockWorldState mock = new MockWorldState() - .SetState( - ReservedAddresses.LegacyAccount, + IWorld mock = new World(MockUtil.MockModernWorldState) + .SetLegacyState( GoldCurrencyState.Address, new GoldCurrencyState(currency, 0).Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( weekly.address, weekly.Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( Addresses.GoldDistribution, new List()) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( gameConfigState.address, gameConfigState.Serialize()); @@ -84,35 +81,28 @@ public void MigrateAgentAvatar(int legacyAvatarVersion, bool alreadyMigrated) { case 1: mock = mock - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( agentAddress, SerializeLegacyAgent(agentState)) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( avatarAddress, MigrationAvatarState.LegacySerializeV1(avatarState)); break; case 2: mock = mock - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( agentAddress, SerializeLegacyAgent(agentState)) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( avatarAddress, MigrationAvatarState.LegacySerializeV2(avatarState)) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( inventoryAddress, avatarState.inventory.Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( worldInformationAddress, avatarState.questList.Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, + .SetLegacyState( questListAddress, avatarState.questList.Serialize()); break; @@ -122,27 +112,26 @@ public void MigrateAgentAvatar(int legacyAvatarVersion, bool alreadyMigrated) if (alreadyMigrated) { - mock = mock - .SetState( - Addresses.Agent, - agentAddress, - agentState.SerializeList()) - .SetState( - Addresses.Avatar, - avatarAddress, - avatarState.SerializeList()) - .SetState( - Addresses.Inventory, - avatarAddress, - avatarState.inventory.Serialize()) - .SetState( - Addresses.WorldInformation, - avatarAddress, - avatarState.worldInformation.Serialize()) - .SetState( - Addresses.QuestList, - avatarAddress, - avatarState.questList.Serialize()); + mock = mock.SetAccount( + Addresses.Agent, + mock.GetAccount(Addresses.Agent) + .SetState(agentAddress, agentState.SerializeList())); + mock = mock.SetAccount( + Addresses.Avatar, + mock.GetAccount(Addresses.Avatar) + .SetState(avatarAddress, avatarState.SerializeList())); + mock = mock.SetAccount( + Addresses.Inventory, + mock.GetAccount(Addresses.Inventory) + .SetState(avatarAddress, avatarState.inventory.Serialize())); + mock = mock.SetAccount( + Addresses.WorldInformation, + mock.GetAccount(Addresses.WorldInformation) + .SetState(avatarAddress, avatarState.worldInformation.Serialize())); + mock = mock.SetAccount( + Addresses.QuestList, + mock.GetAccount(Addresses.QuestList) + .SetState(avatarAddress, avatarState.questList.Serialize())); } IAction action = new MigrateAgentAvatar @@ -154,11 +143,10 @@ public void MigrateAgentAvatar(int legacyAvatarVersion, bool alreadyMigrated) var actionLoader = new NCActionLoader(); action = actionLoader.LoadAction(123, plainValue); - var states = new World(mock); IWorld nextState = action.Execute( new ActionContext() { - PreviousState = states, + PreviousState = mock, Miner = default, Signer = new Address("e2D18a50472e93d3165c478DefA69fa149214E72"), } diff --git a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs index 4a0fc6c454..257d9c5fa3 100644 --- a/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs +++ b/.Lib9c.Tests/Action/MigrateMonsterCollectionTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -31,7 +32,7 @@ public MigrateMonsterCollectionTest(ITestOutputHelper outputHelper) _signer = default; _avatarAddress = _signer.Derive("avatar"); - _state = new World(new MockWorldState()); + _state = new World(MockUtil.MockModernWorldState); Dictionary sheets = TableSheetsImporter.ImportSheets(); var tableSheets = new TableSheets(sheets); var rankingMapAddress = new PrivateKey().Address; diff --git a/.Lib9c.Tests/Action/MigrationActivatedAccountsStateTest.cs b/.Lib9c.Tests/Action/MigrationActivatedAccountsStateTest.cs index 63fc3b4328..f131917863 100644 --- a/.Lib9c.Tests/Action/MigrationActivatedAccountsStateTest.cs +++ b/.Lib9c.Tests/Action/MigrationActivatedAccountsStateTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model; using Nekoyume.Model.State; @@ -17,10 +18,9 @@ public void Execute() { var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var admin = new Address("8d9f76aF8Dc5A812aCeA15d8bf56E2F790F47fd7"); - var state = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, new AdminState(admin, 100).Serialize()) - .SetState(ReservedAddresses.LegacyAccount, ActivatedAccountsState.Address, new ActivatedAccountsState().AddAccount(default).Serialize())); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, new AdminState(admin, 100).Serialize()) + .SetLegacyState(ActivatedAccountsState.Address, new ActivatedAccountsState().AddAccount(default).Serialize()); var action = new MigrationActivatedAccountsState(); diff --git a/.Lib9c.Tests/Action/MigrationAvatarStateTest.cs b/.Lib9c.Tests/Action/MigrationAvatarStateTest.cs index 3b2e115951..fa11a13926 100644 --- a/.Lib9c.Tests/Action/MigrationAvatarStateTest.cs +++ b/.Lib9c.Tests/Action/MigrationAvatarStateTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model.State; using Nekoyume.Module; @@ -33,10 +34,9 @@ public void Execute() ); var nonce = new byte[] { 0x00, 0x01, 0x02, 0x03 }; var admin = new Address("8d9f76aF8Dc5A812aCeA15d8bf56E2F790F47fd7"); - var state = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, new AdminState(admin, 100).Serialize()) - .SetState(ReservedAddresses.LegacyAccount, avatarAddress, MigrationAvatarState.LegacySerializeV2(avatarState))); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, new AdminState(admin, 100).Serialize()) + .SetLegacyState(avatarAddress, MigrationAvatarState.LegacySerializeV2(avatarState)); var action = new MigrationAvatarState { diff --git a/.Lib9c.Tests/Action/MintAssetsTest.cs b/.Lib9c.Tests/Action/MintAssetsTest.cs index 981ebb1913..23523f397e 100644 --- a/.Lib9c.Tests/Action/MintAssetsTest.cs +++ b/.Lib9c.Tests/Action/MintAssetsTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Libplanet.Types.Tx; using Nekoyume; @@ -38,11 +39,9 @@ public MintAssetsTest() new PrivateKey().Address, new PrivateKey().Address, }; - _prevState = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, new AdminState(_adminAddress, 100).Serialize()) - .SetState(ReservedAddresses.LegacyAccount, Addresses.AssetMinters, new List(_minters.Select(m => m.Serialize()))) - ); + _prevState = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, new AdminState(_adminAddress, 100).Serialize()) + .SetLegacyState(Addresses.AssetMinters, new List(_minters.Select(m => m.Serialize()))); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) diff --git a/.Lib9c.Tests/Action/PatchTableSheetTest.cs b/.Lib9c.Tests/Action/PatchTableSheetTest.cs index ef629fc041..28f8a63912 100644 --- a/.Lib9c.Tests/Action/PatchTableSheetTest.cs +++ b/.Lib9c.Tests/Action/PatchTableSheetTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; @@ -24,7 +25,7 @@ public PatchTableSheetTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -114,13 +115,11 @@ public void CheckPermission() var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); const string tableName = "TestTable"; - var initStates = new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()) + .SetLegacyState( Addresses.TableSheet.Derive(tableName), Dictionary.Empty.Add(tableName, "Initial")); - var state = new World(initStates); var action = new PatchTableSheet() { TableName = tableName, @@ -160,13 +159,11 @@ public void ExecuteNewTable() var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); const string tableName = "TestTable"; - var initStates = new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize()) - .SetState( - ReservedAddresses.LegacyAccount, + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()) + .SetLegacyState( Addresses.TableSheet.Derive(tableName), Dictionary.Empty.Add(tableName, "Initial")); - var state = new World(initStates); var action = new PatchTableSheet() { TableName = nameof(CostumeStatSheet), diff --git a/.Lib9c.Tests/Action/PrepareRewardAssetsTest.cs b/.Lib9c.Tests/Action/PrepareRewardAssetsTest.cs index bf7189ee75..ce2b205f36 100644 --- a/.Lib9c.Tests/Action/PrepareRewardAssetsTest.cs +++ b/.Lib9c.Tests/Action/PrepareRewardAssetsTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using System.Collections.Immutable; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -37,7 +38,7 @@ public void Execute(bool admin, bool includeNcg, Type exc) #pragma warning restore CS0618 } - IWorld state = new World(new MockWorldState()) + IWorld state = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.Admin, adminState.Serialize()); var action = new PrepareRewardAssets(poolAddress, assets); diff --git a/.Lib9c.Tests/Action/RaidTest.cs b/.Lib9c.Tests/Action/RaidTest.cs new file mode 100644 index 0000000000..c46af29cdd --- /dev/null +++ b/.Lib9c.Tests/Action/RaidTest.cs @@ -0,0 +1,627 @@ +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.Mocks; + 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.Stat; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.TableData; + using Xunit; + using static SerializeKeys; + + public class RaidTest + { + private readonly Dictionary _sheets; + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private readonly TableSheets _tableSheets; + private readonly Currency _goldCurrency; + + public RaidTest() + { + _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)] + // 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 Raid + { + 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(); + IWorld state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) + .SetAgentState(_agentAddress, new AgentState(_agentAddress)); + + foreach (var (key, value) in _sheets) + { + state = state.SetLegacyState(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.SetLegacyState(raiderAddress, raiderState.Serialize()); + + var raiderList = new List().Add(raiderAddress.Serialize()); + + if (raiderListExist) + { + raiderList = raiderList.Add(new PrivateKey().Address.Serialize()); + } + + state = state.SetLegacyState(raiderListAddress, raiderList); + } + + if (rewardRecordExist) + { + var rewardRecord = new WorldBossKillRewardRecord + { + [0] = false, + }; + state = state.SetLegacyState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); + } + + if (ncgExist) + { + var row = _tableSheets.WorldBossListSheet.FindRowByBlockIndex(blockIndex); + state = state.MintAsset(context, _agentAddress, (row.TicketPrice + row.AdditionalTicketPrice * purchaseCount) * _goldCurrency); + } + + state = state + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); + } + + if (kill) + { + var bossState = + new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[level]) + { + CurrentHp = 0, + Level = level, + }; + state = state.SetLegacyState(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, + new List()); + 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.TryGetLegacyState(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.TryGetLegacyState(raiderAddress, out List rawRaider)); + var raiderState = new RaiderState(rawRaider); + long 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.TryGetLegacyState(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.TryGetLegacyState(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.TryGetLegacyState(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 Raid + { + AvatarAddress = _avatarAddress, + EquipmentIds = new List(), + CostumeIds = new List(), + FoodIds = new List(), + RuneInfos = new List(), + PayNcg = false, + }; + + var worldBossRow = _tableSheets.WorldBossListSheet.First().Value; + int raidId = worldBossRow.Id; + Address raiderAddress = Addresses.GetRaiderAddress(_avatarAddress, raidId); + var goldCurrencyState = new GoldCurrencyState(_goldCurrency); + Address bossAddress = Addresses.GetWorldBossAddress(raidId); + Address worldBossKillRewardRecordAddress = Addresses.GetWorldBossKillRewardRecordAddress(_avatarAddress, raidId); + + IWorld state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) + .SetAgentState(_agentAddress, new AgentState(_agentAddress)); + + foreach (var (key, value) in _sheets) + { + state = state.SetLegacyState(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.SetLegacyState(raiderAddress, raiderState.Serialize()); + + var rewardRecord = new WorldBossKillRewardRecord + { + [1] = false, + }; + state = state.SetLegacyState(worldBossKillRewardRecordAddress, rewardRecord.Serialize()); + + state = state + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); + + var bossState = + new WorldBossState(worldBossRow, _tableSheets.WorldBossGlobalHpSheet[2]) + { + CurrentHp = 0, + Level = 2, + }; + state = state.SetLegacyState(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, + new List()); + 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.TryGetLegacyState(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.TryGetLegacyState(bossAddress, out List rawBoss)); + var nextBossState = new WorldBossState(rawBoss); + Assert.Equal(3, nextBossState.Level); + Assert.True(nextState.TryGetLegacyState(worldBossKillRewardRecordAddress, out List rawRewardInfo)); + var nextRewardInfo = new WorldBossKillRewardRecord(rawRewardInfo); + Assert.True(nextRewardInfo[1]); + } + + [Fact] + public void Execute_With_Free_Crystal_Fee() + { + var action = new Raid + { + AvatarAddress = _avatarAddress, + EquipmentIds = new List(), + CostumeIds = new List(), + FoodIds = new List(), + RuneInfos = new List(), + PayNcg = false, + }; + Currency crystal = CrystalCalculator.CRYSTAL; + + _sheets[nameof(WorldBossListSheet)] = + "id,boss_id,started_block_index,ended_block_index,fee,ticket_price,additional_ticket_price,max_purchase_count\r\n" + + "1,900002,0,100,0,1,1,40"; + + var goldCurrencyState = new GoldCurrencyState(_goldCurrency); + IWorld state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) + .SetAgentState(_agentAddress, new AgentState(_agentAddress)); + + foreach (var (key, value) in _sheets) + { + state = state.SetLegacyState(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 + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); + + var blockIndex = gameConfigState.WorldBossRequiredInterval; + var randomSeed = 0; + var ctx = new ActionContext + { + BlockIndex = blockIndex, + PreviousState = state, + RandomSeed = randomSeed, + Signer = _agentAddress, + }; + action.Execute(ctx); + } + } +} diff --git a/.Lib9c.Tests/Action/RankingBattleTest.cs b/.Lib9c.Tests/Action/RankingBattleTest.cs index 2bff5d2cfc..e16518da31 100644 --- a/.Lib9c.Tests/Action/RankingBattleTest.cs +++ b/.Lib9c.Tests/Action/RankingBattleTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model; @@ -29,7 +30,7 @@ public class RankingBattleTest public RankingBattleTest(ITestOutputHelper outputHelper) { - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var keys = new List { diff --git a/.Lib9c.Tests/Action/RapidCombinationTest.cs b/.Lib9c.Tests/Action/RapidCombinationTest.cs new file mode 100644 index 0000000000..a81c03df5b --- /dev/null +++ b/.Lib9c.Tests/Action/RapidCombinationTest.cs @@ -0,0 +1,616 @@ +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 Libplanet.Mocks; + 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.Module; + using Nekoyume.TableData; + using Xunit; + using static Lib9c.SerializeKeys; + + public class RapidCombinationTest + { + private readonly IWorld _initialState; + + private readonly TableSheets _tableSheets; + + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + + public RapidCombinationTest() + { + _initialState = new World(MockUtil.MockModernWorldState); + 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.SetLegacyState(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 + .SetLegacyState(Addresses.GameConfig, new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) + .SetAgentState(_agentAddress, agentState) + .SetAvatarState(_avatarAddress, avatarState); + } + + [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.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 + .SetLegacyState(slotAddress, slotState.Serialize()) + .SetAvatarState(_avatarAddress, avatarState); + + var action = new RapidCombination + { + avatarAddress = _avatarAddress, + slotIndex = 0, + }; + + var nextState = action.Execute(new ActionContext + { + PreviousState = tempState, + Signer = _agentAddress, + BlockIndex = 51, + }); + + 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(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 + .SetLegacyState(slotAddress, slotState.Serialize()); + + var action = new RapidCombination + { + 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 + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(slotAddress, slotState.Serialize()); + + var action = new RapidCombination + { + 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 + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(slotAddress, slotState.Serialize()); + + var action = new RapidCombination + { + 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 + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState(slotAddress, slotState.Serialize()); + + var action = new RapidCombination + { + 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.SetLegacyState(slotAddress, slotState.Serialize()) + .SetAvatarState(_avatarAddress, avatarState); + + var action = new RapidCombination + { + avatarAddress = _avatarAddress, + slotIndex = 0, + }; + + action.Execute(new ActionContext + { + PreviousState = tempState, + Signer = _agentAddress, + BlockIndex = 51, + }); + } + } +} diff --git a/.Lib9c.Tests/Action/ReRegisterProduct0Test.cs b/.Lib9c.Tests/Action/ReRegisterProduct0Test.cs index 6b1d315fea..c200602f7e 100644 --- a/.Lib9c.Tests/Action/ReRegisterProduct0Test.cs +++ b/.Lib9c.Tests/Action/ReRegisterProduct0Test.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Lib9c.Model.Order; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -40,7 +41,7 @@ public ReRegisterProduct0Test(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/Action/ReRegisterProductTest.cs b/.Lib9c.Tests/Action/ReRegisterProductTest.cs index b0c1ecabc4..9b74188d4e 100644 --- a/.Lib9c.Tests/Action/ReRegisterProductTest.cs +++ b/.Lib9c.Tests/Action/ReRegisterProductTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Lib9c.Model.Order; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -40,7 +41,7 @@ public ReRegisterProductTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { diff --git a/.Lib9c.Tests/Action/RedeemCodeTest.cs b/.Lib9c.Tests/Action/RedeemCodeTest.cs index e491e300f3..dcc2266601 100644 --- a/.Lib9c.Tests/Action/RedeemCodeTest.cs +++ b/.Lib9c.Tests/Action/RedeemCodeTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action.State; using Libplanet.Common; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -71,7 +72,7 @@ public void Execute(bool backward) #pragma warning restore CS0618 var context = new ActionContext(); - var initialState = new World(new MockWorldState()) + var initialState = new World(MockUtil.MockModernWorldState) .SetAgentState(_agentAddress, agentState) .SetLegacyState(RedeemCodeState.Address, prevRedeemCodesState.Serialize()) .SetLegacyState(GoldCurrencyState.Address, goldState.Serialize()) diff --git a/.Lib9c.Tests/Action/RegisterProduct0Test.cs b/.Lib9c.Tests/Action/RegisterProduct0Test.cs index 3acb53a28f..72b261530f 100644 --- a/.Lib9c.Tests/Action/RegisterProduct0Test.cs +++ b/.Lib9c.Tests/Action/RegisterProduct0Test.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -55,7 +56,7 @@ public RegisterProduct0Test() }; agentState.avatarAddresses[0] = AvatarAddress; - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, new GoldCurrencyState(Gold).Serialize()) .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.MaterialItemSheet.Serialize()) .SetLegacyState(Addresses.GameConfig, _gameConfigState.Serialize()) diff --git a/.Lib9c.Tests/Action/RegisterProductTest.cs b/.Lib9c.Tests/Action/RegisterProductTest.cs index d9c92299dd..9ee04961cf 100644 --- a/.Lib9c.Tests/Action/RegisterProductTest.cs +++ b/.Lib9c.Tests/Action/RegisterProductTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -55,7 +56,7 @@ public RegisterProductTest() }; agentState.avatarAddresses[0] = AvatarAddress; - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, new GoldCurrencyState(Gold).Serialize()) .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.MaterialItemSheet.Serialize()) .SetLegacyState(Addresses.GameConfig, _gameConfigState.Serialize()) diff --git a/.Lib9c.Tests/Action/RenewAdminStateTest.cs b/.Lib9c.Tests/Action/RenewAdminStateTest.cs index a43b873bcc..b41e36146f 100644 --- a/.Lib9c.Tests/Action/RenewAdminStateTest.cs +++ b/.Lib9c.Tests/Action/RenewAdminStateTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using System; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model.State; @@ -21,9 +22,8 @@ public RenewAdminStateTest() _adminPrivateKey = new PrivateKey(); _validUntil = 1_500_000L; _adminState = new AdminState(_adminPrivateKey.Address, _validUntil); - _stateDelta = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, Addresses.Admin, _adminState.Serialize())); + _stateDelta = new World(MockUtil.MockModernWorldState) + .SetLegacyState(Addresses.Admin, _adminState.Serialize()); } [Fact] diff --git a/.Lib9c.Tests/Action/RequestPledgeTest.cs b/.Lib9c.Tests/Action/RequestPledgeTest.cs index 71486ac433..77b25fcdb6 100644 --- a/.Lib9c.Tests/Action/RequestPledgeTest.cs +++ b/.Lib9c.Tests/Action/RequestPledgeTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Model.State; @@ -19,7 +20,7 @@ public void Execute(int contractedMead) Currency mead = Currencies.Mead; Address patron = new PrivateKey().Address; var context = new ActionContext(); - IWorld states = new World(new MockWorldState()).MintAsset(context, patron, 2 * mead); + IWorld states = new World(MockUtil.MockModernWorldState).MintAsset(context, patron, 2 * mead); var address = new PrivateKey().Address; var action = new RequestPledge { @@ -50,7 +51,7 @@ public void Execute_Throw_AlreadyContractedException() Address patron = new PrivateKey().Address; var address = new PrivateKey().Address; Address contractAddress = address.GetPledgeAddress(); - IWorld states = new World(new MockWorldState()).SetLegacyState(contractAddress, List.Empty); + IWorld states = new World(MockUtil.MockModernWorldState).SetLegacyState(contractAddress, List.Empty); var action = new RequestPledge { AgentAddress = address, diff --git a/.Lib9c.Tests/Action/RewardGoldTest.cs b/.Lib9c.Tests/Action/RewardGoldTest.cs index 08096bcc43..d97f34d4bf 100644 --- a/.Lib9c.Tests/Action/RewardGoldTest.cs +++ b/.Lib9c.Tests/Action/RewardGoldTest.cs @@ -15,6 +15,7 @@ namespace Lib9c.Tests.Action using Libplanet.Blockchain.Policies; using Libplanet.Blockchain.Renderers; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Store; using Libplanet.Store.Trie; using Libplanet.Types.Assets; @@ -77,7 +78,7 @@ public RewardGoldTest() var gold = new GoldCurrencyState(Currency.Legacy("NCG", 2, null)); #pragma warning restore CS0618 IActionContext context = new ActionContext(); - _baseState = new World(new MockWorldState()) + _baseState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, gold.Serialize()) .SetLegacyState(Addresses.GoldDistribution, GoldDistributionTest.Fixture.Select(v => v.Serialize()).Serialize()) .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000); @@ -585,7 +586,7 @@ public void TransferMead(int patronMead, int balance) var patronAddress = new PrivateKey().Address; var contractAddress = agentAddress.GetPledgeAddress(); IActionContext context = new ActionContext(); - IWorld states = new World(new MockWorldState()) + IWorld states = new World(MockUtil.MockModernWorldState) .MintAsset(context, patronAddress, patronMead * Currencies.Mead) .TransferAsset(context, patronAddress, agentAddress, 1 * Currencies.Mead) .SetLegacyState(contractAddress, List.Empty.Add(patronAddress.Serialize()).Add(true.Serialize()).Add(balance.Serialize())) @@ -607,7 +608,7 @@ public void NoRewardWhenEmptySupply() gameConfigState.Set(_tableSheets.GameConfigSheet); var currency = Currency.Legacy("NCG", 2, null); - IWorld states = new World(new MockWorldState()) + IWorld states = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, new GoldCurrencyState(currency, 0).Serialize()) .SetLegacyState(weekly.address, weekly.Serialize()) .SetLegacyState(Addresses.GoldDistribution, new List()) diff --git a/.Lib9c.Tests/Action/RuneEnhancement0Test.cs b/.Lib9c.Tests/Action/RuneEnhancement0Test.cs deleted file mode 100644 index c80199ccb6..0000000000 --- a/.Lib9c.Tests/Action/RuneEnhancement0Test.cs +++ /dev/null @@ -1,469 +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.Helper; - using Nekoyume.Model.Rune; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.TableData; - using Xunit; - - public class RuneEnhancement0Test - { - private readonly Currency _goldCurrency; - - public RuneEnhancement0Test() - { - _goldCurrency = Currency.Legacy("NCG", 2, null); - } - - [Theory] - [InlineData(10000)] - [InlineData(1)] - public void Execute(int seed) - { - var agentAddress = new PrivateKey().Address; - var avatarAddress = new PrivateKey().Address; - var sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(sheets); - var blockIndex = tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var context = new ActionContext(); - var state = new World(new MockWorldState()) - .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetAgentState(agentAddress, new AgentState(agentAddress)); - - foreach (var (key, value) in sheets) - { - state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var runeListSheet = state.GetSheet(); - var runeId = runeListSheet.First().Value.Id; - var runeStateAddress = RuneState.DeriveAddress(avatarState.address, runeId); - var runeState = new RuneState(runeId); - state = state.SetLegacyState(runeStateAddress, runeState.Serialize()); - - var costSheet = state.GetSheet(); - if (!costSheet.TryGetValue(runeId, out var costRow)) - { - throw new RuneCostNotFoundException($"[{nameof(Execute)}] "); - } - - if (!costRow.TryGetCost(runeState.Level + 1, out var cost)) - { - throw new RuneCostDataNotFoundException($"[{nameof(Execute)}] "); - } - - var runeSheet = state.GetSheet(); - if (!runeSheet.TryGetValue(runeId, out var runeRow)) - { - throw new RuneNotFoundException($"[{nameof(Execute)}] "); - } - - var ncgCurrency = state.GetGoldCurrency(); - var crystalCurrency = CrystalCalculator.CRYSTAL; - var runeCurrency = Currency.Legacy(runeRow.Ticker, 0, minters: null); - - var ncgBal = cost.NcgQuantity * ncgCurrency * 10000; - var crystalBal = cost.CrystalQuantity * crystalCurrency * 10000; - var runeBal = cost.RuneStoneQuantity * runeCurrency * 10000; - - var rand = new TestRandom(seed); - if (!RuneHelper.TryEnhancement(ncgBal, crystalBal, runeBal, ncgCurrency, crystalCurrency, runeCurrency, cost, rand, 99, out var tryCount)) - { - throw new RuneNotFoundException($"[{nameof(Execute)}] "); - } - - if (ncgBal.Sign > 0) - { - state = state.MintAsset(context, agentAddress, ncgBal); - } - - if (crystalBal.Sign > 0) - { - state = state.MintAsset(context, agentAddress, crystalBal); - } - - if (runeBal.Sign > 0) - { - state = state.MintAsset(context, avatarState.address, runeBal); - } - - var action = new RuneEnhancement0() - { - AvatarAddress = avatarState.address, - RuneId = runeId, - TryCount = tryCount, - }; - var ctx = new ActionContext - { - BlockIndex = blockIndex, - PreviousState = state, - RandomSeed = rand.Seed, - Signer = agentAddress, - }; - - var nextState = action.Execute(ctx); - if (!nextState.TryGetLegacyState(runeStateAddress, out List nextRuneRawState)) - { - throw new Exception(); - } - - var nextRunState = new RuneState(nextRuneRawState); - var nextNcgBal = nextState.GetBalance(agentAddress, ncgCurrency); - var nextCrystalBal = nextState.GetBalance(agentAddress, crystalCurrency); - var nextRuneBal = nextState.GetBalance(agentAddress, runeCurrency); - - if (cost.NcgQuantity != 0) - { - Assert.NotEqual(ncgBal, nextNcgBal); - } - - if (cost.CrystalQuantity != 0) - { - Assert.NotEqual(crystalBal, nextCrystalBal); - } - - if (cost.RuneStoneQuantity != 0) - { - Assert.NotEqual(runeBal, nextRuneBal); - } - - var costNcg = tryCount * cost.NcgQuantity * ncgCurrency; - var costCrystal = tryCount * cost.CrystalQuantity * crystalCurrency; - var costRune = tryCount * cost.RuneStoneQuantity * runeCurrency; - - if (costNcg.Sign > 0) - { - nextState = nextState.MintAsset(context, agentAddress, costNcg); - } - - if (costCrystal.Sign > 0) - { - nextState = nextState.MintAsset(context, agentAddress, costCrystal); - } - - if (costRune.Sign > 0) - { - nextState = nextState.MintAsset(context, avatarState.address, costRune); - } - - var finalNcgBal = nextState.GetBalance(agentAddress, ncgCurrency); - var finalCrystalBal = nextState.GetBalance(agentAddress, crystalCurrency); - var finalRuneBal = nextState.GetBalance(avatarState.address, runeCurrency); - Assert.Equal(ncgBal, finalNcgBal); - Assert.Equal(crystalBal, finalCrystalBal); - Assert.Equal(runeBal, finalRuneBal); - Assert.Equal(runeState.Level + 1, nextRunState.Level); - } - - [Fact] - public void Execute_RuneCostNotFoundException() - { - var agentAddress = new PrivateKey().Address; - var avatarAddress = new PrivateKey().Address; - var sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(sheets); - var blockIndex = tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var state = new World(new MockWorldState()) - .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetAgentState(agentAddress, new AgentState(agentAddress)); - - foreach (var (key, value) in sheets) - { - state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var runeListSheet = state.GetSheet(); - var runeId = runeListSheet.First().Value.Id; - var runeStateAddress = RuneState.DeriveAddress(avatarState.address, runeId); - var runeState = new RuneState(128381293); - state = state.SetLegacyState(runeStateAddress, runeState.Serialize()); - var action = new RuneEnhancement0() - { - AvatarAddress = avatarState.address, - RuneId = runeId, - TryCount = 1, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_RuneCostDataNotFoundException() - { - var agentAddress = new PrivateKey().Address; - var avatarAddress = new PrivateKey().Address; - var sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(sheets); - var blockIndex = tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var state = new World(new MockWorldState()) - .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetAgentState(agentAddress, new AgentState(agentAddress)); - - foreach (var (key, value) in sheets) - { - state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var runeListSheet = state.GetSheet(); - var runeId = runeListSheet.First().Value.Id; - var runeStateAddress = RuneState.DeriveAddress(avatarState.address, runeId); - var runeState = new RuneState(runeId); - var costSheet = state.GetSheet(); - if (!costSheet.TryGetValue(runeId, out var costRow)) - { - throw new RuneCostNotFoundException($"[{nameof(Execute)}] "); - } - - for (var i = 0; i < costRow.Cost.Count + 1; i++) - { - runeState.LevelUp(); - } - - state = state.SetLegacyState(runeStateAddress, runeState.Serialize()); - - var action = new RuneEnhancement0() - { - AvatarAddress = avatarState.address, - RuneId = runeId, - TryCount = 1, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - - [Theory] - [InlineData(false, true, true)] - [InlineData(true, true, false)] - [InlineData(true, false, true)] - public void Execute_NotEnoughFungibleAssetValueException(bool ncg, bool crystal, bool rune) - { - var agentAddress = new PrivateKey().Address; - var avatarAddress = new PrivateKey().Address; - var sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(sheets); - var blockIndex = tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var context = new ActionContext(); - var state = new World(new MockWorldState()) - .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetAgentState(agentAddress, new AgentState(agentAddress)); - - foreach (var (key, value) in sheets) - { - state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var runeListSheet = state.GetSheet(); - var runeId = runeListSheet.First().Value.Id; - var runeStateAddress = RuneState.DeriveAddress(avatarState.address, runeId); - var runeState = new RuneState(runeId); - state = state.SetLegacyState(runeStateAddress, runeState.Serialize()); - - var costSheet = state.GetSheet(); - if (!costSheet.TryGetValue(runeId, out var costRow)) - { - throw new RuneCostNotFoundException($"[{nameof(Execute)}] "); - } - - if (!costRow.TryGetCost(runeState.Level + 1, out var cost)) - { - throw new RuneCostDataNotFoundException($"[{nameof(Execute)}] "); - } - - var runeSheet = state.GetSheet(); - if (!runeSheet.TryGetValue(runeId, out var runeRow)) - { - throw new RuneNotFoundException($"[{nameof(Execute)}] "); - } - - var ncgCurrency = state.GetGoldCurrency(); - var crystalCurrency = CrystalCalculator.CRYSTAL; - var runeCurrency = Currency.Legacy(runeRow.Ticker, 0, minters: null); - - if (ncg && cost.NcgQuantity > 0) - { - state = state.MintAsset(context, agentAddress, cost.NcgQuantity * ncgCurrency); - } - - if (crystal && cost.CrystalQuantity > 0) - { - state = state.MintAsset(context, agentAddress, cost.CrystalQuantity * crystalCurrency); - } - - if (rune) - { - state = state.MintAsset(context, avatarState.address, cost.RuneStoneQuantity * runeCurrency); - } - - var action = new RuneEnhancement0() - { - AvatarAddress = avatarState.address, - RuneId = runeId, - TryCount = 1, - }; - var ctx = new ActionContext - { - BlockIndex = blockIndex, - PreviousState = state, - RandomSeed = 0, - Signer = agentAddress, - }; - - if (!ncg && cost.NcgQuantity == 0) - { - return; - } - - if (!crystal && cost.CrystalQuantity == 0) - { - return; - } - - if (!rune && cost.RuneStoneQuantity == 0) - { - return; - } - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - - [Fact] - public void Execute_TryCountIsZeroException() - { - var agentAddress = new PrivateKey().Address; - var avatarAddress = new PrivateKey().Address; - var sheets = TableSheetsImporter.ImportSheets(); - var tableSheets = new TableSheets(sheets); - var blockIndex = tableSheets.WorldBossListSheet.Values - .OrderBy(x => x.StartedBlockIndex) - .First() - .StartedBlockIndex; - - var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var state = new World(new MockWorldState()) - .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) - .SetAgentState(agentAddress, new AgentState(agentAddress)); - - foreach (var (key, value) in sheets) - { - state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); - } - - var avatarState = new AvatarState( - avatarAddress, - agentAddress, - 0, - tableSheets.GetAvatarSheets(), - new GameConfigState(), - default - ); - - var runeListSheet = state.GetSheet(); - var runeId = runeListSheet.First().Value.Id; - var runeStateAddress = RuneState.DeriveAddress(avatarState.address, runeId); - var runeState = new RuneState(runeId); - state = state.SetLegacyState(runeStateAddress, runeState.Serialize()); - - var action = new RuneEnhancement0() - { - AvatarAddress = avatarState.address, - RuneId = runeId, - TryCount = 0, - }; - - Assert.Throws(() => - action.Execute(new ActionContext() - { - PreviousState = state, - Signer = agentAddress, - RandomSeed = 0, - BlockIndex = blockIndex, - })); - } - } -} diff --git a/.Lib9c.Tests/Action/RuneEnhancementTest.cs b/.Lib9c.Tests/Action/RuneEnhancementTest.cs index 16b398d0a4..ec108d4b75 100644 --- a/.Lib9c.Tests/Action/RuneEnhancementTest.cs +++ b/.Lib9c.Tests/Action/RuneEnhancementTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -51,7 +52,7 @@ public void Execute(int seed) ); agentState.avatarAddresses.Add(0, avatarAddress); var context = new ActionContext(); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, agentState) .SetAvatarState(avatarAddress, avatarState); @@ -205,7 +206,7 @@ public void Execute_RuneCostNotFoundException() rankingMapAddress ); agentState.avatarAddresses.Add(0, avatarAddress); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, agentState) .SetAvatarState(avatarAddress, avatarState); @@ -261,7 +262,7 @@ public void Execute_RuneCostDataNotFoundException() rankingMapAddress ); agentState.avatarAddresses.Add(0, avatarAddress); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, agentState) .SetAvatarState(avatarAddress, avatarState); @@ -333,7 +334,7 @@ public void Execute_NotEnoughFungibleAssetValueException(bool ncg, bool crystal, ); agentState.avatarAddresses.Add(0, avatarAddress); var context = new ActionContext(); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, agentState) .SetAvatarState(avatarAddress, avatarState); @@ -448,7 +449,7 @@ public void Execute_TryCountIsZeroException() rankingMapAddress ); agentState.avatarAddresses.Add(0, avatarAddress); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, agentState) .SetAvatarState(avatarAddress, avatarState); @@ -494,7 +495,7 @@ public void Execute_FailedLoadStateException() .StartedBlockIndex; var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, new AgentState(agentAddress)); diff --git a/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs index 5b6262816e..4818449363 100644 --- a/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/ArenaScenarioTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.Scenario using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -36,7 +37,7 @@ public ArenaScenarioTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _state = new World(new MockWorldState()); + _state = new World(MockUtil.MockModernWorldState); _sheets = TableSheetsImporter.ImportSheets(); var tableSheets = new TableSheets(_sheets); diff --git a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs index 49382047cb..2fa35fe85d 100644 --- a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.Scenario using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -50,7 +51,7 @@ public AuraScenarioTest() var skill = SkillFactory.Get(skillRow, 0, 100, 0, StatType.NONE); _aura.Skills.Add(skill); var addresses = new[] { _avatarAddress, _enemyAvatarAddress }; - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); for (int i = 0; i < addresses.Length; i++) { var avatarAddress = addresses[i]; diff --git a/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs index 720f58cc4a..745a8415c4 100644 --- a/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action.Scenario using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -35,7 +36,7 @@ public CollectionScenarioTest() _tableSheets = new TableSheets(_sheets); var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); var addresses = new[] { _avatarAddress, _enemyAvatarAddress }; - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); for (int i = 0; i < addresses.Length; i++) { var avatarAddress = addresses[i]; diff --git a/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs index c89b190833..ac9bb76e65 100644 --- a/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/MarketScenarioTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action.Scenario using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -103,7 +104,7 @@ public MarketScenarioTest(ITestOutputHelper outputHelper) agentState3.avatarAddresses[0] = _buyerAvatarAddress; _currency = Currency.Legacy("NCG", 2, minters: null); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, new GoldCurrencyState(_currency).Serialize()) .SetLegacyState(Addresses.GameConfig, _gameConfigState.Serialize()) .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.MaterialItemSheet.Serialize()) @@ -446,7 +447,7 @@ public void Register_And_Cancel() } Assert.All(new[] { nonFungibleProductId, fungibleProductId, assetProductId }, productId => Assert.NotEqual(default, productId)); - var action2 = new CancelProductRegistration0 + var action2 = new CancelProductRegistration { AvatarAddress = _sellerAvatarAddress, ProductInfos = new List @@ -498,7 +499,9 @@ public void Register_And_Cancel() ); } - Assert.Equal(_gameConfigState.ActionPointMax - RegisterProduct.CostAp - CancelProductRegistration0.CostAp, latestAvatarState.actionPoint); + Assert.Equal( + _gameConfigState.ActionPointMax - RegisterProduct.CostAp - CancelProductRegistration.CostAp, + latestAvatarState.actionPoint); var sellProductList = new ProductsState((List)latestState.GetLegacyState(productsStateAddress)); Assert.Empty(sellProductList.ProductIds); @@ -965,7 +968,7 @@ public void HardFork() }, }; //Cancel - var cancelAction = new CancelProductRegistration0 + var cancelAction = new CancelProductRegistration { AvatarAddress = _sellerAvatarAddress, ProductInfos = productInfos, diff --git a/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs index de83be4c2c..ac56297807 100644 --- a/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/MeadScenarioTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.Scenario using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -20,7 +21,7 @@ public void Contract() Currency mead = Currencies.Mead; var patron = new PrivateKey().Address; IActionContext context = new ActionContext(); - IWorld states = new World(new MockWorldState()).MintAsset(context, patron, 10 * mead); + IWorld states = new World(MockUtil.MockModernWorldState).MintAsset(context, patron, 10 * mead); var agentAddress = new PrivateKey().Address; var requestPledge = new RequestPledge @@ -85,7 +86,7 @@ bool IsTarget(Type type) var action = (IAction)Activator.CreateInstance(typeId)!; var actionContext = new ActionContext { - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), }; try { diff --git a/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs index d6f786bbbe..48cb43d1bb 100644 --- a/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/RuneScenarioTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action.Scenario using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -40,7 +41,7 @@ public void Craft_And_Unlock_And_Equip() ); var context = new ActionContext(); - IWorld initialState = new World(new MockWorldState()) + IWorld initialState = new World(MockUtil.MockModernWorldState) .SetAgentState(agentAddress, agentState) .SetAvatarState(avatarAddress, avatarState) .SetLegacyState( diff --git a/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs b/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs index 5aa2457b93..63186f8a01 100644 --- a/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs +++ b/.Lib9c.Tests/Action/Scenario/SellAndCancellationAndSellTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action.Scenario using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -60,7 +61,7 @@ public SellAndCancellationAndSellTest(ITestOutputHelper outputHelper) GameConfig.RequireClearedStageLevel.ActionsInShop), }; - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, gold.Serialize()) .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()) .SetAgentState(_agentAddress, agentState) diff --git a/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs index c158ccce4c..fbe0dc1065 100644 --- a/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action.Scenario using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -56,7 +57,7 @@ public WorldUnlockScenarioTest() var currency = Currency.Legacy("NCG", 2, null); #pragma warning restore CS0618 var goldCurrencyState = new GoldCurrencyState(currency); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .SetLegacyState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) .SetAgentState(_agentAddress, agentState) diff --git a/.Lib9c.Tests/Action/SecureMiningRewardTest.cs b/.Lib9c.Tests/Action/SecureMiningRewardTest.cs index 1d84b24da6..4ba4db9052 100644 --- a/.Lib9c.Tests/Action/SecureMiningRewardTest.cs +++ b/.Lib9c.Tests/Action/SecureMiningRewardTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using System.Collections.Immutable; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume.Action; using Nekoyume.Model.State; @@ -35,13 +36,13 @@ public class SecureMiningRewardTest }.ToImmutableList(); private static readonly IWorld _previousState = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, new AdminState(_admin, 100).Serialize()) - .SetState(ReservedAddresses.LegacyAccount, GoldCurrencyState.Address, new GoldCurrencyState(NCG).Serialize()) + MockWorldState.CreateModern() .SetBalance(_authMiners[0], NCG * 1000) .SetBalance(_authMiners[1], NCG * 2000) .SetBalance(_authMiners[2], NCG * 3000) - .SetBalance(_authMiners[3], NCG * 4000)); + .SetBalance(_authMiners[3], NCG * 4000)) + .SetLegacyState(AdminState.Address, new AdminState(_admin, 100).Serialize()) + .SetLegacyState(GoldCurrencyState.Address, new GoldCurrencyState(NCG).Serialize()); [Fact] public void Execute() diff --git a/.Lib9c.Tests/Action/SellCancellationTest.cs b/.Lib9c.Tests/Action/SellCancellationTest.cs index 5feb2f10a2..59f84113d7 100644 --- a/.Lib9c.Tests/Action/SellCancellationTest.cs +++ b/.Lib9c.Tests/Action/SellCancellationTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Lib9c.Model.Order; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -38,7 +39,7 @@ public SellCancellationTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -233,7 +234,7 @@ bool fromPreviousAction Signer = _agentAddress, }); - var cancelProductRegistration = new CancelProductRegistration0 + var cancelProductRegistration = new CancelProductRegistration { AvatarAddress = _avatarAddress, ProductInfos = new List diff --git a/.Lib9c.Tests/Action/SellTest.cs b/.Lib9c.Tests/Action/SellTest.cs index d791456cc4..474cbc890c 100644 --- a/.Lib9c.Tests/Action/SellTest.cs +++ b/.Lib9c.Tests/Action/SellTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Action using Lib9c.Model.Order; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -35,7 +36,7 @@ public SellTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -297,7 +298,7 @@ public void Execute_Throw_InvalidOperationException_DueTo_EmptyState() Assert.Throws(() => action.Execute(new ActionContext { BlockIndex = 0, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), Signer = _agentAddress, })); } diff --git a/.Lib9c.Tests/Action/Stake0Test.cs b/.Lib9c.Tests/Action/Stake0Test.cs index dddc15b24a..d47d89be41 100644 --- a/.Lib9c.Tests/Action/Stake0Test.cs +++ b/.Lib9c.Tests/Action/Stake0Test.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -28,7 +29,7 @@ public Stake0Test(ITestOutputHelper outputHelper) .CreateLogger(); var context = new ActionContext(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) diff --git a/.Lib9c.Tests/Action/Stake2Test.cs b/.Lib9c.Tests/Action/Stake2Test.cs index 7fc03212dc..22816c8886 100644 --- a/.Lib9c.Tests/Action/Stake2Test.cs +++ b/.Lib9c.Tests/Action/Stake2Test.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -29,7 +30,7 @@ public Stake2Test(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) diff --git a/.Lib9c.Tests/Action/Summon/AuraSummonTest.cs b/.Lib9c.Tests/Action/Summon/AuraSummonTest.cs index d8a70fd4ec..3b331002dd 100644 --- a/.Lib9c.Tests/Action/Summon/AuraSummonTest.cs +++ b/.Lib9c.Tests/Action/Summon/AuraSummonTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Summon using Lib9c.Tests.Fixtures.TableCSV.Summon; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -55,7 +56,7 @@ public AuraSummonTest() var gold = new GoldCurrencyState(_currency); var context = new ActionContext(); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetAgentState(_agentAddress, agentState) .SetAvatarState(_avatarAddress, _avatarState) .SetLegacyState(GoldCurrencyState.Address, gold.Serialize()) diff --git a/.Lib9c.Tests/Action/Summon/RuneSummonTest.cs b/.Lib9c.Tests/Action/Summon/RuneSummonTest.cs index 8dc111a3cd..7cf03d4e9d 100644 --- a/.Lib9c.Tests/Action/Summon/RuneSummonTest.cs +++ b/.Lib9c.Tests/Action/Summon/RuneSummonTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action.Summon using System.Linq; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -55,7 +56,7 @@ public RuneSummonTest() var gold = new GoldCurrencyState(_currency); var context = new ActionContext(); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetAgentState(_agentAddress, agentState) .SetAvatarState(_avatarAddress, _avatarState) .SetLegacyState(GoldCurrencyState.Address, gold.Serialize()) diff --git a/.Lib9c.Tests/Action/TransferAssetTest.cs b/.Lib9c.Tests/Action/TransferAssetTest.cs index 7c5e8fae63..4ec020e08e 100644 --- a/.Lib9c.Tests/Action/TransferAssetTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -54,7 +55,7 @@ public void Execute() var contractAddress = _sender.Derive(nameof(RequestPledge)); var patronAddress = new PrivateKey().Address; var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_recipient, _currency * 10)); var action = new TransferAsset( @@ -77,7 +78,7 @@ public void Execute() public void Execute_Throw_InvalidTransferSignerException() { var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_recipient, _currency * 10) .SetBalance(_sender, Currencies.Mead * 1)); @@ -107,7 +108,7 @@ public void Execute_Throw_InvalidTransferSignerException() public void Execute_Throw_InvalidTransferRecipientException() { var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_sender, Currencies.Mead * 1)); // Should not allow TransferAsset with same sender and recipient. @@ -135,7 +136,7 @@ public void Execute_Throw_InvalidTransferRecipientException() public void Execute_Throw_InsufficientBalanceException() { IWorld prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_recipient, _currency * 10)); prevState = prevState.SetAgentState(_recipient, new AgentState(_recipient)); @@ -170,7 +171,7 @@ public void Execute_Throw_InvalidTransferMinterException(bool minterAsSender) var currencyBySender = Currency.Legacy("NCG", 2, minter); #pragma warning restore CS0618 var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, currencyBySender * 1000) .SetBalance(_recipient, currencyBySender * 10) .SetBalance(_sender, Currencies.Mead * 1)) @@ -247,10 +248,10 @@ public void Execute_Throw_InvalidTransferCurrencyException() { var crystal = CrystalCalculator.CRYSTAL; var prevState = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, _recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) + MockWorldState.CreateModern() .SetBalance(_sender, crystal * 1000) - .SetBalance(_sender, Currencies.Mead * 1)); + .SetBalance(_sender, Currencies.Mead * 1)) + .SetLegacyState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()); var action = new TransferAsset( sender: _sender, recipient: _recipient, @@ -285,7 +286,7 @@ public void LoadPlainValue_ThrowsMemoLengthOverflowException() public void Execute_Throw_ArgumentException() { var baseState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000)); var action = new TransferAsset( sender: _sender, diff --git a/.Lib9c.Tests/Action/TransferAssetsTest.cs b/.Lib9c.Tests/Action/TransferAssetsTest.cs index 45c828e486..70a3169f44 100644 --- a/.Lib9c.Tests/Action/TransferAssetsTest.cs +++ b/.Lib9c.Tests/Action/TransferAssetsTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -69,7 +70,7 @@ public void Execute() var contractAddress = _sender.Derive(nameof(RequestPledge)); var patronAddress = new PrivateKey().Address; var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_recipient, _currency * 10)); var action = new TransferAssets( @@ -98,7 +99,7 @@ public void Execute() public void Execute_Throw_InvalidTransferSignerException() { var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_recipient, _currency * 10)); var action = new TransferAssets( @@ -129,7 +130,7 @@ public void Execute_Throw_InvalidTransferSignerException() public void Execute_Throw_InvalidTransferRecipientException() { var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000)); // Should not allow TransferAsset with same sender and recipient. var action = new TransferAssets( @@ -158,7 +159,7 @@ public void Execute_Throw_InvalidTransferRecipientException() public void Execute_Throw_InsufficientBalanceException() { var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, _currency * 1000) .SetBalance(_recipient, _currency * 10)) .SetAgentState(_recipient, new AgentState(_recipient)); @@ -192,7 +193,7 @@ public void Execute_Throw_InvalidTransferMinterException() var currencyBySender = Currency.Legacy("NCG", 2, _sender); #pragma warning restore CS0618 var prevState = new World( - new MockWorldState() + MockWorldState.CreateModern() .SetBalance(_sender, currencyBySender * 1000) .SetBalance(_recipient, currencyBySender * 10)) .SetAgentState(_recipient, new AgentState(_recipient)); @@ -332,7 +333,7 @@ public void Execute_Throw_ArgumentOutOfRangeException() { action.Execute(new ActionContext() { - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), Signer = _sender, BlockIndex = 1, }); @@ -344,9 +345,9 @@ public void Execute_Throw_InvalidTransferCurrencyException() { var crystal = CrystalCalculator.CRYSTAL; var prevState = new World( - new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, _recipient.Derive(ActivationKey.DeriveKey), true.Serialize()) - .SetBalance(_sender, crystal * 1000)); + MockWorldState.CreateModern() + .SetBalance(_sender, crystal * 1000)) + .SetLegacyState(_recipient.Derive(ActivationKey.DeriveKey), true.Serialize()); var action = new TransferAssets( sender: _sender, recipients: new List<(Address, FungibleAssetValue)> @@ -366,9 +367,7 @@ public void Execute_Throw_InvalidTransferCurrencyException() [Fact] public void Execute_Throw_ArgumentException() { - var baseState = new World( - new MockWorldState() - .SetBalance(_sender, _currency * 1000)); + var baseState = new World(MockWorldState.CreateModern().SetBalance(_sender, _currency * 1000)); var action = new TransferAssets( sender: _sender, new List<(Address, FungibleAssetValue)> diff --git a/.Lib9c.Tests/Action/UnlockEquipmentRecipe1Test.cs b/.Lib9c.Tests/Action/UnlockEquipmentRecipe1Test.cs deleted file mode 100644 index ab9c910537..0000000000 --- a/.Lib9c.Tests/Action/UnlockEquipmentRecipe1Test.cs +++ /dev/null @@ -1,198 +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.Model.Item; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class UnlockEquipmentRecipe1Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private readonly IWorld _initialState; - - public UnlockEquipmentRecipe1Test() - { - _random = new TestRandom(); - var 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 - _currency = Currency.Legacy("CRYSTAL", 18, null); -#pragma warning restore CS0618 - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - - var agentState = new AgentState(_agentAddress); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new World(new MockWorldState()) - .SetAgentState(_agentAddress, agentState) - .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.EquipmentItemSheet.Serialize()) - .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.EquipmentItemRecipeSheet.Serialize()) - .SetLegacyState(Addresses.GameConfig, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(new[] { 6, 5 }, true, false, true, true, null)] - [InlineData(new[] { 6 }, true, false, true, true, null)] - // Unlock Belt without Armor unlock. - [InlineData(new[] { 94 }, true, false, true, true, null)] - // Unlock Weapon & Ring - [InlineData(new[] { 6, 133 }, true, false, true, true, null)] - // Invalid recipe id. - [InlineData(new[] { -1 }, true, false, false, false, typeof(InvalidRecipeIdException))] - [InlineData(new[] { 1 }, true, false, true, false, typeof(InvalidRecipeIdException))] - [InlineData(new int[] { }, true, false, false, false, typeof(InvalidRecipeIdException))] - // AvatarState is null. - [InlineData(new[] { 6 }, false, false, true, true, typeof(FailedLoadStateException))] - // Already unlocked recipe. - [InlineData(new[] { 6 }, true, true, true, true, typeof(AlreadyRecipeUnlockedException))] - // Skip prev recipe. - [InlineData(new[] { 5 }, true, false, true, false, typeof(InvalidRecipeIdException))] - // Stage not cleared. - [InlineData(new[] { 6 }, true, false, false, true, typeof(NotEnoughClearedStageLevelException))] - // Insufficient CRYSTAL. - [InlineData(new[] { 6 }, true, false, true, false, typeof(NotEnoughFungibleAssetValueException))] - public void Execute( - IEnumerable ids, - bool stateExist, - bool alreadyUnlocked, - bool stageCleared, - bool balanceEnough, - Type exc - ) - { - var context = new ActionContext(); - List recipeIds = ids.ToList(); - var rows = _tableSheets.EquipmentItemRecipeSheet.Values - .Where(r => recipeIds.Contains(r.Id)).ToList(); - var balance = balanceEnough ? rows.Sum(r => r.CRYSTAL) : 1; - var state = _initialState.MintAsset(context, _agentAddress, balance * _currency); - Address unlockedRecipeIdsAddress = _avatarAddress.Derive("recipe_ids"); - if (stateExist) - { - var stage = rows.Any() ? rows.Max(r => r.UnlockStage) : 1; - var worldSheet = _tableSheets.WorldSheet; - var worldId = worldSheet.OrderedList - .Last(r => r.StageBegin <= stage && stage <= r.StageEnd).Id; - var worldInformation = _avatarState.worldInformation; - if (stageCleared) - { - for (int j = 1; j < worldId + 1; j++) - { - for (var i = 1; i < stage + 1; i++) - { - _avatarState.worldInformation.ClearStage( - j, - i, - 0, - _tableSheets.WorldSheet, - _tableSheets.WorldUnlockSheet - ); - } - } - } - else - { - Assert.All(recipeIds, recipeId => worldInformation.IsStageCleared(recipeId)); - } - - if (alreadyUnlocked) - { - var serializedIds = new List(recipeIds.Select(i => i.Serialize())); - state = state.SetLegacyState(unlockedRecipeIdsAddress, serializedIds); - } - - state = state.SetAvatarState(_avatarAddress, _avatarState); - } - - var action = new UnlockEquipmentRecipe1 - { - RecipeIds = recipeIds.ToList(), - AvatarAddress = _avatarAddress, - }; - - if (exc is null) - { - IWorld nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - Assert.True(nextState.TryGetLegacyState(unlockedRecipeIdsAddress, out List rawIds)); - - var unlockedIds = rawIds.ToList(StateExtensions.ToInteger); - - Assert.All(recipeIds, recipeId => Assert.Contains(recipeId, unlockedIds)); - Assert.Equal(0 * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal(balance * _currency, nextState.GetBalance(Addresses.UnlockEquipmentRecipe, _currency)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - })); - } - } - - [Theory] - [InlineData(ItemSubType.Weapon)] - [InlineData(ItemSubType.Armor)] - [InlineData(ItemSubType.Belt)] - [InlineData(ItemSubType.Necklace)] - [InlineData(ItemSubType.Ring)] - public void UnlockedIds(ItemSubType itemSubType) - { - var worldInformation = _avatarState.worldInformation; - var rows = _tableSheets.EquipmentItemRecipeSheet.Values - .Where(i => i.ItemSubType == itemSubType && i.Id != 1 && i.UnlockStage != 999); - - // Clear Stage - for (int i = 0; i < _tableSheets.WorldSheet.Count; i++) - { - var worldRow = _tableSheets.WorldSheet.OrderedList[i]; - for (int v = worldRow.StageBegin; v < worldRow.StageEnd + 1; v++) - { - worldInformation.ClearStage(worldRow.Id, v, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - // Unlock All recipe by ItemSubType - UnlockEquipmentRecipe1.UnlockedIds(_initialState, new PrivateKey().Address, _tableSheets.EquipmentItemRecipeSheet, worldInformation, rows.Select(i => i.Id).ToList()); - } - } -} diff --git a/.Lib9c.Tests/Action/UnlockEquipmentRecipeTest.cs b/.Lib9c.Tests/Action/UnlockEquipmentRecipeTest.cs index d45563ae92..69ab353f07 100644 --- a/.Lib9c.Tests/Action/UnlockEquipmentRecipeTest.cs +++ b/.Lib9c.Tests/Action/UnlockEquipmentRecipeTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -52,7 +53,7 @@ public UnlockEquipmentRecipeTest() agentState.avatarAddresses.Add(0, _avatarAddress); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetAgentState(_agentAddress, agentState) .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.EquipmentItemSheet.Serialize()) .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.EquipmentItemRecipeSheet.Serialize()) diff --git a/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs b/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs index 6491c72a19..215f08f96b 100644 --- a/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs +++ b/.Lib9c.Tests/Action/UnlockRuneSlotTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -35,7 +36,7 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long .StartedBlockIndex; var goldCurrencyState = new GoldCurrencyState(_goldCurrency); - var state = new World(new MockWorldState()) + var state = new World(MockUtil.MockModernWorldState) .SetLegacyState(goldCurrencyState.address, goldCurrencyState.Serialize()) .SetAgentState(agentAddress, new AgentState(agentAddress)); diff --git a/.Lib9c.Tests/Action/UnlockWorld1Test.cs b/.Lib9c.Tests/Action/UnlockWorld1Test.cs deleted file mode 100644 index 628bcc6bf0..0000000000 --- a/.Lib9c.Tests/Action/UnlockWorld1Test.cs +++ /dev/null @@ -1,174 +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.Helper; - using Nekoyume.Model; - using Nekoyume.Model.State; - using Nekoyume.Module; - using Nekoyume.TableData; - using Xunit; - using static Lib9c.SerializeKeys; - - public class UnlockWorld1Test - { - private readonly IRandom _random; - private readonly TableSheets _tableSheets; - private readonly Address _agentAddress; - private readonly Address _avatarAddress; - private readonly AvatarState _avatarState; - private readonly Currency _currency; - private readonly IWorld _initialState; - - public UnlockWorld1Test() - { - _random = new TestRandom(); - var sheets = TableSheetsImporter.ImportSheets(); - _tableSheets = new TableSheets(sheets); - _agentAddress = new PrivateKey().Address; - _avatarAddress = new PrivateKey().Address; - _currency = CrystalCalculator.CRYSTAL; - var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); - - var agentState = new AgentState(_agentAddress); - _avatarState = new AvatarState( - _avatarAddress, - _agentAddress, - 0, - _tableSheets.GetAvatarSheets(), - gameConfigState, - default - ); - - agentState.avatarAddresses.Add(0, _avatarAddress); - - _initialState = new World(new MockWorldState()) - .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.WorldUnlockSheet.Serialize()) - .SetLegacyState(Addresses.GameConfig, gameConfigState.Serialize()); - } - - [Theory] - [InlineData(new[] { 2 }, true, false, true, 500, null)] - // Migration AvatarState. - [InlineData(new[] { 2, 3, 4, 5 }, true, false, true, 153000, null)] - // Try open Yggdrasil. - [InlineData(new[] { 1 }, false, false, true, 0, typeof(InvalidWorldException))] - // Try open Mimisbrunnr. - [InlineData(new[] { GameConfig.MimisbrunnrWorldId }, false, false, true, 0, typeof(InvalidWorldException))] - // Empty WorldId. - [InlineData(new int[] { }, false, false, true, 0, typeof(InvalidWorldException))] - // AvatarState is null. - [InlineData(new[] { 2 }, false, false, true, 0, typeof(FailedLoadStateException))] - // Already unlocked world. - [InlineData(new[] { 2 }, true, true, true, 0, typeof(AlreadyWorldUnlockedException))] - // Skip previous world. - [InlineData(new[] { 3 }, true, false, true, 0, typeof(FailedToUnlockWorldException))] - // Stage not cleared. - [InlineData(new[] { 2 }, true, false, false, 0, typeof(FailedToUnlockWorldException))] - // Insufficient CRYSTAL. - [InlineData(new[] { 2 }, true, false, true, 100, typeof(NotEnoughFungibleAssetValueException))] - public void Execute( - IEnumerable ids, - bool stateExist, - bool alreadyUnlocked, - bool stageCleared, - int balance, - Type exc - ) - { - var context = new ActionContext(); - var state = (balance > 0) - ? _initialState.MintAsset(context, _agentAddress, balance * _currency) - : _initialState; - var worldIds = ids.ToList(); - - if (stateExist) - { - var worldInformation = _avatarState.worldInformation; - if (stageCleared) - { - foreach (var wordId in worldIds) - { - var row = _tableSheets.WorldUnlockSheet.OrderedList.First(r => - r.WorldIdToUnlock == wordId); - var worldRow = _tableSheets.WorldSheet[row.WorldId]; - var prevRow = - _tableSheets.WorldUnlockSheet.OrderedList.FirstOrDefault(r => - r.WorldIdToUnlock == row.WorldId); - // Clear prev world. - if (!(prevRow is null)) - { - var prevWorldRow = _tableSheets.WorldSheet[prevRow.WorldId]; - for (int i = prevWorldRow.StageBegin; i < prevWorldRow.StageEnd + 1; i++) - { - worldInformation.ClearStage(prevWorldRow.Id, i, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - - for (int i = worldRow.StageBegin; i < worldRow.StageEnd + 1; i++) - { - worldInformation.ClearStage(worldRow.Id, i, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); - } - } - } - - state = state.SetAvatarState(_avatarAddress, _avatarState); - } - - var unlockedWorldIdsAddress = _avatarAddress.Derive("world_ids"); - if (alreadyUnlocked) - { - var unlockIds = List.Empty.Add(1.Serialize()); - foreach (var worldId in worldIds) - { - unlockIds = unlockIds.Add(worldId.Serialize()); - } - - state = state.SetLegacyState(unlockedWorldIdsAddress, unlockIds); - } - - var action = new UnlockWorld1 - { - WorldIds = worldIds, - AvatarAddress = _avatarAddress, - }; - - if (exc is null) - { - IWorld nextState = action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - }); - - Assert.True(nextState.TryGetLegacyState(unlockedWorldIdsAddress, out List rawIds)); - - var unlockedIds = rawIds.ToList(StateExtensions.ToInteger); - - Assert.All(worldIds, worldId => Assert.Contains(worldId, unlockedIds)); - Assert.Equal(0 * _currency, nextState.GetBalance(_agentAddress, _currency)); - Assert.Equal(balance * _currency, nextState.GetBalance(Addresses.UnlockWorld, _currency)); - } - else - { - Assert.Throws(exc, () => action.Execute(new ActionContext - { - PreviousState = state, - Signer = _agentAddress, - BlockIndex = 1, - RandomSeed = _random.Seed, - })); - } - } - } -} diff --git a/.Lib9c.Tests/Action/UnlockWorldTest.cs b/.Lib9c.Tests/Action/UnlockWorldTest.cs index 28d57e370a..04bbd92d77 100644 --- a/.Lib9c.Tests/Action/UnlockWorldTest.cs +++ b/.Lib9c.Tests/Action/UnlockWorldTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -50,7 +51,7 @@ public UnlockWorldTest() agentState.avatarAddresses.Add(0, _avatarAddress); - _initialState = new World(new MockWorldState()) + _initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(Addresses.GetSheetAddress(), _tableSheets.WorldUnlockSheet.Serialize()) .SetLegacyState(Addresses.GameConfig, gameConfigState.Serialize()); } diff --git a/.Lib9c.Tests/Action/UpdateSellTest.cs b/.Lib9c.Tests/Action/UpdateSellTest.cs index 3791cc5f1e..944b72a8da 100644 --- a/.Lib9c.Tests/Action/UpdateSellTest.cs +++ b/.Lib9c.Tests/Action/UpdateSellTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Action using Lib9c.Model.Order; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -38,7 +39,7 @@ public UpdateSellTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); var sheets = TableSheetsImporter.ImportSheets(); foreach (var (key, value) in sheets) { @@ -254,7 +255,7 @@ public void Execute_Throw_ListEmptyException() Assert.Throws(() => action.Execute(new ActionContext { BlockIndex = 0, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), Signer = _agentAddress, })); } @@ -279,7 +280,7 @@ public void Execute_Throw_FailedLoadStateException() Assert.Throws(() => action.Execute(new ActionContext { BlockIndex = 0, - PreviousState = new World(new MockWorldState()), + PreviousState = new World(MockUtil.MockModernWorldState), Signer = _agentAddress, })); } diff --git a/.Lib9c.Tests/Action/ValidatorSetOperateTest.cs b/.Lib9c.Tests/Action/ValidatorSetOperateTest.cs index 743242fcd3..73f0cf0a6a 100644 --- a/.Lib9c.Tests/Action/ValidatorSetOperateTest.cs +++ b/.Lib9c.Tests/Action/ValidatorSetOperateTest.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Action using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Consensus; using Nekoyume; using Nekoyume.Action; @@ -27,7 +28,7 @@ public ValidatorSetOperateTest(ITestOutputHelper outputHelper) .WriteTo.TestOutput(outputHelper) .CreateLogger(); - _initialState = new World(new MockWorldState()); + _initialState = new World(MockUtil.MockModernWorldState); _validator = new Validator(new PrivateKey().PublicKey, BigInteger.One); var sheets = TableSheetsImporter.ImportSheets(); @@ -44,9 +45,8 @@ public void CheckPermission() { var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var initStates = new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize()); - var state = new World(initStates); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); var action = ValidatorSetOperate.Append(_validator); var nextState = action.Execute( new ActionContext() @@ -65,9 +65,8 @@ public void CheckPermission_Throws_PermissionDenied() { var adminAddress = new Address("399bddF9F7B6d902ea27037B907B2486C9910730"); var adminState = new AdminState(adminAddress, 100); - var initStates = new MockWorldState() - .SetState(ReservedAddresses.LegacyAccount, AdminState.Address, adminState.Serialize()); - var state = new World(initStates); + var state = new World(MockUtil.MockModernWorldState) + .SetLegacyState(AdminState.Address, adminState.Serialize()); var action = ValidatorSetOperate.Append(_validator); PermissionDeniedException exc1 = Assert.Throws(() => @@ -101,7 +100,7 @@ public void Append_Throws_WhenAlreadyExistValidator() [Fact] public void Update_Throws_WhenDoNotExistValidator() { - var state = new World(new MockWorldState()); + var state = new World(MockUtil.MockModernWorldState); var action = ValidatorSetOperate.Update(_validator); InvalidOperationException exc = Assert.Throws(() => action.Execute(new ActionContext @@ -116,7 +115,7 @@ public void Update_Throws_WhenDoNotExistValidator() [Fact] public void Remove_Throws_WhenDoNotExistValidator() { - var state = new World(new MockWorldState()); + var state = new World(MockUtil.MockModernWorldState); var action = ValidatorSetOperate.Remove(_validator); InvalidOperationException exc = Assert.Throws(() => action.Execute(new ActionContext diff --git a/.Lib9c.Tests/Extensions/SheetsExtensionsTest.cs b/.Lib9c.Tests/Extensions/SheetsExtensionsTest.cs index 99e281627f..b556f78a6e 100644 --- a/.Lib9c.Tests/Extensions/SheetsExtensionsTest.cs +++ b/.Lib9c.Tests/Extensions/SheetsExtensionsTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Extensions using Lib9c.Tests.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Action; using Nekoyume.Extensions; @@ -26,7 +27,7 @@ public class SheetsExtensionsTest public SheetsExtensionsTest() { - _states = new World(new MockWorldState()); + _states = new World(MockUtil.MockModernWorldState); InitSheets( _states, out _sheetNameAndFiles, diff --git a/.Lib9c.Tests/Lib9c.Tests.csproj b/.Lib9c.Tests/Lib9c.Tests.csproj index fb877391a1..d6545f071e 100644 --- a/.Lib9c.Tests/Lib9c.Tests.csproj +++ b/.Lib9c.Tests/Lib9c.Tests.csproj @@ -50,6 +50,7 @@ + diff --git a/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs index aa9554b53a..06ed7a0c5d 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/CombatTest.cs @@ -181,11 +181,10 @@ public void DispelOnUse(int dispelId, int chance, int seed, int[] debuffIdList, [Fact] public void DispelOnDuration_Block() { - const int actionBuffId = 708000; // Dispel with duration var actionBuffSheet = _tableSheets.ActionBuffSheet; // Use Dispel first - var dispel = actionBuffSheet.Values.First(bf => bf.Id == actionBuffId); + var dispel = actionBuffSheet.Values.First(bf => bf.ActionBuffType == ActionBuffType.Dispel); _player.AddBuff(BuffFactory.GetActionBuff(_player.Stats, dispel)); Assert.Single(_player.Buffs); @@ -216,11 +215,10 @@ public void DispelOnDuration_Block() [Fact] public void DispelOnDuration_Affect() { - const int actionBuffId = 708000; // Dispel with duration var actionBuffSheet = _tableSheets.ActionBuffSheet; // Use Dispel first - var dispel = actionBuffSheet.Values.First(bf => bf.Id == actionBuffId); + var dispel = actionBuffSheet.Values.First(bf => bf.ActionBuffType == ActionBuffType.Dispel); _player.AddBuff(BuffFactory.GetActionBuff(_player.Stats, dispel)); Assert.Single(_player.Buffs); diff --git a/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs index 29a80523ef..876eebde7a 100644 --- a/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/Adventure/ShatterStrikeTest.cs @@ -35,7 +35,7 @@ public void Use(int enemyHp, int ratioBp, bool expectedEnemyDead, bool copyChara var gameConfigState = new GameConfigState((Text)_tableSheets.GameConfigSheet.Serialize()); Assert.True( - _tableSheets.SkillSheet.TryGetValue(700011, out var skillRow) + _tableSheets.SkillSheet.TryGetValue(700010, out var skillRow) ); // 700011 is ShatterStrike var shatterStrike = new ShatterStrike(skillRow, 0, 0, ratioBp, StatType.NONE); diff --git a/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs b/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs index 04e42a5aaf..3bd23d47a9 100644 --- a/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs @@ -6,6 +6,7 @@ namespace Lib9c.Tests.Model.Skill.Arena using Nekoyume.Arena; using Nekoyume.Model; using Nekoyume.Model.Buff; + using Nekoyume.Model.Skill; using Nekoyume.Model.Skill.Arena; using Nekoyume.Model.Stat; using Nekoyume.Model.State; @@ -13,8 +14,6 @@ namespace Lib9c.Tests.Model.Skill.Arena public class ArenaCombatTest { - private const int ActionBuffId = 708000; // Dispel with duration - private readonly TableSheets _tableSheets; private readonly AvatarState _avatar1; private readonly AvatarState _avatar2; @@ -48,7 +47,7 @@ public ArenaCombatTest() [Theory] [InlineData(700009, new[] { 600001 })] - [InlineData(700010, new[] { 600001, 704000 })] + [InlineData(700009, new[] { 600001, 704000 })] public void DispelOnUse(int dispelId, int[] debuffIdList) { var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); @@ -122,7 +121,7 @@ public void DispelOnDuration_Block() ); // Use Dispel first - var dispel = _tableSheets.ActionBuffSheet.Values.First(bf => bf.Id == ActionBuffId); + var dispel = _tableSheets.ActionBuffSheet.Values.First(bf => bf.ActionBuffType == ActionBuffType.Dispel); challenger.AddBuff(BuffFactory.GetActionBuff(challenger.Stats, dispel)); Assert.Single(challenger.Buffs); @@ -170,7 +169,7 @@ public void DispelOnDuration_Affect() ); // Use Dispel first - var dispel = _tableSheets.ActionBuffSheet.Values.First(bf => bf.Id == ActionBuffId); + var dispel = _tableSheets.ActionBuffSheet.Values.First(bf => bf.ActionBuffType == ActionBuffType.Dispel); challenger.AddBuff(BuffFactory.GetActionBuff(challenger.Stats, dispel)); Assert.Single(challenger.Buffs); diff --git a/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs index 545852abc8..d3e8109ad5 100644 --- a/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs @@ -81,7 +81,7 @@ public void Use(int hpModifier, int ratioBp, bool expectedEnemyDead) new List() ); - var skillRow = _tableSheets.SkillSheet.OrderedList.First(s => s.Id == 700011); + var skillRow = _tableSheets.SkillSheet.OrderedList.First(s => s.Id == 700010); var shatterStrike = new ArenaShatterStrike(skillRow, 0, 0, ratioBp, StatType.NONE); var used = shatterStrike.Use(challenger, enemy, simulator.Turn, new List()); Assert.Single(used.SkillInfos); diff --git a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs index 8c3b103ca5..37626bee61 100644 --- a/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs +++ b/.Lib9c.Tests/Model/Stake/StakeStateUtilsTest.cs @@ -2,11 +2,11 @@ namespace Lib9c.Tests.Model.Stake { using System; using Bencodex.Types; - using Lib9c.Tests.Action; using Lib9c.Tests.Fixtures.TableCSV; using Lib9c.Tests.Fixtures.TableCSV.Stake; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume; using Nekoyume.Model.Stake; using Nekoyume.Model.State; @@ -32,7 +32,7 @@ public void TryMigrate_Return_False_When_IValue_Null() [Fact] public void TryMigrate_Return_False_When_Staking_State_Null() { - var state = new World(new MockWorldState()); + var state = new World(MockUtil.MockModernWorldState); Assert.False(StakeStateUtils.TryMigrate(state, new PrivateKey().Address, out _)); } @@ -108,7 +108,7 @@ public void TryMigrate_Return_True_With_StakeState( string stakeRegularFixedRewardSheetTableName, string stakeRegularRewardSheetTableName) { - IWorld state = new World(new MockWorldState()); + IWorld state = new World(MockUtil.MockModernWorldState); state = state.SetLegacyState( Addresses.GameConfig, new GameConfigState(GameConfigSheetFixtures.Default).Serialize()); @@ -140,7 +140,7 @@ public void TryMigrate_Return_True_With_StakeStateV2( long startedBlockIndex, long? receivedBlockIndex) { - IWorld state = new World(new MockWorldState()); + IWorld state = new World(MockUtil.MockModernWorldState); state = state.SetLegacyState( Addresses.GameConfig, new GameConfigState(GameConfigSheetFixtures.Default).Serialize()); diff --git a/.Lib9c.Tests/Module/CollectionModuleTest.cs b/.Lib9c.Tests/Module/CollectionModuleTest.cs index b3ea162800..a996886239 100644 --- a/.Lib9c.Tests/Module/CollectionModuleTest.cs +++ b/.Lib9c.Tests/Module/CollectionModuleTest.cs @@ -4,6 +4,7 @@ namespace Lib9c.Tests.Module using Lib9c.Tests.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Nekoyume.Action; using Nekoyume.Model.State; using Nekoyume.Module; @@ -14,7 +15,7 @@ public class CollectionModuleTest [Fact] public void CollectionState() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); var address = new PrivateKey().Address; Assert.Throws(() => states.GetCollectionState(address)); Assert.False(states.TryGetCollectionState(address, out _)); @@ -37,7 +38,7 @@ public void CollectionState() [Fact] public void CollectionStates() { - IWorld states = new World(new MockWorldState()); + IWorld states = new World(MockUtil.MockModernWorldState); var address = new PrivateKey().Address; var address2 = new PrivateKey().Address; var addresses = new[] { address, address2 }; diff --git a/.Lib9c.Tests/Policy/BlockPolicyTest.cs b/.Lib9c.Tests/Policy/BlockPolicyTest.cs index f5591b66cb..f420bec26e 100644 --- a/.Lib9c.Tests/Policy/BlockPolicyTest.cs +++ b/.Lib9c.Tests/Policy/BlockPolicyTest.cs @@ -396,7 +396,6 @@ public void EarnMiningGoldWhenSuccessMining() blockChain.Append(block, GenerateBlockCommit(block, adminPrivateKey)); FungibleAssetValue actualBalance = blockChain .GetWorldState() - .GetAccountState(ReservedAddresses.LegacyAccount) .GetBalance(adminAddress, _currency); FungibleAssetValue expectedBalance = new FungibleAssetValue(_currency, 10, 0); Assert.True(expectedBalance.Equals(actualBalance)); diff --git a/.Lib9c.Tests/TestHelper/BlockChainHelper.cs b/.Lib9c.Tests/TestHelper/BlockChainHelper.cs index 42a9b17e2f..6383494f65 100644 --- a/.Lib9c.Tests/TestHelper/BlockChainHelper.cs +++ b/.Lib9c.Tests/TestHelper/BlockChainHelper.cs @@ -12,6 +12,7 @@ namespace Lib9c.Tests.TestHelper using Libplanet.Blockchain; using Libplanet.Blockchain.Policies; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Store; using Libplanet.Store.Trie; using Libplanet.Types.Assets; @@ -99,16 +100,14 @@ public static MakeInitialStateResult MakeInitialState() var sheets = TableSheetsImporter.ImportSheets(); var weeklyArenaAddress = WeeklyArenaState.DeriveAddress(0); var context = new ActionContext(); - var initialState = new World(new MockWorldState()) + var initialState = new World(MockUtil.MockModernWorldState) .SetLegacyState(GoldCurrencyState.Address, goldCurrencyState.Serialize()) .SetLegacyState( Addresses.GoldDistribution, - GoldDistributionTest.Fixture.Select(v => v.Serialize()).Serialize() - ) + GoldDistributionTest.Fixture.Select(v => v.Serialize()).Serialize()) .SetLegacyState( Addresses.GameConfig, - new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize() - ) + new GameConfigState(sheets[nameof(GameConfigSheet)]).Serialize()) .SetLegacyState(Addresses.Ranking, ranking.Serialize()) .SetLegacyState(weeklyArenaAddress, new WeeklyArenaState(0).Serialize()); diff --git a/.Lib9c.Tests/Util/InitializeUtil.cs b/.Lib9c.Tests/Util/InitializeUtil.cs index 8d3490af88..4bc3089055 100644 --- a/.Lib9c.Tests/Util/InitializeUtil.cs +++ b/.Lib9c.Tests/Util/InitializeUtil.cs @@ -5,6 +5,7 @@ namespace Lib9c.Tests.Util using Lib9c.Tests.Action; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Mocks; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; @@ -29,9 +30,10 @@ IWorld initialStatesWithAvatarStateV2 { adminAddr ??= new PrivateKey().Address; var context = new ActionContext(); - var states = new World(new MockWorldState()).SetLegacyState( - Addresses.Admin, - new AdminState(adminAddr.Value, long.MaxValue).Serialize()); + var states = new World(MockUtil.MockModernWorldState) + .SetLegacyState( + Addresses.Admin, + new AdminState(adminAddr.Value, long.MaxValue).Serialize()); var goldCurrency = Currency.Legacy( "NCG", diff --git a/.Lib9c.Tools/SubCommand/Account.cs b/.Lib9c.Tools/SubCommand/Account.cs index 7eee98c734..ca4ec8408c 100644 --- a/.Lib9c.Tools/SubCommand/Account.cs +++ b/.Lib9c.Tools/SubCommand/Account.cs @@ -62,7 +62,6 @@ public void Balance( FungibleAssetValue balance = chain .GetWorldState(offset.Hash) - .GetAccountState(ReservedAddresses.LegacyAccount) .GetBalance(addr, gold); Console.WriteLine("{0}\t{1}", addr, balance); return; @@ -94,7 +93,6 @@ i.GoldDistributions is Bencodex.Types.List l FungibleAssetValue balance = chain .GetWorldState(offset.Hash) - .GetAccountState(ReservedAddresses.LegacyAccount) .GetBalance(addr, gold); Console.WriteLine("{0}\t{1}", addr, balance); printed.Add(addr); diff --git a/.Libplanet b/.Libplanet index 1016fbce88..81ddb0a52b 160000 --- a/.Libplanet +++ b/.Libplanet @@ -1 +1 @@ -Subproject commit 1016fbce882309452a45eda1a19c9a8b213801a5 +Subproject commit 81ddb0a52b2ea66034848270f3d8a6630432585b diff --git a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccount.cs b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccount.cs index 69e7860b71..c9471db66b 100644 --- a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccount.cs +++ b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccount.cs @@ -22,21 +22,12 @@ public RemoteAccount(IAccountState baseState) public ITrie Trie => _baseState.Trie; - public FungibleAssetValue GetBalance(Address address, Currency currency) - => _baseState.GetBalance(address, currency); - public IValue? GetState(Address address) => _baseState.GetState(address); public IReadOnlyList GetStates(IReadOnlyList
addresses) => _baseState.GetStates(addresses); - public FungibleAssetValue GetTotalSupply(Currency currency) - => _baseState.GetTotalSupply(currency); - - public ValidatorSet GetValidatorSet() - => _baseState.GetValidatorSet(); - public IAccount MintAsset(IActionContext context, Address recipient, FungibleAssetValue value) { throw new NotSupportedException(); diff --git a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccountState.cs b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccountState.cs index c9ac2bb574..dd4aef008f 100644 --- a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccountState.cs +++ b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteAccountState.cs @@ -8,9 +8,7 @@ using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store.Trie; -using Libplanet.Types.Assets; using Libplanet.Types.Blocks; -using Libplanet.Types.Consensus; namespace Libplanet.Extensions.RemoteBlockChainStates; @@ -121,131 +119,6 @@ public RemoteAccountState( return response.Data.StateQuery.States is { } state ? codec.Decode(state) : null; } - public FungibleAssetValue GetBalance(Address address, Currency currency) - { - object? currencyInput = currency.TotalSupplyTrackable ? new - { - ticker = currency.Ticker, - decimalPlaces = currency.DecimalPlaces, - minters = currency.Minters?.Select(addr => addr.ToString()).ToArray(), - totalSupplyTrackable = currency.TotalSupplyTrackable, - maximumSupplyMajorUnit = currency.MaximumSupply.Value.MajorUnit, - maximumSupplyMinorUnit = currency.MaximumSupply.Value.MinorUnit, - } : new - { - ticker = currency.Ticker, - decimalPlaces = currency.DecimalPlaces, - minters = currency.Minters?.Select(addr => addr.ToString()).ToArray(), - totalSupplyTrackable = currency.TotalSupplyTrackable, - }; - var response = _graphQlHttpClient.SendQueryAsync( - new GraphQLRequest( - @"query GetBalance( - $owner: Address!, - $currency: CurrencyInput!, - $accountStateRootHash: HashDigest_SHA256!) - { - stateQuery - { - balance( - owner: $owner, - currency: $currency, - accountStateRootHash: $accountStateRootHash) - { - string - } - } - }", - operationName: "GetBalance", - variables: new - { - owner = address.ToString(), - currency = currencyInput, - accountStateRootHash = Trie.Hash is { } accountSrh - ? ByteUtil.Hex(accountSrh.ByteArray) - : null, - })).Result; - - return FungibleAssetValue.Parse(currency, response.Data.StateQuery.Balance.String.Split()[0]); - } - - public FungibleAssetValue GetTotalSupply(Currency currency) - { - object? currencyInput = currency.TotalSupplyTrackable ? new - { - ticker = currency.Ticker, - decimalPlaces = currency.DecimalPlaces, - minters = currency.Minters.Select(addr => addr.ToString()).ToArray(), - totalSupplyTrackable = currency.TotalSupplyTrackable, - maximumSupplyMajorUnit = currency.MaximumSupply.Value.MajorUnit, - maximumSupplyMinorUnit = currency.MaximumSupply.Value.MinorUnit, - } : new - { - ticker = currency.Ticker, - decimalPlaces = currency.DecimalPlaces, - minters = currency.Minters.Select(addr => addr.ToString()).ToArray(), - totalSupplyTrackable = currency.TotalSupplyTrackable, - }; - var response = _graphQlHttpClient.SendQueryAsync( - new GraphQLRequest( - @"query GetTotalSupply( - $currency: CurrencyInput!, - $accountStateRootHash: HashDigest_SHA256!) - { - stateQuery - { - totalSupply( - currency: $currency, - offsetBlockHash: $offsetBlockHash - accountStateRootHash: $accountStateRootHash) - { - string - } - } - }", - operationName: "GetTotalSupply", - variables: new - { - currency = currencyInput, - accountStateRootHash = Trie.Hash is { } accountSrh - ? ByteUtil.Hex(accountSrh.ByteArray) - : null, - })).Result; - - return FungibleAssetValue.Parse(currency, response.Data.StateQuery.TotalSupply.String.Split()[0]); - } - - public ValidatorSet GetValidatorSet() - { - var response = _graphQlHttpClient.SendQueryAsync( - new GraphQLRequest( - @"query GetValidators( - $accountStateRootHash: HashDigest_SHA256!) - { - stateQuery - { - validators( - accountStateRootHash: $accountStateRootHash) - { - publicKey - power - } - } - }", - operationName: "GetValidators", - variables: new - { - accountStateRootHash = Trie.Hash is { } accountSrh - ? ByteUtil.Hex(accountSrh.ByteArray) - : null, - })).Result; - - return new ValidatorSet(response.Data.StateQuery.Validators - .Select(x => - new Validator(new PublicKey(ByteUtil.ParseHex(x.PublicKey)), x.Power)) - .ToList()); - } - private class GetAccountStateResponseType { public StateQueryWithAccountStateType StateQuery { get; set; } @@ -270,46 +143,4 @@ private class StateQueryWithStatesType { public byte[] States { get; set; } } - - private class GetBalanceResponseType - { - public StateQueryWithBalanceType StateQuery { get; set; } - } - - private class StateQueryWithBalanceType - { - public FungibleAssetValueWithStringType Balance { get; set; } - } - - private class FungibleAssetValueWithStringType - { - public string String { get; set; } - } - - private class GetTotalSupplyResponseType - { - public StateQueryWithTotalSupplyType StateQuery { get; set; } - } - - private class StateQueryWithTotalSupplyType - { - public FungibleAssetValueWithStringType TotalSupply { get; set; } - } - - private class GetValidatorsResponseType - { - public StateQueryWithValidatorsType StateQuery { get; set; } - } - - private class StateQueryWithValidatorsType - { - public ValidatorType[] Validators { get; set; } - } - - private class ValidatorType - { - public string PublicKey { get; set; } - - public long Power { get; set; } - } } diff --git a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteWorldState.cs b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteWorldState.cs index 4ccffd1619..6d2977b219 100644 --- a/.Libplanet.Extensions.RemoteBlockChainStates/RemoteWorldState.cs +++ b/.Libplanet.Extensions.RemoteBlockChainStates/RemoteWorldState.cs @@ -6,7 +6,9 @@ using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Store.Trie; +using Libplanet.Types.Assets; using Libplanet.Types.Blocks; +using Libplanet.Types.Consensus; namespace Libplanet.Extensions.RemoteBlockChainStates; @@ -77,6 +79,131 @@ public RemoteWorldState(Uri explorerEndpoint, HashDigest? offsetStateRoo public IAccountState GetAccountState(Address address) => new RemoteAccountState(_explorerEndpoint, address, Trie.Hash); + public FungibleAssetValue GetBalance(Address address, Currency currency) + { + object? currencyInput = currency.TotalSupplyTrackable ? new + { + ticker = currency.Ticker, + decimalPlaces = currency.DecimalPlaces, + minters = currency.Minters?.Select(addr => addr.ToString()).ToArray(), + totalSupplyTrackable = currency.TotalSupplyTrackable, + maximumSupplyMajorUnit = currency.MaximumSupply.Value.MajorUnit, + maximumSupplyMinorUnit = currency.MaximumSupply.Value.MinorUnit, + } : new + { + ticker = currency.Ticker, + decimalPlaces = currency.DecimalPlaces, + minters = currency.Minters?.Select(addr => addr.ToString()).ToArray(), + totalSupplyTrackable = currency.TotalSupplyTrackable, + }; + var response = _graphQlHttpClient.SendQueryAsync( + new GraphQLRequest( + @"query GetBalance( + $owner: Address!, + $currency: CurrencyInput!, + $accountStateRootHash: HashDigest_SHA256!) + { + stateQuery + { + balance( + owner: $owner, + currency: $currency, + accountStateRootHash: $accountStateRootHash) + { + string + } + } + }", + operationName: "GetBalance", + variables: new + { + owner = address.ToString(), + currency = currencyInput, + accountStateRootHash = Trie.Hash is { } accountSrh + ? ByteUtil.Hex(accountSrh.ByteArray) + : null, + })).Result; + + return FungibleAssetValue.Parse(currency, response.Data.StateQuery.Balance.String.Split()[0]); + } + + public FungibleAssetValue GetTotalSupply(Currency currency) + { + object? currencyInput = currency.TotalSupplyTrackable ? new + { + ticker = currency.Ticker, + decimalPlaces = currency.DecimalPlaces, + minters = currency.Minters.Select(addr => addr.ToString()).ToArray(), + totalSupplyTrackable = currency.TotalSupplyTrackable, + maximumSupplyMajorUnit = currency.MaximumSupply.Value.MajorUnit, + maximumSupplyMinorUnit = currency.MaximumSupply.Value.MinorUnit, + } : new + { + ticker = currency.Ticker, + decimalPlaces = currency.DecimalPlaces, + minters = currency.Minters.Select(addr => addr.ToString()).ToArray(), + totalSupplyTrackable = currency.TotalSupplyTrackable, + }; + var response = _graphQlHttpClient.SendQueryAsync( + new GraphQLRequest( + @"query GetTotalSupply( + $currency: CurrencyInput!, + $accountStateRootHash: HashDigest_SHA256!) + { + stateQuery + { + totalSupply( + currency: $currency, + offsetBlockHash: $offsetBlockHash + accountStateRootHash: $accountStateRootHash) + { + string + } + } + }", + operationName: "GetTotalSupply", + variables: new + { + currency = currencyInput, + accountStateRootHash = Trie.Hash is { } accountSrh + ? ByteUtil.Hex(accountSrh.ByteArray) + : null, + })).Result; + + return FungibleAssetValue.Parse(currency, response.Data.StateQuery.TotalSupply.String.Split()[0]); + } + + public ValidatorSet GetValidatorSet() + { + var response = _graphQlHttpClient.SendQueryAsync( + new GraphQLRequest( + @"query GetValidators( + $accountStateRootHash: HashDigest_SHA256!) + { + stateQuery + { + validators( + accountStateRootHash: $accountStateRootHash) + { + publicKey + power + } + } + }", + operationName: "GetValidators", + variables: new + { + accountStateRootHash = Trie.Hash is { } accountSrh + ? ByteUtil.Hex(accountSrh.ByteArray) + : null, + })).Result; + + return new ValidatorSet(response.Data.StateQuery.Validators + .Select(x => + new Validator(new PublicKey(ByteUtil.ParseHex(x.PublicKey)), x.Power)) + .ToList()); + } + public class GetWorldStateResponseType { public StateQueryWithWorldStateType StateQuery { get; set; } @@ -93,4 +220,46 @@ public class WorldStateType public bool Legacy { get; set; } } + + private class GetBalanceResponseType + { + public StateQueryWithBalanceType StateQuery { get; set; } + } + + private class StateQueryWithBalanceType + { + public FungibleAssetValueWithStringType Balance { get; set; } + } + + private class FungibleAssetValueWithStringType + { + public string String { get; set; } + } + + private class GetTotalSupplyResponseType + { + public StateQueryWithTotalSupplyType StateQuery { get; set; } + } + + private class StateQueryWithTotalSupplyType + { + public FungibleAssetValueWithStringType TotalSupply { get; set; } + } + + private class GetValidatorsResponseType + { + public StateQueryWithValidatorsType StateQuery { get; set; } + } + + private class StateQueryWithValidatorsType + { + public ValidatorType[] Validators { get; set; } + } + + private class ValidatorType + { + public string PublicKey { get; set; } + + public long Power { get; set; } + } } diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 5d31b60a2d..946749aa62 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -174,7 +174,6 @@ internal IBlockPolicy GetPolicy( { if (blockChain .GetWorldState() - .GetAccountState(ReservedAddresses.LegacyAccount) .GetBalance(MeadConfig.PatronAddress, Currencies.Mead) < 1 * Currencies.Mead) { // Check Activation diff --git a/Lib9c/Action/ActivateAccount0.cs b/Lib9c/Action/ActivateAccount0.cs deleted file mode 100644 index 53090ca922..0000000000 --- a/Lib9c/Action/ActivateAccount0.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("activate_account")] - public class ActivateAccount0 : ActionBase, IActivateAccount - { - public Address PendingAddress { get; private set; } - - public byte[] Signature { get; private set; } - - Address IActivateAccount.PendingAddress => PendingAddress; - byte[] IActivateAccount.Signature => Signature; - - public override IValue PlainValue => Dictionary.Empty - .Add("type_id", "activate_account") - .Add("values", new Dictionary( - new[] - { - new KeyValuePair((Text)"pending_address", PendingAddress.Serialize()), - new KeyValuePair((Text)"signature", (Binary) Signature), - } - )); - - public ActivateAccount0() - { - } - - public ActivateAccount0(Address pendingAddress, byte[] signature) - { - PendingAddress = pendingAddress; - Signature = signature; - } - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - IWorld state = context.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - if (!state.TryGetLegacyState(ActivatedAccountsState.Address, out Dictionary accountsAsDict)) - { - throw new ActivatedAccountsDoesNotExistsException(); - } - if (!state.TryGetLegacyState(PendingAddress, out Dictionary pendingAsDict)) - { - throw new PendingActivationDoesNotExistsException(PendingAddress); - } - - var accounts = new ActivatedAccountsState(accountsAsDict); - var pending = new PendingActivationState(pendingAsDict); - - if (pending.Verify(Signature)) - { - // We left this log message to track activation history. - // Please delete it if we have an API for evaluation results on the Libplanet side. - Log.Information("{pendingAddress} is activated by {signer} now.", pending.address, context.Signer); - return state.SetLegacyState( - ActivatedAccountsState.Address, - accounts.AddAccount(context.Signer).Serialize() - ).SetLegacyState( - pending.address, - new Bencodex.Types.Null() - ); - } - else - { - throw new InvalidSignatureException(pending, Signature); - } - } - - public override void LoadPlainValue(IValue plainValue) - { - var asDict = (Dictionary)((Dictionary)plainValue)["values"]; - PendingAddress = asDict["pending_address"].ToAddress(); - Signature = ((Binary)asDict["signature"]).ToByteArray(); - } - - public Address GetPendingAddress() => PendingAddress; - - public byte[] GetSignature() => Signature; - } -} diff --git a/Lib9c/Action/CancelProductRegistration0.cs b/Lib9c/Action/CancelProductRegistration0.cs deleted file mode 100644 index a7472bdbb8..0000000000 --- a/Lib9c/Action/CancelProductRegistration0.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Bencodex.Types; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.Market; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [ActionObsolete(ActionObsoleteConfig.V200092ObsoleteIndex)] - [ActionType("cancel_product_registration")] - public class CancelProductRegistration0 : GameAction - { - public const int CostAp = 5; - public const int Capacity = 100; - public Address AvatarAddress; - public List ProductInfos; - public bool ChargeAp; - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - IWorld states = context.PreviousState; - - if (!ProductInfos.Any()) - { - throw new ListEmptyException("ProductInfos was empty."); - } - - if (ProductInfos.Count > Capacity) - { - throw new ArgumentOutOfRangeException($"{nameof(ProductInfos)} must be less than or equal {Capacity}."); - } - - foreach (var productInfo in ProductInfos) - { - productInfo.ValidateType(); - if (productInfo.AvatarAddress != AvatarAddress || - productInfo.AgentAddress != context.Signer) - { - throw new InvalidAddressException(); - } - } - - if (!states.TryGetAvatarState(context.Signer, AvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException("failed to load avatar state"); - } - - if (!avatarState.worldInformation.IsStageCleared(GameConfig.RequireClearedStageLevel.ActionsInShop)) - { - avatarState.worldInformation.TryGetLastClearedStageId(out var current); - throw new NotEnoughClearedStageLevelException(AvatarAddress.ToHex(), - GameConfig.RequireClearedStageLevel.ActionsInShop, current); - } - - avatarState.UseAp(CostAp, ChargeAp, states.GetSheet(), context.BlockIndex, states.GetGameConfigState()); - var productsStateAddress = ProductsState.DeriveAddress(AvatarAddress); - ProductsState productsState; - if (states.TryGetLegacyState(productsStateAddress, out List rawProductList)) - { - productsState = new ProductsState(rawProductList); - } - else - { - // cancel order before product registered case. - var marketState = states.TryGetLegacyState(Addresses.Market, out List rawMarketList) - ? rawMarketList - : List.Empty; - productsState = new ProductsState(); - marketState = marketState.Add(AvatarAddress.Serialize()); - states = states.SetLegacyState(Addresses.Market, marketState); - } - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - foreach (var productInfo in ProductInfos) - { - if (productInfo is ItemProductInfo {Legacy: true}) - { - var productType = productInfo.Type; - var orderAddress = Order.DeriveAddress(productInfo.ProductId); - if (!states.TryGetLegacyState(orderAddress, out Dictionary rawOrder)) - { - throw new FailedLoadStateException( - $"{addressesHex} failed to load {nameof(Order)}({orderAddress})."); - } - - var order = OrderFactory.Deserialize(rawOrder); - switch (order) - { - case FungibleOrder _: - if (productInfo.Type == ProductType.NonFungible) - { - throw new InvalidProductTypeException($"FungibleOrder not support {productType}"); - } - - break; - case NonFungibleOrder _: - if (productInfo.Type == ProductType.Fungible) - { - throw new InvalidProductTypeException($"NoneFungibleOrder not support {productType}"); - } - - break; - default: - throw new ArgumentOutOfRangeException(nameof(order)); - } - - states = SellCancellation.Cancel(context, states, avatarState, addressesHex, - order); - } - else - { - states = Cancel(productsState, productInfo, states, avatarState, context); - } - } - - states = states - .SetAvatarState(AvatarAddress, avatarState) - .SetLegacyState(productsStateAddress, productsState.Serialize()); - - return states; - } - - public static IWorld Cancel(ProductsState productsState, IProductInfo productInfo, IWorld states, - AvatarState avatarState, IActionContext context) - { - var productId = productInfo.ProductId; - if (!productsState.ProductIds.Contains(productId)) - { - throw new ProductNotFoundException($"can't find product {productId}"); - } - - productsState.ProductIds.Remove(productId); - - var productAddress = Product.DeriveAddress(productId); - var product = ProductFactory.DeserializeProduct((List) states.GetLegacyState(productAddress)); - product.Validate(productInfo); - - switch (product) - { - case FavProduct favProduct: - states = states.TransferAsset(context, productAddress, avatarState.address, - favProduct.Asset); - break; - case ItemProduct itemProduct: - switch (itemProduct.TradableItem) - { - case Costume costume: - avatarState.UpdateFromAddCostume(costume, true); - break; - case ItemUsable itemUsable: - avatarState.UpdateFromAddItem(itemUsable, true); - break; - case TradableMaterial tradableMaterial: - { - avatarState.UpdateFromAddItem(tradableMaterial, itemProduct.ItemCount, true); - break; - } - } - - break; - default: - throw new ArgumentOutOfRangeException(nameof(product)); - } - - var mail = new ProductCancelMail(context.BlockIndex, productId, context.BlockIndex, productId); - avatarState.Update(mail); - states = states.SetLegacyState(productAddress, Null.Value); - return states; - } - - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["p"] = new List(ProductInfos.Select(p => p.Serialize())), - ["c"] = ChargeAp.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - ProductInfos = plainValue["p"].ToList(s => ProductFactory.DeserializeProductInfo((List) s)); - ChargeAp = plainValue["c"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/CreateAvatar.cs b/Lib9c/Action/CreateAvatar.cs index be34d1f3f2..293eb72828 100644 --- a/Lib9c/Action/CreateAvatar.cs +++ b/Lib9c/Action/CreateAvatar.cs @@ -3,13 +3,16 @@ 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 Libplanet.Crypto; using Nekoyume.Extensions; using Nekoyume.Model.Item; +using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -126,7 +129,7 @@ public override IWorld Execute(IActionContext context) // Avoid NullReferenceException in test var materialItemSheet = ctx.PreviousState.GetSheet(); - avatarState = CreateAvatar0.CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, default); + avatarState = CreateAvatarState(name, avatarAddress, ctx, materialItemSheet, default); if (hair < 0) hair = 0; if (lens < 0) lens = 0; @@ -148,11 +151,11 @@ public override IWorld Execute(IActionContext context) // prepare for test when executing on editor mode. var data = TestbedHelper.LoadData("TestbedCreateAvatar"); - states = CreateAvatar0.AddRunesForTest(ctx, avatarAddress, states, data.RuneStoneCount); - states = CreateAvatar0.AddSoulStoneForTest(ctx, avatarAddress, states, data.SoulStoneCount); + states = AddRunesForTest(ctx, avatarAddress, states, data.RuneStoneCount); + states = AddSoulStoneForTest(ctx, avatarAddress, states, data.SoulStoneCount); if (data.AddPet) { - states = CreateAvatar0.AddPetsForTest(avatarAddress, states); + states = AddPetsForTest(avatarAddress, states); } var equipmentSheet = states.GetSheet(); @@ -279,5 +282,174 @@ public static IWorld MintAsset(CreateAvatarFavSheet favSheet, return states; } + + public static AvatarState CreateAvatarState(string name, + Address avatarAddress, + IActionContext ctx, + MaterialItemSheet materialItemSheet, + Address rankingMapAddress) + { + var state = ctx.PreviousState; + var random = ctx.GetRandom(); + var gameConfigState = state.GetGameConfigState(); + var avatarState = new AvatarState( + avatarAddress, + ctx.Signer, + ctx.BlockIndex, + state.GetAvatarSheets(), + gameConfigState, + rankingMapAddress, + name + ); + +#if LIB9C_DEV_EXTENSIONS || UNITY_EDITOR + var data = TestbedHelper.LoadData("TestbedCreateAvatar"); + var costumeItemSheet = ctx.PreviousState.GetSheet(); + var equipmentItemSheet = ctx.PreviousState.GetSheet(); + var consumableItemSheet = ctx.PreviousState.GetSheet(); + + AddItemsForTest( + avatarState: avatarState, + random: random, + costumeItemSheet: costumeItemSheet, + materialItemSheet: materialItemSheet, + equipmentItemSheet: equipmentItemSheet, + consumableItemSheet: consumableItemSheet, + data.MaterialCount, + data.TradableMaterialCount, + data.FoodCount); + + var skillSheet = ctx.PreviousState.GetSheet(); + var optionSheet = ctx.PreviousState.GetSheet(); + + var items = data.CustomEquipmentItems; + foreach (var item in items) + { + AddCustomEquipment( + avatarState: avatarState, + random: random, + skillSheet: skillSheet, + equipmentItemSheet: equipmentItemSheet, + equipmentItemOptionSheet: optionSheet, + // Set level of equipment here. + level: item.Level, + // Set recipeId of target equipment here. + recipeId: item.ID, + // Add optionIds here. + item.OptionIds); + } +#endif + + return avatarState; + } + + private static void AddItemsForTest( + AvatarState avatarState, + IRandom random, + CostumeItemSheet costumeItemSheet, + MaterialItemSheet materialItemSheet, + EquipmentItemSheet equipmentItemSheet, + ConsumableItemSheet consumableItemSheet, + int materialCount, + int tradableMaterialCount, + int foodCount) + { + foreach (var row in costumeItemSheet.OrderedList) + { + avatarState.inventory.AddItem2(ItemFactory.CreateCostume(row, random.GenerateRandomGuid())); + } + + foreach (var row in materialItemSheet.OrderedList) + { + avatarState.inventory.AddItem2(ItemFactory.CreateMaterial(row), materialCount); + + if (row.ItemSubType == ItemSubType.Hourglass || + row.ItemSubType == ItemSubType.ApStone) + { + avatarState.inventory.AddItem2(ItemFactory.CreateTradableMaterial(row), tradableMaterialCount); + } + } + + foreach (var row in equipmentItemSheet.OrderedList.Where(row => + row.Id > GameConfig.DefaultAvatarWeaponId)) + { + var itemId = random.GenerateRandomGuid(); + avatarState.inventory.AddItem2(ItemFactory.CreateItemUsable(row, itemId, default)); + } + + foreach (var row in consumableItemSheet.OrderedList) + { + for (var i = 0; i < foodCount; i++) + { + var itemId = random.GenerateRandomGuid(); + var consumable = (Consumable)ItemFactory.CreateItemUsable(row, itemId, + 0, 0); + avatarState.inventory.AddItem2(consumable); + } + } + } + + private static void AddCustomEquipment( + AvatarState avatarState, + IRandom random, + SkillSheet skillSheet, + EquipmentItemSheet equipmentItemSheet, + EquipmentItemOptionSheet equipmentItemOptionSheet, + int level, + int recipeId, + params int[] optionIds + ) + { + if (!equipmentItemSheet.TryGetValue(recipeId, out var equipmentRow)) + { + return; + } + + var itemId = random.GenerateRandomGuid(); + var equipment = (Equipment)ItemFactory.CreateItemUsable(equipmentRow, itemId, 0, level); + var optionRows = new List(); + foreach (var optionId in optionIds) + { + if (!equipmentItemOptionSheet.TryGetValue(optionId, out var optionRow)) + { + continue; + } + optionRows.Add(optionRow); + } + + AddOption(skillSheet, equipment, optionRows, random); + + avatarState.inventory.AddItem2(equipment); + } + + private static HashSet AddOption( + SkillSheet skillSheet, + Equipment equipment, + IEnumerable optionRows, + IRandom random) + { + var optionIds = new HashSet(); + + foreach (var optionRow in optionRows.OrderBy(r => r.Id)) + { + if (optionRow.StatType != StatType.NONE) + { + var stat = CombinationEquipment5.GetStat(optionRow, random); + equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); + } + else + { + var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); + if (!(skill is null)) + { + equipment.Skills.Add(skill); + } + } + + optionIds.Add(optionRow.Id); + } + + return optionIds; + } } } diff --git a/Lib9c/Action/CreateAvatar0.cs b/Lib9c/Action/CreateAvatar0.cs deleted file mode 100644 index 8b84c3c25d..0000000000 --- a/Lib9c/Action/CreateAvatar0.cs +++ /dev/null @@ -1,372 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using System.Text.RegularExpressions; -using Bencodex.Types; -using Lib9c; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Model.Item; -using Nekoyume.Model.Stat; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; -using Serilog; -using Nekoyume.Helper; - -#if LIB9C_DEV_EXTENSIONS || UNITY_EDITOR -using Lib9c.DevExtensions; -using Lib9c.DevExtensions.Model; -#endif - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("create_avatar")] - public class CreateAvatar0 : GameAction, ICreateAvatarV1 - { - public Address avatarAddress; - public int index; - public int hair; - public int lens; - public int ear; - public int tail; - public string name; - - Address ICreateAvatarV1.AvatarAddress => avatarAddress; - int ICreateAvatarV1.Index => index; - int ICreateAvatarV1.Hair => hair; - int ICreateAvatarV1.Lens => lens; - int ICreateAvatarV1.Ear => ear; - int ICreateAvatarV1.Tail => tail; - string ICreateAvatarV1.Name => name; - - protected override IImmutableDictionary PlainValueInternal => new Dictionary() - { - ["avatarAddress"] = avatarAddress.Serialize(), - ["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) - { - avatarAddress = plainValue["avatarAddress"].ToAddress(); - 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 IWorld Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var random = ctx.GetRandom(); - var states = ctx.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - Log.Warning("{AddressesHex}create_avatar is deprecated. Please use create_avatar2", addressesHex); - - 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 = 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.SetLegacyState(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 - .SetAgentState(ctx.Signer, agentState) - .SetLegacyState(Addresses.Ranking, rankingState.Serialize()) - .SetAvatarState(avatarAddress, avatarState); - } - - public static AvatarState CreateAvatarState(string name, - Address avatarAddress, - IActionContext ctx, - MaterialItemSheet materialItemSheet, - Address rankingMapAddress) - { - var state = ctx.PreviousState; - var random = ctx.GetRandom(); - var gameConfigState = state.GetGameConfigState(); - var avatarState = new AvatarState( - avatarAddress, - ctx.Signer, - ctx.BlockIndex, - state.GetAvatarSheets(), - gameConfigState, - rankingMapAddress, - name - ); - -#if LIB9C_DEV_EXTENSIONS || UNITY_EDITOR - var data = TestbedHelper.LoadData("TestbedCreateAvatar"); - var costumeItemSheet = ctx.PreviousState.GetSheet(); - var equipmentItemSheet = ctx.PreviousState.GetSheet(); - var consumableItemSheet = ctx.PreviousState.GetSheet(); - - AddItemsForTest( - avatarState: avatarState, - random: random, - costumeItemSheet: costumeItemSheet, - materialItemSheet: materialItemSheet, - equipmentItemSheet: equipmentItemSheet, - consumableItemSheet: consumableItemSheet, - data.MaterialCount, - data.TradableMaterialCount, - data.FoodCount); - - var skillSheet = ctx.PreviousState.GetSheet(); - var optionSheet = ctx.PreviousState.GetSheet(); - - var items = data.CustomEquipmentItems; - foreach (var item in items) - { - AddCustomEquipment( - avatarState: avatarState, - random: random, - skillSheet: skillSheet, - equipmentItemSheet: equipmentItemSheet, - equipmentItemOptionSheet: optionSheet, - // Set level of equipment here. - level: item.Level, - // Set recipeId of target equipment here. - recipeId: item.ID, - // Add optionIds here. - item.OptionIds); - } -#endif - - return avatarState; - } - - private static void AddItemsForTest( - AvatarState avatarState, - IRandom random, - CostumeItemSheet costumeItemSheet, - MaterialItemSheet materialItemSheet, - EquipmentItemSheet equipmentItemSheet, - ConsumableItemSheet consumableItemSheet, - int materialCount, - int tradableMaterialCount, - int foodCount) - { - foreach (var row in costumeItemSheet.OrderedList) - { - avatarState.inventory.AddItem2(ItemFactory.CreateCostume(row, random.GenerateRandomGuid())); - } - - foreach (var row in materialItemSheet.OrderedList) - { - avatarState.inventory.AddItem2(ItemFactory.CreateMaterial(row), materialCount); - - if (row.ItemSubType == ItemSubType.Hourglass || - row.ItemSubType == ItemSubType.ApStone) - { - avatarState.inventory.AddItem2(ItemFactory.CreateTradableMaterial(row), tradableMaterialCount); - } - } - - foreach (var row in equipmentItemSheet.OrderedList.Where(row => - row.Id > GameConfig.DefaultAvatarWeaponId)) - { - var itemId = random.GenerateRandomGuid(); - avatarState.inventory.AddItem2(ItemFactory.CreateItemUsable(row, itemId, default)); - } - - foreach (var row in consumableItemSheet.OrderedList) - { - for (var i = 0; i < foodCount; i++) - { - var itemId = random.GenerateRandomGuid(); - var consumable = (Consumable)ItemFactory.CreateItemUsable(row, itemId, - 0, 0); - avatarState.inventory.AddItem2(consumable); - } - } - } - - private static void AddCustomEquipment( - AvatarState avatarState, - IRandom random, - SkillSheet skillSheet, - EquipmentItemSheet equipmentItemSheet, - EquipmentItemOptionSheet equipmentItemOptionSheet, - int level, - int recipeId, - params int[] optionIds - ) - { - if (!equipmentItemSheet.TryGetValue(recipeId, out var equipmentRow)) - { - return; - } - - var itemId = random.GenerateRandomGuid(); - var equipment = (Equipment)ItemFactory.CreateItemUsable(equipmentRow, itemId, 0, level); - var optionRows = new List(); - foreach (var optionId in optionIds) - { - if (!equipmentItemOptionSheet.TryGetValue(optionId, out var optionRow)) - { - continue; - } - optionRows.Add(optionRow); - } - - AddOption(skillSheet, equipment, optionRows, random); - - avatarState.inventory.AddItem2(equipment); - } - - private static HashSet AddOption( - SkillSheet skillSheet, - Equipment equipment, - IEnumerable optionRows, - IRandom random) - { - var optionIds = new HashSet(); - - foreach (var optionRow in optionRows.OrderBy(r => r.Id)) - { - if (optionRow.StatType != StatType.NONE) - { - var stat = CombinationEquipment5.GetStat(optionRow, random); - equipment.StatsMap.AddStatAdditionalValue(stat.StatType, stat.BaseValue); - } - else - { - var skill = CombinationEquipment5.GetSkill(optionRow, skillSheet, random); - if (!(skill is null)) - { - equipment.Skills.Add(skill); - } - } - - optionIds.Add(optionRow.Id); - } - - return optionIds; - } - - public static IWorld AddRunesForTest( - IActionContext context, - Address avatarAddress, - IWorld states, - int count = int.MaxValue) - { - var runeSheet = states.GetSheet(); - foreach (var row in runeSheet.Values) - { - var rune = RuneHelper.ToFungibleAssetValue(row, count); - states = states.MintAsset(context, avatarAddress, rune); - } - return states; - } - - public static IWorld AddSoulStoneForTest( - IActionContext context, - Address avatarAddress, - IWorld states, - int count = int.MaxValue) - { - var petSheet = states.GetSheet(); - foreach (var row in petSheet.Values) - { - var soulStone = Currencies.GetSoulStone(row.SoulStoneTicker) * count; - states = states.MintAsset(context, avatarAddress, soulStone); - } - return states; - } - - public static IWorld AddPetsForTest( - Address avatarAddress, - IWorld states) - { - var petSheet = states.GetSheet(); - foreach (var id in petSheet.Keys) - { - var petState = new PetState(id); - petState.LevelUp(); - var petStateAddress = PetState.DeriveAddress(avatarAddress, id); - states = states.SetLegacyState(petStateAddress, petState.Serialize()); - } - - return states; - } - } -} diff --git a/Lib9c/Action/Grinding0.cs b/Lib9c/Action/Grinding0.cs deleted file mode 100644 index e048888820..0000000000 --- a/Lib9c/Action/Grinding0.cs +++ /dev/null @@ -1,168 +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.Extensions; -using Nekoyume.Helper; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; -using Nekoyume.TableData.Crystal; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [ActionType("grinding")] - public class Grinding0 : GameAction, IGrindingV1 - { - public const int CostAp = 5; - public const int Limit = 10; - public Address AvatarAddress; - public List EquipmentIds; - public bool ChargeAp; - - Address IGrindingV1.AvatarAddress => AvatarAddress; - List IGrindingV1.EquipmentsIds => EquipmentIds; - bool IGrindingV1.ChargeAp => ChargeAp; - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - IWorld states = ctx.PreviousState; - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Grinding exec started", addressesHex); - if (!EquipmentIds.Any() || EquipmentIds.Count > Limit) - { - throw new InvalidItemCountException(); - } - - var agentState = states.GetAgentState(ctx.Signer); - if (agentState is null) - { - throw new FailedLoadStateException(""); - } - - if (!states.TryGetAvatarState(ctx.Signer, AvatarAddress, out var avatarState)) - { - throw new FailedLoadStateException(""); - } - - Address monsterCollectionAddress = MonsterCollectionState.DeriveAddress( - context.Signer, - agentState.MonsterCollectionRound - ); - - Dictionary sheets = states.GetSheets(sheetTypes: new[] - { - typeof(CrystalEquipmentGrindingSheet), - typeof(CrystalMonsterCollectionMultiplierSheet), - typeof(MaterialItemSheet), - typeof(StakeRegularRewardSheet) - }); - - Currency currency = states.GetGoldCurrency(); - FungibleAssetValue stakedAmount = states.GetStakedAmount(context.Signer); - if (stakedAmount == currency * 0 && states.TryGetLegacyState(monsterCollectionAddress, out Dictionary _)) - { - stakedAmount = states.GetBalance(monsterCollectionAddress, currency); - } - - if (avatarState.actionPoint < CostAp) - { - switch (ChargeAp) - { - case false: - throw new NotEnoughActionPointException(""); - case true: - { - MaterialItemSheet.Row row = sheets.GetSheet() - .OrderedList - .First(r => r.ItemSubType == ItemSubType.ApStone); - if (!avatarState.inventory.RemoveFungibleItem(row.ItemId, context.BlockIndex)) - { - throw new NotEnoughMaterialException("not enough ap stone."); - } - GameConfigState gameConfigState = states.GetGameConfigState(); - avatarState.actionPoint = gameConfigState.ActionPointMax; - break; - } - } - } - - avatarState.actionPoint -= CostAp; - - List equipmentList = new List(); - foreach (var equipmentId in EquipmentIds) - { - if(avatarState.inventory.TryGetNonFungibleItem(equipmentId, out Equipment equipment)) - { - if (equipment.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException($"{equipment.ItemSubType} / unlock on {equipment.RequiredBlockIndex}"); - } - } - else - { - // Invalid Item Type. - throw new ItemDoesNotExistException($"Can't find Equipment. {equipmentId}"); - } - - if (!avatarState.inventory.RemoveNonFungibleItem(equipmentId)) - { - throw new ItemDoesNotExistException($"Can't find Equipment. {equipmentId}"); - } - equipmentList.Add(equipment); - } - - FungibleAssetValue crystal = CrystalCalculator.CalculateCrystal( - context.Signer, - equipmentList, - stakedAmount, - false, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() - ); - - var mail = new GrindingMail( - ctx.BlockIndex, - Id, - ctx.BlockIndex, - EquipmentIds.Count, - crystal - ); - avatarState.Update(mail); - - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}Grinding Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states - .SetAvatarState(AvatarAddress, avatarState) - .MintAsset(context, context.Signer, crystal); - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["e"] = new List(EquipmentIds.OrderBy(i => i).Select(i => i.Serialize())), - ["c"] = ChargeAp.Serialize(), - }.ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - EquipmentIds = plainValue["e"].ToList(StateExtensions.ToGuid); - ChargeAp = plainValue["c"].ToBoolean(); - } - } -} diff --git a/Lib9c/Action/ReRegisterProduct.cs b/Lib9c/Action/ReRegisterProduct.cs index 7ed74ff2af..bf05bd2568 100644 --- a/Lib9c/Action/ReRegisterProduct.cs +++ b/Lib9c/Action/ReRegisterProduct.cs @@ -153,7 +153,7 @@ public override IWorld Execute(IActionContext context) } else { - states = CancelProductRegistration0.Cancel(productsState, productInfo, + states = CancelProductRegistration.Cancel(productsState, productInfo, states, avatarState, context); } diff --git a/Lib9c/Action/ReRegisterProduct0.cs b/Lib9c/Action/ReRegisterProduct0.cs index a7f3561b24..504f3f5767 100644 --- a/Lib9c/Action/ReRegisterProduct0.cs +++ b/Lib9c/Action/ReRegisterProduct0.cs @@ -153,7 +153,7 @@ public override IWorld Execute(IActionContext context) } else { - states = CancelProductRegistration0.Cancel(productsState, productInfo, + states = CancelProductRegistration.Cancel(productsState, productInfo, states, avatarState, context); } diff --git a/Lib9c/Action/RewardGold.cs b/Lib9c/Action/RewardGold.cs index 13a2e21265..a4fcebbb71 100644 --- a/Lib9c/Action/RewardGold.cs +++ b/Lib9c/Action/RewardGold.cs @@ -308,8 +308,7 @@ public IWorld MinerReward(IActionContext ctx, IWorld states) public static IWorld TransferMead(IActionContext context, IWorld states) { #pragma warning disable LAA1002 - var targetAddresses = states.GetAccount(ReservedAddresses.LegacyAccount) - .TotalUpdatedFungibleAssets + var targetAddresses = states.TotalUpdatedFungibleAssets #pragma warning restore LAA1002 .Where(pair => pair.Item2.Equals(Currencies.Mead)) .Select(pair => pair.Item1) diff --git a/Lib9c/Action/RuneEnhancement0.cs b/Lib9c/Action/RuneEnhancement0.cs deleted file mode 100644 index 277eef7b05..0000000000 --- a/Lib9c/Action/RuneEnhancement0.cs +++ /dev/null @@ -1,143 +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.Rune; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("runeEnhancement")] - public class RuneEnhancement0 : GameAction, IRuneEnhancementV1 - { - public Address AvatarAddress; - public int RuneId; - public int TryCount = 1; - - Address IRuneEnhancementV1.AvatarAddress => AvatarAddress; - int IRuneEnhancementV1.RuneId => RuneId; - int IRuneEnhancementV1.TryCount => TryCount; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["a"] = AvatarAddress.Serialize(), - ["r"] = RuneId.Serialize(), - ["t"] = TryCount.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - AvatarAddress = plainValue["a"].ToAddress(); - RuneId = plainValue["r"].ToInteger(); - TryCount = plainValue["t"].ToInteger(); - } - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V100360ObsoleteIndex, context); - - var sheets = states.GetSheets( - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(RuneSheet), - typeof(RuneListSheet), - typeof(RuneCostSheet), - }); - - if (TryCount < 1) - { - throw new TryCountIsZeroException( - $"{AvatarAddress}TryCount must be greater than 0. " + - $"current TryCount : {TryCount}"); - } - - RuneState runeState; - var runeStateAddress = RuneState.DeriveAddress(AvatarAddress, RuneId); - if (states.TryGetLegacyState(runeStateAddress, out List rawState)) - { - runeState = new RuneState(rawState); - } - else - { - runeState = new RuneState(RuneId); - } - - var costSheet = sheets.GetSheet(); - if (!costSheet.TryGetValue(runeState.RuneId, out var costRow)) - { - throw new RuneCostNotFoundException( - $"[{nameof(RuneEnhancement0)}] my avatar address : {AvatarAddress}"); - } - - var targetLevel = runeState.Level + 1; - if (!costRow.TryGetCost(targetLevel, out var cost)) - { - throw new RuneCostDataNotFoundException( - $"[{nameof(RuneEnhancement0)}] my avatar address : {AvatarAddress}"); - } - - var runeSheet = sheets.GetSheet(); - if (!runeSheet.TryGetValue(runeState.RuneId, out var runeRow)) - { - throw new RuneNotFoundException( - $"[{nameof(RuneEnhancement0)}] my avatar address : {AvatarAddress}"); - } - - var ncgCurrency = states.GetGoldCurrency(); - var crystalCurrency = CrystalCalculator.CRYSTAL; - var runeCurrency = Currency.Legacy(runeRow.Ticker, 0, minters: null); - var ncgBalance = states.GetBalance(context.Signer, ncgCurrency); - var crystalBalance = states.GetBalance(context.Signer, crystalCurrency); - var runeBalance = states.GetBalance(AvatarAddress, runeCurrency); - var random = context.GetRandom(); - if (RuneHelper.TryEnhancement(ncgBalance, crystalBalance, runeBalance, - ncgCurrency, crystalCurrency, runeCurrency, - cost, random, TryCount, out var tryCount)) - { - runeState.LevelUp(); - states = states.SetLegacyState(runeStateAddress, runeState.Serialize()); - } - - var arenaSheet = sheets.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - - var ncgCost = cost.NcgQuantity * tryCount * ncgCurrency; - if (cost.NcgQuantity > 0) - { - states = states.TransferAsset(context, context.Signer, feeStoreAddress, ncgCost); - } - - var crystalCost = cost.CrystalQuantity * tryCount * crystalCurrency; - if (cost.CrystalQuantity > 0) - { - states = states.TransferAsset(context, context.Signer, feeStoreAddress, crystalCost); - } - - var runeCost = cost.RuneStoneQuantity * tryCount * runeCurrency; - if (cost.RuneStoneQuantity > 0) - { - states = states.TransferAsset(context, AvatarAddress, feeStoreAddress, runeCost); - } - - return states; - } - } -} diff --git a/Lib9c/Action/SellCancellation7.cs b/Lib9c/Action/SellCancellation7.cs deleted file mode 100644 index 2b0a439da3..0000000000 --- a/Lib9c/Action/SellCancellation7.cs +++ /dev/null @@ -1,181 +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 Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Serilog; -using BxDictionary = Bencodex.Types.Dictionary; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation7")] - public class SellCancellation7 : GameAction, ISellCancellationV3 - { - public Guid orderId; - public Guid tradableId; - public Address sellerAvatarAddress; - public ItemSubType itemSubType; - - Guid ISellCancellationV3.OrderId => orderId; - Guid ISellCancellationV3.TradableId => tradableId; - Address ISellCancellationV3.SellerAvatarAddress => sellerAvatarAddress; - string ISellCancellationV3.ItemSubType => itemSubType.ToString(); - - [Serializable] - public class Result : AttachmentActionResult - { - public ShopItem shopItem; - public Guid id; - - protected override string TypeId => "sellCancellation.result"; - - public Result() - { - } - - public Result(BxDictionary serialized) : base(serialized) - { - shopItem = new ShopItem((BxDictionary) serialized["shopItem"]); - id = serialized["id"].ToGuid(); - } - - public override IValue Serialize() => -#pragma warning disable LAA1002 - new BxDictionary(new Dictionary - { - [(Text) "shopItem"] = shopItem.Serialize(), - [(Text) "id"] = id.Serialize() - }.Union((BxDictionary) base.Serialize())); -#pragma warning restore LAA1002 - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [ProductIdKey] = orderId.Serialize(), - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [TradableIdKey] = tradableId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - orderId = plainValue[ProductIdKey].ToGuid(); - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - if (plainValue.ContainsKey(TradableIdKey)) - { - tradableId = plainValue[TradableIdKey].ToGuid(); - } - } - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var orderDigestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - var itemAddress = Addresses.GetItemAddress(tradableId); - - 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.TryGetLegacyState(shardedShopAddress, out BxDictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shardedShopAddress})."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - if (!states.TryGetLegacyState(Order.DeriveAddress(orderId), out Dictionary orderDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(Order)}({Order.DeriveAddress(orderId)})."); - } - - Order order = OrderFactory.Deserialize(orderDict); - order.ValidateCancelOrder2(avatarState, tradableId); - ITradableItem sellItem = order.Cancel2(avatarState, context.BlockIndex); - var shardedShopState = new ShardedShopStateV2(shopStateDict); - shardedShopState.Remove(order, context.BlockIndex); - states = states.SetLegacyState(shardedShopAddress, shardedShopState.Serialize()); - if (!states.TryGetLegacyState(orderDigestListAddress, out Dictionary rawList)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(OrderDigest)}({orderDigestListAddress})."); - } - var digestList = new OrderDigestListState(rawList); - digestList.Remove(order.OrderId); - - var expirationMail = avatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - avatarState.mailBox.Remove(expirationMail); - } - - var mail = new CancelOrderMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - 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 - .SetLegacyState(itemAddress, sellItem.Serialize()) - .SetLegacyState(orderDigestListAddress, digestList.Serialize()) - .SetAvatarState(sellerAvatarAddress, avatarState); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - 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/SellCancellation8.cs b/Lib9c/Action/SellCancellation8.cs deleted file mode 100644 index 813ec07c99..0000000000 --- a/Lib9c/Action/SellCancellation8.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Lib9c.Model.Order; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Model.Item; -using Nekoyume.Model.Mail; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Serilog; -using BxDictionary = Bencodex.Types.Dictionary; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("sell_cancellation8")] - public class SellCancellation8 : GameAction, ISellCancellationV3 - { - public Guid orderId; - public Guid tradableId; - public Address sellerAvatarAddress; - public ItemSubType itemSubType; - - Guid ISellCancellationV3.OrderId => orderId; - Guid ISellCancellationV3.TradableId => tradableId; - Address ISellCancellationV3.SellerAvatarAddress => sellerAvatarAddress; - string ISellCancellationV3.ItemSubType => itemSubType.ToString(); - - [Serializable] - public class Result : AttachmentActionResult - { - public ShopItem shopItem; - public Guid id; - - protected override string TypeId => "sellCancellation.result"; - - public Result() - { - } - - public Result(BxDictionary serialized) : base(serialized) - { - shopItem = new ShopItem((BxDictionary) serialized["shopItem"]); - id = serialized["id"].ToGuid(); - } - - public override IValue Serialize() => -#pragma warning disable LAA1002 - new BxDictionary(new Dictionary - { - [(Text) "shopItem"] = shopItem.Serialize(), - [(Text) "id"] = id.Serialize() - }.Union((BxDictionary) base.Serialize())); -#pragma warning restore LAA1002 - } - - protected override IImmutableDictionary PlainValueInternal => new Dictionary - { - [ProductIdKey] = orderId.Serialize(), - [SellerAvatarAddressKey] = sellerAvatarAddress.Serialize(), - [ItemSubTypeKey] = itemSubType.Serialize(), - [TradableIdKey] = tradableId.Serialize(), - }.ToImmutableDictionary(); - - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - orderId = plainValue[ProductIdKey].ToGuid(); - sellerAvatarAddress = plainValue[SellerAvatarAddressKey].ToAddress(); - itemSubType = plainValue[ItemSubTypeKey].ToEnum(); - if (plainValue.ContainsKey(TradableIdKey)) - { - tradableId = plainValue[TradableIdKey].ToGuid(); - } - } - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var shardedShopAddress = ShardedShopStateV2.DeriveAddress(itemSubType, orderId); - var digestListAddress = OrderDigestListState.DeriveAddress(sellerAvatarAddress); - var itemAddress = Addresses.GetItemAddress(tradableId); - - 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.TryGetLegacyState(shardedShopAddress, out BxDictionary shopStateDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(ShardedShopStateV2)}({shardedShopAddress})."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Get ShopState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - avatarState.updatedAt = context.BlockIndex; - avatarState.blockIndex = context.BlockIndex; - - if (!states.TryGetLegacyState(digestListAddress, out Dictionary rawList)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(OrderDigest)}({digestListAddress})."); - } - var digestList = new OrderDigestListState(rawList); - - digestList.Remove(orderId); - - if (!states.TryGetLegacyState(Order.DeriveAddress(orderId), out Dictionary orderDict)) - { - throw new FailedLoadStateException($"{addressesHex}failed to load {nameof(Order)}({Order.DeriveAddress(orderId)})."); - } - - Order order = OrderFactory.Deserialize(orderDict); - bool fromPreviousAction = false; - try - { - order.ValidateCancelOrder(avatarState, tradableId); - } - catch (Exception) - { - order.ValidateCancelOrder2(avatarState, tradableId); - fromPreviousAction = true; - } - - var sellItem = fromPreviousAction - ? order.Cancel2(avatarState, context.BlockIndex) - : order.Cancel(avatarState, context.BlockIndex); - - if (context.BlockIndex < order.ExpiredBlockIndex) - { - var shardedShopState = new ShardedShopStateV2(shopStateDict); - shardedShopState.Remove(order, context.BlockIndex); - states = states.SetLegacyState(shardedShopAddress, shardedShopState.Serialize()); - } - - var expirationMail = avatarState.mailBox.OfType() - .FirstOrDefault(m => m.OrderId.Equals(orderId)); - if (!(expirationMail is null)) - { - avatarState.mailBox.Remove(expirationMail); - } - - var mail = new CancelOrderMail( - context.BlockIndex, - orderId, - context.BlockIndex, - orderId - ); - avatarState.Update(mail); - - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Update AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - states = states - .SetLegacyState(itemAddress, sellItem.Serialize()) - .SetLegacyState(digestListAddress, digestList.Serialize()) - .SetAvatarState(sellerAvatarAddress, avatarState); - sw.Stop(); - Log.Verbose("{AddressesHex}Sell Cancel Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - 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/UnlockEquipmentRecipe1.cs b/Lib9c/Action/UnlockEquipmentRecipe1.cs deleted file mode 100644 index 0172998120..0000000000 --- a/Lib9c/Action/UnlockEquipmentRecipe1.cs +++ /dev/null @@ -1,158 +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.Helper; -using Nekoyume.Model; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; -using Serilog; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [ActionType("unlock_equipment_recipe")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class UnlockEquipmentRecipe1: GameAction, IUnlockEquipmentRecipeV1 - { - public List RecipeIds = new List(); - public Address AvatarAddress; - - IEnumerable IUnlockEquipmentRecipeV1.RecipeIds => RecipeIds; - Address IUnlockEquipmentRecipeV1.AvatarAddress => AvatarAddress; - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var unlockedRecipeIdsAddress = AvatarAddress.Derive("recipe_ids"); - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - var addressesHex = GetSignerAndOtherAddressesHex(context, AvatarAddress); - var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}UnlockEquipmentRecipe exec started", addressesHex); - if (!RecipeIds.Any() || RecipeIds.Any(i => i < 2)) - { - throw new InvalidRecipeIdException(); - } - - WorldInformation worldInformation; - if (states.GetWorldInformation(AvatarAddress) is { } worldInfo) - { - worldInformation = worldInfo; - } - else - { - // AvatarState migration required. - if (states.TryGetAvatarState(context.Signer, AvatarAddress, out AvatarState avatarState)) - { - worldInformation = avatarState.worldInformation; - states.SetAvatarState(AvatarAddress, avatarState); - } - else - { - // Invalid Address. - throw new FailedLoadStateException($"Can't find AvatarState {AvatarAddress}"); - } - } - - var equipmentRecipeSheet = states.GetSheet(); - - var unlockedIds = UnlockedIds(states, unlockedRecipeIdsAddress, equipmentRecipeSheet, worldInformation, RecipeIds); - - FungibleAssetValue cost = CrystalCalculator.CalculateRecipeUnlockCost(RecipeIds, equipmentRecipeSheet); - FungibleAssetValue balance = states.GetBalance(context.Signer, cost.Currency); - - if (balance < cost) - { - throw new NotEnoughFungibleAssetValueException($"required {cost}, but balance is {balance}"); - } - - states = states.SetLegacyState(unlockedRecipeIdsAddress, - unlockedIds.Aggregate(List.Empty, - (current, address) => current.Add(address.Serialize()))); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}UnlockEquipmentRecipe Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.TransferAsset(context, context.Signer, Addresses.UnlockEquipmentRecipe, cost); - } - - public static List UnlockedIds( - IWorld states, - Address unlockedRecipeIdsAddress, - EquipmentItemRecipeSheet equipmentRecipeSheet, - WorldInformation worldInformation, - List recipeIds - ) - { - List unlockedIds = states.TryGetLegacyState(unlockedRecipeIdsAddress, out List rawIds) - ? rawIds.ToList(StateExtensions.ToInteger) - : new List - { - 1 - }; - - // Sort recipe by ItemSubType & UnlockStage. - // 999 is not opened recipe. - var sortedRecipeRows = equipmentRecipeSheet.Values - .Where(r => r.UnlockStage != 999) - .OrderBy(r => r.ItemSubType) - .ThenBy(r => r.UnlockStage) - .ToList(); - - var unlockRecipeRows = sortedRecipeRows - .Where(r => recipeIds.Contains(r.Id)) - .ToList(); - - foreach (var recipeRow in unlockRecipeRows) - { - var recipeId = recipeRow.Id; - if (unlockedIds.Contains(recipeId)) - { - // Already Unlocked - throw new AlreadyRecipeUnlockedException( - $"recipe: {recipeId} already unlocked."); - } - - if (!worldInformation.IsStageCleared(recipeRow.UnlockStage)) - { - throw new NotEnoughClearedStageLevelException( - $"clear {recipeRow.UnlockStage} first."); - } - - var index = sortedRecipeRows.IndexOf(recipeRow); - if (index > 0) - { - var prevRow = sortedRecipeRows[index - 1]; - if (prevRow.ItemSubType == recipeRow.ItemSubType && !unlockedIds.Contains(prevRow.Id)) - { - // Can't skip previous recipe unlock. - throw new InvalidRecipeIdException($"unlock {prevRow.Id} first."); - } - } - - unlockedIds.Add(recipeId); - } - - return unlockedIds; - } - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["r"] = new List(RecipeIds.Select(i => i.Serialize())), - ["a"] = AvatarAddress.Serialize(), - }.ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - RecipeIds = plainValue["r"].ToList(StateExtensions.ToInteger); - AvatarAddress = plainValue["a"].ToAddress(); - } - } -} diff --git a/Lib9c/Action/UnlockWorld1.cs b/Lib9c/Action/UnlockWorld1.cs deleted file mode 100644 index 43e5efd853..0000000000 --- a/Lib9c/Action/UnlockWorld1.cs +++ /dev/null @@ -1,123 +0,0 @@ -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.Helper; -using Nekoyume.Model; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; -using static Lib9c.SerializeKeys; - -namespace Nekoyume.Action -{ - [ActionType("unlock_world")] - [ActionObsolete(ActionObsoleteConfig.V200030ObsoleteIndex)] - public class UnlockWorld1: GameAction, IUnlockWorldV1 - { - public List WorldIds; - public Address AvatarAddress; - - IEnumerable IUnlockWorldV1.WorldIds => WorldIds; - Address IUnlockWorldV1.AvatarAddress => AvatarAddress; - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - var states = context.PreviousState; - var unlockedWorldIdsAddress = AvatarAddress.Derive("world_ids"); - - CheckObsolete(ActionObsoleteConfig.V200030ObsoleteIndex, context); - if (!WorldIds.Any() || WorldIds.Any(i => i < 2 || i == GameConfig.MimisbrunnrWorldId)) - { - throw new InvalidWorldException(); - } - - WorldInformation worldInformation; - if (states.GetWorldInformation(AvatarAddress) is { } worldInfo) - { - worldInformation = worldInfo; - } - else - { - // AvatarState migration required. - if (states.TryGetAvatarState(context.Signer, AvatarAddress, out AvatarState avatarState)) - { - worldInformation = avatarState.worldInformation; - states = states.SetAvatarState(AvatarAddress, avatarState); - } - else - { - // Invalid Address. - throw new FailedLoadStateException($"Can't find AvatarState {AvatarAddress}"); - } - } - - List unlockedIds = states.TryGetLegacyState(unlockedWorldIdsAddress, out List rawIds) - ? rawIds.ToList(StateExtensions.ToInteger) - : new List - { - 1, - GameConfig.MimisbrunnrWorldId, - }; - - var sortedWorldIds = WorldIds.OrderBy(i => i).ToList(); - var worldUnlockSheet = states.GetSheet(); - foreach (var worldId in sortedWorldIds) - { - // Already Unlocked. - if (unlockedIds.Contains(worldId)) - { - throw new AlreadyWorldUnlockedException($"World {worldId} Already unlocked."); - } - - WorldUnlockSheet.Row row = - worldUnlockSheet.OrderedList.First(r => r.WorldIdToUnlock == worldId); - // Check Previous world unlocked. - if (!worldInformation.IsWorldUnlocked(row.WorldId) || !unlockedIds.Contains(row.WorldId)) - { - throw new FailedToUnlockWorldException($"unlock ${row.WorldId} first."); - } - - // Check stage cleared in HackAndSlash. - if (!worldInformation.IsWorldUnlocked(worldId) && !worldInformation.IsStageCleared(row.StageId)) - { - throw new FailedToUnlockWorldException($"{worldId} is locked."); - } - - unlockedIds.Add(worldId); - } - - FungibleAssetValue cost = - CrystalCalculator.CalculateWorldUnlockCost(sortedWorldIds, worldUnlockSheet); - FungibleAssetValue balance = states.GetBalance(context.Signer, cost.Currency); - - // Insufficient CRYSTAL. - if (balance < cost) - { - throw new NotEnoughFungibleAssetValueException($"UnlockWorld required {cost}, but balance is {balance}"); - } - - return states - .SetLegacyState(unlockedWorldIdsAddress, new List(unlockedIds.Select(i => i.Serialize()))) - .TransferAsset(context, context.Signer, Addresses.UnlockWorld, cost); - } - - protected override IImmutableDictionary PlainValueInternal - => new Dictionary - { - ["w"] = new List(WorldIds.Select(i => i.Serialize())), - ["a"] = AvatarAddress.Serialize(), - }.ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) - { - WorldIds = plainValue["w"].ToList(StateExtensions.ToInteger); - AvatarAddress = plainValue["a"].ToAddress(); - } - } -} diff --git a/Lib9c/Extensions/CombinationSlotStateExtensions.cs b/Lib9c/Extensions/CombinationSlotStateExtensions.cs index 6d2b906aa6..b5765aa836 100644 --- a/Lib9c/Extensions/CombinationSlotStateExtensions.cs +++ b/Lib9c/Extensions/CombinationSlotStateExtensions.cs @@ -27,7 +27,7 @@ public static void ValidateFromAction( $"{addressesHex}Aborted as the slot state is invalid: {slotState} @ {slotIndex}"); } } - + public static bool TryGetResultId(this CombinationSlotState state, out Guid resultId) { if (state?.Result is null) @@ -74,67 +74,6 @@ public static bool TryGetResultId(this CombinationSlotState state, out Guid resu case SellCancellation.Result r: resultId = r.id; break; - case SellCancellation7.Result r: - resultId = r.id; - break; - case SellCancellation8.Result r: - resultId = r.id; - break; - default: - resultId = default(Guid); - return false; - } - - return true; - } - - [Obsolete("Use TryGetResultId instead")] - public static bool TryGetResultIdV1(this CombinationSlotState state, out Guid resultId) - { - if (state?.Result is null) - { - resultId = default(Guid); - return false; - } - - switch (state.Result) - { - case Buy7.BuyerResult r: - resultId = r.id; - break; - case Buy7.SellerResult r: - resultId = r.id; - break; - case DailyReward2.DailyRewardResult r: - resultId = r.id; - break; - case ItemEnhancement13.ResultModel r: - resultId = r.id; - break; - case ItemEnhancement7.ResultModel r: - resultId = r.id; - break; - case ItemEnhancement11.ResultModel r: - resultId = r.id; - break; - case MonsterCollectionResult r: - resultId = r.id; - break; - case CombinationConsumable5.ResultModel r: - resultId = r.id; - break; - case RapidCombination5.ResultModel r: - resultId = r.id; - break; - case SellCancellation.Result r: - resultId = r.id; - break; - case SellCancellation7.Result r: - resultId = r.id; - break; - case SellCancellation8.Result r: - resultId = r.id; - break; default: resultId = default(Guid); return false; @@ -206,56 +145,5 @@ public static bool TryGetMail( return false; } } - - [Obsolete("Use TryGetMail instead")] - public static bool TryGetMailV1( - this CombinationSlotState state, - long blockIndex, - long requiredBlockIndex, - out CombinationMail combinationMail, - out ItemEnhanceMail itemEnhanceMail) - { - combinationMail = null; - itemEnhanceMail = null; - - if (!state.TryGetResultIdV1(out var resultId)) - { - return false; - } - - switch (state.Result) - { - case ItemEnhancement13.ResultModel r: - itemEnhanceMail = new ItemEnhanceMail( - r, - blockIndex, - resultId, - requiredBlockIndex); - return true; - case ItemEnhancement7.ResultModel r: - itemEnhanceMail = new ItemEnhanceMail( - r, - blockIndex, - resultId, - requiredBlockIndex); - return true; - case ItemEnhancement11.ResultModel r: - itemEnhanceMail = new ItemEnhanceMail( - r, - blockIndex, - resultId, - requiredBlockIndex); - return true; - case CombinationConsumable5.ResultModel r: - combinationMail = new CombinationMail( - r, - blockIndex, - resultId, - requiredBlockIndex); - return true; - default: - return false; - } - } } } diff --git a/Lib9c/Model/ActivationKey.cs b/Lib9c/Model/ActivationKey.cs index f900636c41..487af19302 100644 --- a/Lib9c/Model/ActivationKey.cs +++ b/Lib9c/Model/ActivationKey.cs @@ -64,11 +64,5 @@ public ActivateAccount CreateActivateAccount(byte[] nonce) { return new ActivateAccount(PendingAddress, PrivateKey.Sign(nonce)); } - - [Obsolete("ActivateAccount0 is obsolete. use CreateActivateAccount")] - public ActivateAccount0 CreateActivateAccount0(byte[] nonce) - { - return new ActivateAccount0(PendingAddress, PrivateKey.Sign(nonce)); - } } } diff --git a/Lib9c/Model/State/DeletedAvatarState.cs b/Lib9c/Model/State/DeletedAvatarState.cs deleted file mode 100644 index dc5328e46b..0000000000 --- a/Lib9c/Model/State/DeletedAvatarState.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Bencodex.Types; - -namespace Nekoyume.Model.State -{ - [Serializable] - public class DeletedAvatarState : AvatarState - { - public long deletedAt; - - public DeletedAvatarState(AvatarState avatarState, long blockIndex) - : base(avatarState) - { - deletedAt = blockIndex; - } - - public DeletedAvatarState(Dictionary serialized) - : base(serialized) - { - deletedAt = serialized["deletedAt"].ToLong(); - } - - public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text) "deletedAt"] = deletedAt.Serialize(), - }.Union((Dictionary) base.Serialize())); -#pragma warning restore LAA1002 - } -} diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index d4313b3577..044d356fe0 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -13,7 +13,6 @@ using Libplanet.Common; using Libplanet.Crypto; using Libplanet.Types.Assets; -using Libplanet.Types.Consensus; using LruCacheNet; using Nekoyume.Action; using Nekoyume.Helper; @@ -33,11 +32,6 @@ public static class LegacyModule private static readonly LruCache SheetsCache = new LruCache(SheetsCacheSize); - // Basic implementations from IAccount and IAccountState - public static IImmutableSet<(Address, Currency)> TotalUpdatedFungibleAssets( - this IWorld worldState) => - worldState.GetAccount(ReservedAddresses.LegacyAccount).TotalUpdatedFungibleAssets; - public static IValue GetLegacyState(this IWorldState worldState, Address address) => worldState.GetAccountState(ReservedAddresses.LegacyAccount).GetState(address); @@ -51,58 +45,6 @@ public static IWorld SetLegacyState(this IWorld world, Address address, IValue s ReservedAddresses.LegacyAccount, world.GetAccount(ReservedAddresses.LegacyAccount).SetState(address, state)); - public static FungibleAssetValue GetBalance( - this IWorldState worldState, - Address address, - Currency currency) => - worldState.GetAccountState(ReservedAddresses.LegacyAccount).GetBalance(address, currency); - - public static FungibleAssetValue GetTotalSupply(this IWorldState worldState, Currency currency) => - worldState.GetAccountState(ReservedAddresses.LegacyAccount).GetTotalSupply(currency); - - public static IWorld MintAsset( - this IWorld world, - IActionContext context, - Address recipient, - FungibleAssetValue value) => - world.SetAccount( - ReservedAddresses.LegacyAccount, - world.GetAccount(ReservedAddresses.LegacyAccount) - .MintAsset(context, recipient, value)); - - public static ValidatorSet GetValidatorSet(this IWorldState worldState) => - worldState.GetAccountState(ReservedAddresses.LegacyAccount).GetValidatorSet(); - - public static IWorld TransferAsset( - this IWorld world, - IActionContext context, - Address sender, - Address recipient, - FungibleAssetValue value, - bool allowNegativeBalance = false) => - world.SetAccount( - ReservedAddresses.LegacyAccount, - world.GetAccount(ReservedAddresses.LegacyAccount) - .TransferAsset(context, sender, recipient, value, allowNegativeBalance)); - - public static IWorld BurnAsset( - this IWorld world, - IActionContext context, - Address owner, - FungibleAssetValue value) => - world.SetAccount( - ReservedAddresses.LegacyAccount, - world.GetAccount(ReservedAddresses.LegacyAccount) - .BurnAsset(context, owner, value)); - - public static IWorld SetValidator( - this IWorld world, - Libplanet.Types.Consensus.Validator validator) => - world.SetAccount( - ReservedAddresses.LegacyAccount, - world.GetAccount(ReservedAddresses.LegacyAccount) - .SetValidator(validator)); - // Methods from AccountExtensions public static IWorld MarkBalanceChanged( this IWorld world, @@ -113,7 +55,7 @@ params Address[] accounts { if (accounts.Length == 1) { - return MintAsset(world, context, accounts[0], currency * 1); + return world.MintAsset(context, accounts[0], currency * 1); } else if (accounts.Length < 1) { @@ -122,8 +64,7 @@ params Address[] accounts for (int i = 1; i < accounts.Length; i++) { - world = TransferAsset( - world, + world = world.TransferAsset( context, accounts[i - 1], accounts[i], @@ -173,11 +114,11 @@ public static IWorld SetWorldBossKillReward( { if (reward.Currency.Equals(CrystalCalculator.CRYSTAL)) { - world = MintAsset(world, context, agentAddress, reward); + world = world.MintAsset(context, agentAddress, reward); } else { - world = MintAsset(world, context, avatarAddress, reward); + world = world.MintAsset(context, avatarAddress, reward); } } } @@ -205,7 +146,7 @@ public static IWorld Mead( while (true) { var price = rawValue * Currencies.Mead; - var balance = GetBalance(world, signer, Currencies.Mead); + var balance = world.GetBalance(signer, Currencies.Mead); if (balance < price) { var requiredMead = price - balance; @@ -215,7 +156,7 @@ public static IWorld Mead( var patron = contract[0].ToAddress(); try { - world = TransferAsset(world, context, patron, signer, requiredMead); + world = world.TransferAsset(context, patron, signer, requiredMead); } catch (InsufficientBalanceException) { @@ -270,7 +211,7 @@ public static bool TryGetGoldBalance( { try { - balance = GetBalance(worldState, address, currency); + balance = worldState.GetBalance(address, currency); return true; } catch (BalanceDoesNotExistsException) @@ -284,7 +225,7 @@ public static GoldBalanceState GetGoldBalanceState( this IWorldState worldState, Address address, Currency currency - ) => new GoldBalanceState(address, GetBalance(worldState, address, currency)); + ) => new GoldBalanceState(address, worldState.GetBalance(address, currency)); public static Currency GetGoldCurrency(this IWorldState worldState) { @@ -932,7 +873,7 @@ public static FungibleAssetValue GetStakedAmount( Address agentAddr) { var goldCurrency = GetGoldCurrency(worldState); - return GetBalance(worldState, StakeState.DeriveAddress(agentAddr), goldCurrency); + return worldState.GetBalance(StakeState.DeriveAddress(agentAddr), goldCurrency); } public static bool TryGetStakeStateV2( diff --git a/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv b/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv index a44dc19e94..4b622fa6fa 100644 --- a/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv +++ b/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv @@ -628,4 +628,204 @@ id,item_sub_type,grade,level,cost,exp,required_block,base_stat_growth_min,base_s 627,Aura,5,18,0,562521908,1,1000,1100,1000,1100,1500,1600,200,200 628,Aura,5,19,0,956287243,1,1000,1100,1000,1100,1500,1600,200,200 629,Aura,5,20,0,1625688313,1,1000,1100,1000,1100,1500,1600,200,200 -630,Aura,5,21,0,2763670132,1,1000,1100,1000,1100,1500,1600,200,200 \ No newline at end of file +630,Aura,5,21,0,2763670132,1,1000,1100,1000,1100,1500,1600,200,200 +631,Weapon,6,1,0,4000000,3600,800,800,600,600,0,0,0,0 +632,Weapon,6,2,0,8000000,7200,800,800,600,600,0,0,0,0 +633,Weapon,6,3,0,16000000,10800,800,800,600,600,0,0,0,0 +634,Weapon,6,4,10,32000000,14400,2000,2000,1800,1800,900,900,1500,1500 +635,Weapon,6,5,20,64000000,18000,800,800,600,600,0,0,0,0 +636,Weapon,6,6,40,128000000,21600,800,800,600,600,0,0,0,0 +637,Weapon,6,7,80,256000000,25200,800,800,600,600,0,0,0,0 +638,Weapon,6,8,80,256000000,28800,2000,2000,1800,1800,900,900,1500,1500 +639,Weapon,6,9,80,256000000,32400,800,800,600,600,0,0,0,0 +640,Weapon,6,10,80,256000000,36000,800,800,600,600,0,0,0,0 +641,Weapon,6,11,80,256000000,39600,800,800,600,600,0,0,0,0 +642,Weapon,6,12,80,256000000,43200,2000,2000,1800,1800,900,900,1500,1500 +643,Weapon,6,13,80,256000000,46800,800,800,600,600,0,0,0,0 +644,Weapon,6,14,80,256000000,50400,800,800,600,600,0,0,0,0 +645,Weapon,6,15,80,256000000,54000,800,800,600,600,0,0,0,0 +646,Weapon,6,16,80,256000000,57600,2000,2000,1800,1800,900,900,1500,1500 +647,Weapon,6,17,80,256000000,61200,800,800,600,600,0,0,0,0 +648,Weapon,6,18,80,256000000,64800,800,800,600,600,0,0,0,0 +649,Weapon,6,19,80,256000000,68400,800,800,600,600,0,0,0,0 +650,Weapon,6,20,80,256000000,72000,2000,2000,1800,1800,900,900,1500,1500 +651,Weapon,6,21,80,256000000,75600,800,800,600,600,0,0,0,0 +652,Weapon,6,22,80,256000000,75600,800,800,600,600,0,0,0,0 +653,Weapon,6,23,80,256000000,75600,800,800,600,600,0,0,0,0 +654,Weapon,6,24,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +655,Weapon,6,25,80,256000000,75600,800,800,600,600,0,0,0,0 +656,Weapon,6,26,80,256000000,75600,800,800,600,600,0,0,0,0 +657,Weapon,6,27,80,256000000,75600,800,800,600,600,0,0,0,0 +658,Weapon,6,28,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +659,Weapon,6,29,80,256000000,75600,800,800,600,600,0,0,0,0 +660,Weapon,6,30,80,256000000,75600,800,800,600,600,0,0,0,0 +661,Weapon,6,31,80,256000000,75600,800,800,600,600,0,0,0,0 +662,Weapon,6,32,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +663,Weapon,6,33,80,256000000,75600,800,800,600,600,0,0,0,0 +664,Weapon,6,34,80,256000000,75600,800,800,600,600,0,0,0,0 +665,Weapon,6,35,80,256000000,75600,800,800,600,600,0,0,0,0 +666,Weapon,6,36,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +667,Weapon,6,37,80,256000000,75600,800,800,600,600,0,0,0,0 +668,Weapon,6,38,80,256000000,75600,800,800,600,600,0,0,0,0 +669,Weapon,6,39,80,256000000,75600,800,800,600,600,0,0,0,0 +670,Weapon,6,40,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +671,Armor,6,1,0,4000000,3600,800,800,600,600,0,0,0,0 +672,Armor,6,2,0,8000000,7200,800,800,600,600,0,0,0,0 +673,Armor,6,3,0,16000000,10800,800,800,600,600,0,0,0,0 +674,Armor,6,4,10,32000000,14400,2000,2000,1800,1800,900,900,1500,1500 +675,Armor,6,5,20,64000000,18000,800,800,600,600,0,0,0,0 +676,Armor,6,6,40,128000000,21600,800,800,600,600,0,0,0,0 +677,Armor,6,7,80,256000000,25200,800,800,600,600,0,0,0,0 +678,Armor,6,8,80,256000000,28800,2000,2000,1800,1800,900,900,1500,1500 +679,Armor,6,9,80,256000000,32400,800,800,600,600,0,0,0,0 +680,Armor,6,10,80,256000000,36000,800,800,600,600,0,0,0,0 +681,Armor,6,11,80,256000000,39600,800,800,600,600,0,0,0,0 +682,Armor,6,12,80,256000000,43200,2000,2000,1800,1800,900,900,1500,1500 +683,Armor,6,13,80,256000000,46800,800,800,600,600,0,0,0,0 +684,Armor,6,14,80,256000000,50400,800,800,600,600,0,0,0,0 +685,Armor,6,15,80,256000000,54000,800,800,600,600,0,0,0,0 +686,Armor,6,16,80,256000000,57600,2000,2000,1800,1800,900,900,1500,1500 +687,Armor,6,17,80,256000000,61200,800,800,600,600,0,0,0,0 +688,Armor,6,18,80,256000000,64800,800,800,600,600,0,0,0,0 +689,Armor,6,19,80,256000000,68400,800,800,600,600,0,0,0,0 +690,Armor,6,20,80,256000000,72000,2000,2000,1800,1800,900,900,1500,1500 +691,Armor,6,21,80,256000000,75600,800,800,600,600,0,0,0,0 +692,Armor,6,22,80,256000000,75600,800,800,600,600,0,0,0,0 +693,Armor,6,23,80,256000000,75600,800,800,600,600,0,0,0,0 +694,Armor,6,24,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +695,Armor,6,25,80,256000000,75600,800,800,600,600,0,0,0,0 +696,Armor,6,26,80,256000000,75600,800,800,600,600,0,0,0,0 +697,Armor,6,27,80,256000000,75600,800,800,600,600,0,0,0,0 +698,Armor,6,28,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +699,Armor,6,29,80,256000000,75600,800,800,600,600,0,0,0,0 +700,Armor,6,30,80,256000000,75600,800,800,600,600,0,0,0,0 +701,Armor,6,31,80,256000000,75600,800,800,600,600,0,0,0,0 +702,Armor,6,32,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +703,Armor,6,33,80,256000000,75600,800,800,600,600,0,0,0,0 +704,Armor,6,34,80,256000000,75600,800,800,600,600,0,0,0,0 +705,Armor,6,35,80,256000000,75600,800,800,600,600,0,0,0,0 +706,Armor,6,36,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +707,Armor,6,37,80,256000000,75600,800,800,600,600,0,0,0,0 +708,Armor,6,38,80,256000000,75600,800,800,600,600,0,0,0,0 +709,Armor,6,39,80,256000000,75600,800,800,600,600,0,0,0,0 +710,Armor,6,40,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +711,Belt,6,1,0,4000000,3600,800,800,600,600,0,0,0,0 +712,Belt,6,2,0,8000000,7200,800,800,600,600,0,0,0,0 +713,Belt,6,3,0,16000000,10800,800,800,600,600,0,0,0,0 +714,Belt,6,4,10,32000000,14400,2000,2000,1800,1800,900,900,1500,1500 +715,Belt,6,5,20,64000000,18000,800,800,600,600,0,0,0,0 +716,Belt,6,6,40,128000000,21600,800,800,600,600,0,0,0,0 +717,Belt,6,7,80,256000000,25200,800,800,600,600,0,0,0,0 +718,Belt,6,8,80,256000000,28800,2000,2000,1800,1800,900,900,1500,1500 +719,Belt,6,9,80,256000000,32400,800,800,600,600,0,0,0,0 +720,Belt,6,10,80,256000000,36000,800,800,600,600,0,0,0,0 +721,Belt,6,11,80,256000000,39600,800,800,600,600,0,0,0,0 +722,Belt,6,12,80,256000000,43200,2000,2000,1800,1800,900,900,1500,1500 +723,Belt,6,13,80,256000000,46800,800,800,600,600,0,0,0,0 +724,Belt,6,14,80,256000000,50400,800,800,600,600,0,0,0,0 +725,Belt,6,15,80,256000000,54000,800,800,600,600,0,0,0,0 +726,Belt,6,16,80,256000000,57600,2000,2000,1800,1800,900,900,1500,1500 +727,Belt,6,17,80,256000000,61200,800,800,600,600,0,0,0,0 +728,Belt,6,18,80,256000000,64800,800,800,600,600,0,0,0,0 +729,Belt,6,19,80,256000000,68400,800,800,600,600,0,0,0,0 +730,Belt,6,20,80,256000000,72000,2000,2000,1800,1800,900,900,1500,1500 +731,Belt,6,21,80,256000000,75600,800,800,600,600,0,0,0,0 +732,Belt,6,22,80,256000000,75600,800,800,600,600,0,0,0,0 +733,Belt,6,23,80,256000000,75600,800,800,600,600,0,0,0,0 +734,Belt,6,24,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +735,Belt,6,25,80,256000000,75600,800,800,600,600,0,0,0,0 +736,Belt,6,26,80,256000000,75600,800,800,600,600,0,0,0,0 +737,Belt,6,27,80,256000000,75600,800,800,600,600,0,0,0,0 +738,Belt,6,28,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +739,Belt,6,29,80,256000000,75600,800,800,600,600,0,0,0,0 +740,Belt,6,30,80,256000000,75600,800,800,600,600,0,0,0,0 +741,Belt,6,31,80,256000000,75600,800,800,600,600,0,0,0,0 +742,Belt,6,32,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +743,Belt,6,33,80,256000000,75600,800,800,600,600,0,0,0,0 +744,Belt,6,34,80,256000000,75600,800,800,600,600,0,0,0,0 +745,Belt,6,35,80,256000000,75600,800,800,600,600,0,0,0,0 +746,Belt,6,36,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +747,Belt,6,37,80,256000000,75600,800,800,600,600,0,0,0,0 +748,Belt,6,38,80,256000000,75600,800,800,600,600,0,0,0,0 +749,Belt,6,39,80,256000000,75600,800,800,600,600,0,0,0,0 +750,Belt,6,40,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +751,Necklace,6,1,0,4000000,3600,800,800,600,600,0,0,0,0 +752,Necklace,6,2,0,8000000,7200,800,800,600,600,0,0,0,0 +753,Necklace,6,3,0,16000000,10800,800,800,600,600,0,0,0,0 +754,Necklace,6,4,10,32000000,14400,2000,2000,1800,1800,900,900,1500,1500 +755,Necklace,6,5,20,64000000,18000,800,800,600,600,0,0,0,0 +756,Necklace,6,6,40,128000000,21600,800,800,600,600,0,0,0,0 +757,Necklace,6,7,80,256000000,25200,800,800,600,600,0,0,0,0 +758,Necklace,6,8,80,256000000,28800,2000,2000,1800,1800,900,900,1500,1500 +759,Necklace,6,9,80,256000000,32400,800,800,600,600,0,0,0,0 +760,Necklace,6,10,80,256000000,36000,800,800,600,600,0,0,0,0 +761,Necklace,6,11,80,256000000,39600,800,800,600,600,0,0,0,0 +762,Necklace,6,12,80,256000000,43200,2000,2000,1800,1800,900,900,1500,1500 +763,Necklace,6,13,80,256000000,46800,800,800,600,600,0,0,0,0 +764,Necklace,6,14,80,256000000,50400,800,800,600,600,0,0,0,0 +765,Necklace,6,15,80,256000000,54000,800,800,600,600,0,0,0,0 +766,Necklace,6,16,80,256000000,57600,2000,2000,1800,1800,900,900,1500,1500 +767,Necklace,6,17,80,256000000,61200,800,800,600,600,0,0,0,0 +768,Necklace,6,18,80,256000000,64800,800,800,600,600,0,0,0,0 +769,Necklace,6,19,80,256000000,68400,800,800,600,600,0,0,0,0 +770,Necklace,6,20,80,256000000,72000,2000,2000,1800,1800,900,900,1500,1500 +771,Necklace,6,21,80,256000000,75600,800,800,600,600,0,0,0,0 +772,Necklace,6,22,80,256000000,75600,800,800,600,600,0,0,0,0 +773,Necklace,6,23,80,256000000,75600,800,800,600,600,0,0,0,0 +774,Necklace,6,24,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +775,Necklace,6,25,80,256000000,75600,800,800,600,600,0,0,0,0 +776,Necklace,6,26,80,256000000,75600,800,800,600,600,0,0,0,0 +777,Necklace,6,27,80,256000000,75600,800,800,600,600,0,0,0,0 +778,Necklace,6,28,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +779,Necklace,6,29,80,256000000,75600,800,800,600,600,0,0,0,0 +780,Necklace,6,30,80,256000000,75600,800,800,600,600,0,0,0,0 +781,Necklace,6,31,80,256000000,75600,800,800,600,600,0,0,0,0 +782,Necklace,6,32,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +783,Necklace,6,33,80,256000000,75600,800,800,600,600,0,0,0,0 +784,Necklace,6,34,80,256000000,75600,800,800,600,600,0,0,0,0 +785,Necklace,6,35,80,256000000,75600,800,800,600,600,0,0,0,0 +786,Necklace,6,36,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +787,Necklace,6,37,80,256000000,75600,800,800,600,600,0,0,0,0 +788,Necklace,6,38,80,256000000,75600,800,800,600,600,0,0,0,0 +789,Necklace,6,39,80,256000000,75600,800,800,600,600,0,0,0,0 +790,Necklace,6,40,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +791,Ring,6,1,0,4000000,3600,800,800,600,600,0,0,0,0 +792,Ring,6,2,0,8000000,7200,800,800,600,600,0,0,0,0 +793,Ring,6,3,0,16000000,10800,800,800,600,600,0,0,0,0 +794,Ring,6,4,10,32000000,14400,2000,2000,1800,1800,900,900,1500,1500 +795,Ring,6,5,20,64000000,18000,800,800,600,600,0,0,0,0 +796,Ring,6,6,40,128000000,21600,800,800,600,600,0,0,0,0 +797,Ring,6,7,80,256000000,25200,800,800,600,600,0,0,0,0 +798,Ring,6,8,80,256000000,28800,2000,2000,1800,1800,900,900,1500,1500 +799,Ring,6,9,80,256000000,32400,800,800,600,600,0,0,0,0 +800,Ring,6,10,80,256000000,36000,800,800,600,600,0,0,0,0 +801,Ring,6,11,80,256000000,39600,800,800,600,600,0,0,0,0 +802,Ring,6,12,80,256000000,43200,2000,2000,1800,1800,900,900,1500,1500 +803,Ring,6,13,80,256000000,46800,800,800,600,600,0,0,0,0 +804,Ring,6,14,80,256000000,50400,800,800,600,600,0,0,0,0 +805,Ring,6,15,80,256000000,54000,800,800,600,600,0,0,0,0 +806,Ring,6,16,80,256000000,57600,2000,2000,1800,1800,900,900,1500,1500 +807,Ring,6,17,80,256000000,61200,800,800,600,600,0,0,0,0 +808,Ring,6,18,80,256000000,64800,800,800,600,600,0,0,0,0 +809,Ring,6,19,80,256000000,68400,800,800,600,600,0,0,0,0 +810,Ring,6,20,80,256000000,72000,2000,2000,1800,1800,900,900,1500,1500 +811,Ring,6,21,80,256000000,75600,800,800,600,600,0,0,0,0 +812,Ring,6,22,80,256000000,75600,800,800,600,600,0,0,0,0 +813,Ring,6,23,80,256000000,75600,800,800,600,600,0,0,0,0 +814,Ring,6,24,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +815,Ring,6,25,80,256000000,75600,800,800,600,600,0,0,0,0 +816,Ring,6,26,80,256000000,75600,800,800,600,600,0,0,0,0 +817,Ring,6,27,80,256000000,75600,800,800,600,600,0,0,0,0 +818,Ring,6,28,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +819,Ring,6,29,80,256000000,75600,800,800,600,600,0,0,0,0 +820,Ring,6,30,80,256000000,75600,800,800,600,600,0,0,0,0 +821,Ring,6,31,80,256000000,75600,800,800,600,600,0,0,0,0 +822,Ring,6,32,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +823,Ring,6,33,80,256000000,75600,800,800,600,600,0,0,0,0 +824,Ring,6,34,80,256000000,75600,800,800,600,600,0,0,0,0 +825,Ring,6,35,80,256000000,75600,800,800,600,600,0,0,0,0 +826,Ring,6,36,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 +827,Ring,6,37,80,256000000,75600,800,800,600,600,0,0,0,0 +828,Ring,6,38,80,256000000,75600,800,800,600,600,0,0,0,0 +829,Ring,6,39,80,256000000,75600,800,800,600,600,0,0,0,0 +830,Ring,6,40,80,256000000,75600,2000,2000,1800,1800,900,900,1500,1500 \ No newline at end of file diff --git a/Lib9c/TableCSV/Item/ConsumableItemSheet.csv b/Lib9c/TableCSV/Item/ConsumableItemSheet.csv index d6ce8071be..1f592c198d 100644 --- a/Lib9c/TableCSV/Item/ConsumableItemSheet.csv +++ b/Lib9c/TableCSV/Item/ConsumableItemSheet.csv @@ -49,4 +49,7 @@ id,_name,item_sub_type,grade,elemental_type,stat_type_1,stat_value_1,stat_type_2 900103,위대한 몬스터펀치,Food,5,Normal,HP,3629,ATK,202 900104,9C_New_Year_Cookie,Food,3,Normal,ATK,90,DEF,145 900105,Great_9C_New_Year_Cookie,Food,4,Normal,ATK,154,DEF,177 -900106,Legendary_9C_New_Year_Cookie,Food,5,Normal,HP,4354,ATK,242 \ No newline at end of file +900106,Legendary_9C_New_Year_Cookie,Food,5,Normal,HP,4354,ATK,242 +900107,모카 번,Food,3,Normal,HP,2384,ATK,415 +900108,무지개 솜사탕,Food,4,Normal,HP,4508,ATK,787 +900109,모카두카 버블티,Food,5,Normal,HP,7941,ATK,1581 \ No newline at end of file diff --git a/Lib9c/TableCSV/Item/ItemRequirementSheet.csv b/Lib9c/TableCSV/Item/ItemRequirementSheet.csv index b2f3d3ffab..8c9d812bec 100644 --- a/Lib9c/TableCSV/Item/ItemRequirementSheet.csv +++ b/Lib9c/TableCSV/Item/ItemRequirementSheet.csv @@ -256,6 +256,13 @@ item_id,level,mimislevel 49900012,1,1 49900013,1,1 49900014,1,1 +49900015,1,1 +49900016,1,1 +49900017,1,1 +49900018,1,1 +49900019,1,1 +49900020,1,1 +49900021,1,1 105000,1,1 105001,1,1 105002,1,1 @@ -309,6 +316,9 @@ item_id,level,mimislevel 900104,1,1 900105,1,1 900106,1,1 +900107,1,1 +900108,1,1 +900109,1,1 10130002,1,1 10620002,1,1 10630002,1,1 diff --git a/Lib9c/TableCSV/Item/MaterialItemSheet.csv b/Lib9c/TableCSV/Item/MaterialItemSheet.csv index 3c55055fcf..4b99894513 100644 --- a/Lib9c/TableCSV/Item/MaterialItemSheet.csv +++ b/Lib9c/TableCSV/Item/MaterialItemSheet.csv @@ -43,6 +43,8 @@ id,_name,item_sub_type,grade,elemental_type 800107,Candy_Cane,FoodMaterial,2,Normal 800108,Aurora_Powder,FoodMaterial,3,Normal 800109,Fluffy_Marshmallow,FoodMaterial,4,Normal +800110,모카 펄,FoodMaterial,3,Normal +800111,무지개 시럽,FoodMaterial,4,Normal 800201,Silver Dust,FoodMaterial,4,Normal 800202,Golden Meat,FoodMaterial,4,Normal 303000,녹슨 칼,EquipmentMaterial,1,Normal @@ -50,26 +52,31 @@ id,_name,item_sub_type,grade,elemental_type 303002,단단한 검,EquipmentMaterial,3,Normal 303003,영험한 기운의 검,EquipmentMaterial,4,Normal 303004,전설의 검,EquipmentMaterial,5,Normal +303005,신성한 검,EquipmentMaterial,6,Normal 303100,낡은 옷,EquipmentMaterial,1,Normal 303101,버려진 옷,EquipmentMaterial,2,Normal 303102,단단한 옷,EquipmentMaterial,3,Normal 303103,영험한 기운의 갑옷,EquipmentMaterial,4,Normal 303104,전설의 옷,EquipmentMaterial,5,Normal +303105,신성한 옷,EquipmentMaterial,6,Normal 303200,낡은 끈,EquipmentMaterial,1,Normal 303201,버려진 허리띠,EquipmentMaterial,2,Normal 303202,단단한 벨트,EquipmentMaterial,3,Normal 303203,영험한 기운의 벨트,EquipmentMaterial,4,Normal 303204,전설의 벨트,EquipmentMaterial,5,Normal +303205,신성한 벨트,EquipmentMaterial,6,Normal 303300,낡은 목걸이,EquipmentMaterial,1,Normal 303301,버려진 목걸이,EquipmentMaterial,2,Normal 303302,단단한 목걸이,EquipmentMaterial,3,Normal 303303,영험한 기운의 목걸이,EquipmentMaterial,4,Normal 303304,전설의 목걸이,EquipmentMaterial,5,Normal +303305,신성한 목걸이,EquipmentMaterial,6,Normal 303400,낡은 반지,EquipmentMaterial,1,Normal 303401,버려진 반지,EquipmentMaterial,2,Normal 303402,단단한 반지,EquipmentMaterial,3,Normal 303403,영험한 기운의 반지,EquipmentMaterial,4,Normal 303404,전설의 반지,EquipmentMaterial,5,Normal +303405,신성한 반지,EquipmentMaterial,6,Normal 304000,불의 정수,MonsterPart,1,Fire 304001,대지의 정수,MonsterPart,1,Land 304002,물의 정수,MonsterPart,1,Water @@ -164,21 +171,21 @@ id,_name,item_sub_type,grade,elemental_type 306082,뽀족한 칼날 가면,MonsterPart,4,Water 306083,랜턴 파편,MonsterPart,4,Water 306084,어둠의 정수 파편,MonsterPart,4,Water -306085,신규 무색 결정,MonsterPart,5,Normal -306086,화염 결정,MonsterPart,5,Fire -306087,물 결정,MonsterPart,5,Water -306088,대지 결정,MonsterPart,5,Land -306089,바람 결정,MonsterPart,5,Wind -306090,얼어붙은 잎사귀,MonsterPart,4,Water -306091,얼어붙은 열매,MonsterPart,4,Water -306092,날카로운 얼음 파편,MonsterPart,4,Water -306093,짙은 보석 결정,MonsterPart,4,Water -306094,튼튼한 뿔 조각,MonsterPart,4,Water -306095,뾰족한 철재 파편,MonsterPart,4,Water -306096,얼어붙은 방패 파편,MonsterPart,4,Water -306097,뽀족한 칼날 가면,MonsterPart,4,Water -306098,랜턴 파편,MonsterPart,4,Water -306099,어둠의 정수 파편,MonsterPart,4,Water +306085,Mucus Crystal (Small),MonsterPart,5,Normal +306086,Flame Mucus Crystal (Small),MonsterPart,5,Fire +306087,Water Mucus Crystal (Small),MonsterPart,5,Water +306088,Earth Mucus Crystal (Small),MonsterPart,5,Land +306089,Wind Mucus Crystal (Small),MonsterPart,5,Wind +306090,Scarecrow Hat,MonsterPart,4,Water +306091,Broom Shard,MonsterPart,4,Water +306092,Crimson Crystal,MonsterPart,4,Water +306093,Thorny Arm Shield,MonsterPart,4,Water +306094,Frozen Helmet,MonsterPart,4,Water +306095,Tough Strap Belt,MonsterPart,4,Water +306096,Mace Shard,MonsterPart,4,Water +306097,Rough Tail Bundle,MonsterPart,4,Water +306098,Jewel-Encrusted Tiara,MonsterPart,4,Water +306099,Gruesome Gloves,MonsterPart,4,Water 400000,모래시계,Hourglass,4,Normal 500000,AP 스톤,ApStone,4,Normal 600101,Special Crystal Piece,EquipmentMaterial,1,Normal diff --git a/Lib9c/TableCSV/Skill/ActionBuffSheet.csv b/Lib9c/TableCSV/Skill/ActionBuffSheet.csv index 070d95ec38..b15c3216fd 100644 --- a/Lib9c/TableCSV/Skill/ActionBuffSheet.csv +++ b/Lib9c/TableCSV/Skill/ActionBuffSheet.csv @@ -7,5 +7,4 @@ id,group,_name,chance,duration,target_type,buff_type,elemental_type,atk_power_ra 704001,704000,기절,100,0,Enemies,Stun,Normal,0 705000,705000,흡혈,100,0,Self,Vampiric,Normal,0 706000,706000,집중,100,20,Self,Focus,Normal,0 -707000,707000,치유,100,0,Self,Dispel,Normal,0 -708000,708000,면역(1),100,20,Self,Dispel,Normal,0 +707000,707000,디버프 회피율,100,20,Self,Dispel,Normal,0 \ No newline at end of file diff --git a/Lib9c/TableCSV/Skill/SkillActionBuffSheet.csv b/Lib9c/TableCSV/Skill/SkillActionBuffSheet.csv index f38d791a32..41685e019b 100644 --- a/Lib9c/TableCSV/Skill/SkillActionBuffSheet.csv +++ b/Lib9c/TableCSV/Skill/SkillActionBuffSheet.csv @@ -7,5 +7,4 @@ skill_id,buff_id 700005,704001 700006,705000 700007,706000 -700009,707000 -700010,708000 +700009,707000 \ No newline at end of file diff --git a/Lib9c/TableCSV/Skill/SkillSheet.csv b/Lib9c/TableCSV/Skill/SkillSheet.csv index 3a3d59cfe0..e5d8c9d3b5 100644 --- a/Lib9c/TableCSV/Skill/SkillSheet.csv +++ b/Lib9c/TableCSV/Skill/SkillSheet.csv @@ -1,177 +1,176 @@ id,_name,elemental_type,skill_type,skill_category,skill_target_type,hit_count,cooldown,combo -100000,기본 공격,Normal,Attack,NormalAttack,Enemy,1,0 -100001,일격,Normal,Attack,BlowAttack,Enemy,1,1 -_100002,일격(전체),Normal,Attack,BlowAttack,Enemies,1,1 // 노멀 속성의 전체 일격은 추가하면 안 됩니다. -100003,연사,Normal,Attack,DoubleAttack,Enemy,2,1 -100005,광역 난사,Normal,Attack,AreaAttack,Enemies,5,1 -110000,불 공격,Fire,Attack,NormalAttack,Enemy,1,1 -110001,불꽃 일격,Fire,Attack,BlowAttack,Enemy,1,5 -110002,불꽃 일격(전체),Fire,Attack,BlowAttack,Enemies,1,13 -110003,불꽃 연사,Fire,Attack,DoubleAttack,Enemy,2,3 -110004,불꽃 연사(전체),Fire,Attack,DoubleAttack,Enemy,2,13 -110005,용암 해일,Fire,Attack,AreaAttack,Enemies,5,13 -110006,Flame Attack (ATK),Fire,Attack,AreaAttack,Enemies,5,13 -110007,Flame Attack (DEF),Fire,Attack,AreaAttack,Enemies,5,13 -110008,Barrage Aura (ATK),Fire,Attack,AreaAttack,Enemies,5,14 -120000,물 공격,Water,Attack,NormalAttack,Enemy,1,1 -120001,얼음 일격,Water,Attack,BlowAttack,Enemy,1,5 -120002,얼음 일격(전체),Water,Attack,BlowAttack,Enemies,1,13 -120003,얼음 연사,Water,Attack,DoubleAttack,Enemy,2,3 -120004,얼음 연사(전체),Water,Attack,DoubleAttack,Enemy,2,13 -120005,얼음 해일,Water,Attack,AreaAttack,Enemies,5,13 -130000,땅 공격,Land,Attack,NormalAttack,Enemy,1,1 -130001,모래 일격,Land,Attack,BlowAttack,Enemy,1,5 -130002,모래 일격(전체),Land,Attack,BlowAttack,Enemies,1,13 -130003,모래 연사,Land,Attack,DoubleAttack,Enemy,2,3 -130004,모래 연사(전체),Land,Attack,DoubleAttack,Enemy,2,13 -130005,모래 폭풍,Land,Attack,AreaAttack,Enemies,5,13 -130006,Sand Attack (ATK),Land,Attack,AreaAttack,Enemies,5,13 -130007,Sand Attack (DEF),Land,Attack,AreaAttack,Enemies,5,13 -140000,바람 공격,Wind,Attack,NormalAttack,Enemy,1,1 -140001,바람 일격,Wind,Attack,BlowAttack,Enemy,1,5 -140002,바람 일격(전체),Wind,Attack,BlowAttack,Enemies,1,13 -140003,바람 연사,Wind,Attack,DoubleAttack,Enemy,2,3 -140004,바람 연사(전체),Wind,Attack,DoubleAttack,Enemy,2,13 -140005,거대 태풍,Wind,Attack,AreaAttack,Enemies,5,13 -200000,체력 증가,Normal,Buff,HPBuff,Self,1,10 -_200001,체력 증가(전체),Normal,Buff,HPBuff,Ally,1,1 // 미구현 -210000,공격 강화,Normal,Buff,AttackBuff,Self,1,25 -_210001,공격 강화(전체),Normal,Buff,AttackBuff,Ally,1,1 // 미구현 -210002,공격 약화,Normal,Debuff,AttackBuff,Enemy,1,10 -210003,공격 약화(전체),Normal,Debuff,AttackBuff,Enemies,1,10 -210004,공격 강화,Normal,Buff,AttackBuff,Self,1,10 -210005,ATK Up (DEF),Normal,Buff,AttackBuff,Self,1,10 -210006,ATK Down (HIT),Normal,Debuff,AttackBuff,Enemy,1,10 -210007,ATK Down (SPD),Normal,Debuff,AttackBuff,Enemy,1,10 -210008,ATK All Down (ATK),Normal,Debuff,AttackBuff,Enemies,1,14 -210009,ATK All Down (DEF),Normal,Debuff,AttackBuff,Enemies,1,14 -210010,ATK Down (HP),Normal,Debuff,AttackBuff,Enemy,1,10 -210011,Double edged sword,Normal,Buff,AttackBuff,Self,1,10 -210012,ATK Down (Aegis Aura),Normal,Debuff,AttackBuff,Enemy,1,14 -220000,방어 강화,Normal,Buff,DefenseBuff,Self,1,25 -_220001,방어 강화(전체),Normal,Buff,DefenseBuff,Ally,1,1 // 미구현 -220002,방어 약화,Normal,Debuff,DefenseBuff,Enemy,1,10 -220003,방어 약화(전체),Normal,Debuff,DefenseBuff,Enemies,1,10 -220004,방어 강화,Normal,Buff,DefenseBuff,Self,1,10 -220005,DEF Up (ATK),Normal,Buff,DefenseBuff,Self,1,10 -220006,DEF Down (HP),Normal,Debuff,DefenseBuff,Enemy,1,10 -220007,DEF Down (HIT),Normal,Debuff,DefenseBuff,Enemy,1,10 -220008,DEF Down (SPD),Normal,Debuff,DefenseBuff,Enemy,1,10 -230000,치명 증가,Normal,Buff,CriticalBuff,Self,1,25 -_230001,치명 증가(전체),Normal,Buff,CriticalBuff,Ally,1,1 // 미구현 -230002,치명 감소,Normal,Debuff,CriticalBuff,Enemy,1,10 -230003,치명 감소(전체),Normal,Debuff,CriticalBuff,Enemies,1,10 -230004,치명 증가,Normal,Buff,CriticalBuff,Self,1,10 -230005,Frenzied Lv1,Normal,Buff,CriticalBuff,Self,1,10 -230006,Frenzied Lv2,Normal,Buff,CriticalBuff,Self,1,10 -230007,Frenzied Lv3,Normal,Buff,CriticalBuff,Self,1,10 -240000,명중 증가,Normal,Buff,HitBuff,Self,1,25 -_240001,명중 증가(전체),Normal,Buff,HitBuff,Ally,1,1 // 미구현 -240002,명중 감소,Normal,Debuff,HitBuff,Enemy,1,10 -240003,명중 감소(전체),Normal,Debuff,HitBuff,Enemies,1,10 -240004,명중 증가,Normal,Buff,HitBuff,Self,1,10 -240005,HIT Down (SPD),Normal,Debuff,HitBuff,Enemy,1,10 -240006,명중 증가(아우라),Normal,Buff,HitBuff,Self,1,10 -250000,속도 증가,Normal,Buff,SpeedBuff,Self,1,25 -_250001,속도 증가(전체),Normal,Buff,SpeedBuff,Ally,1,1 // 미구현 -250002,속도 감소,Normal,Debuff,SpeedBuff,Enemy,1,10 -250003,속도 감소(전체),Normal,Debuff,SpeedBuff,Enemies,1,10 -250004,속도 증가,Normal,Buff,SpeedBuff,Self,1,10 -250005,SPD Down (HP),Normal,Debuff,SpeedBuff,Enemy,1,10 -250006,SPD Down (HIT),Normal,Debuff,SpeedBuff,Enemy,1,10 -260000,Armor Penetration (ATK),Normal,Buff,Buff,Self,1,10 -260001,Armor Penetration (DEF),Normal,Buff,Buff,Self,1,10 -270000,Thorn (HP),Normal,Buff,Buff,Self,1,10 -270001,Thorn (HIT),Normal,Buff,Buff,Self,1,10 -270002,Thorn (SPD),Normal,Buff,Buff,Self,1,10 -300000,힐,Normal,Heal,Heal,Self,1,10 -300001,힐(전체),Normal,Heal,Heal,Ally,1,10 -300002,Heal (HP),Normal,Heal,Heal,Self,1,16 -300003,Heal (ATK),Normal,Heal,Heal,Self,1,16 -300004,Heal (DEF),Normal,Heal,Heal,Self,1,16 -300005,Heal (HIT),Normal,Heal,Heal,Self,1,16 -300006,Heal (SPD),Normal,Heal,Heal,Self,1,16 -400000,SS 올스텟,Normal,Buff,Buff,Self,1,999 -400001,SS 공격 치명,Normal,Buff,AttackBuff,Self,1,999 -400002,SS 방어 치명,Normal,Buff,DefenseBuff,Self,1,999 -400003,SS 속도 치명,Normal,Buff,SpeedBuff,Self,1,999 -400004,SS 명중 치명,Normal,Buff,HitBuff,Self,1,999 -400005,S 올스텟,Normal,Buff,Buff,Self,1,999 -400006,S 공격 치명,Normal,Buff,AttackBuff,Self,1,999 -400007,S 방어 치명,Normal,Buff,DefenseBuff,Self,1,999 -400008,S 속도 치명,Normal,Buff,SpeedBuff,Self,1,999 -400009,S 명중 치명,Normal,Buff,HitBuff,Self,1,999 -400010,S 공격 방어 치명,Normal,Buff,AttackBuff,Self,1,999 -400011,S 공격 속도 치명,Normal,Buff,AttackBuff,Self,1,999 -400012,S 공격 명중 치명,Normal,Buff,AttackBuff,Self,1,999 -400013,S 방어 속도 치명,Normal,Buff,DefenseBuff,Self,1,999 -400014,S 방어 명중 치명,Normal,Buff,DefenseBuff,Self,1,999 -400015,A 올스텟,Normal,Buff,Buff,Self,1,999 -400016,A 공격,Normal,Buff,AttackBuff,Self,1,999 -400017,A 방어,Normal,Buff,DefenseBuff,Self,1,999 -400018,A 속도,Normal,Buff,SpeedBuff,Self,1,999 -400019,A 명중,Normal,Buff,HitBuff,Self,1,999 -400020,A 공격 방어,Normal,Buff,AttackBuff,Self,1,999 -400021,A 공격 명중,Normal,Buff,AttackBuff,Self,1,999 -400022,A 공격 속도,Normal,Buff,AttackBuff,Self,1,999 -400023,A 방어 명중,Normal,Buff,DefenseBuff,Self,1,999 -400024,A 방어 속도,Normal,Buff,DefenseBuff,Self,1,999 -400025,A 명중 속도,Normal,Buff,HitBuff,Self,1,999 -400026,A 공격 방어 명중,Normal,Buff,AttackBuff,Self,1,999 -400027,A 공격 방어 속도,Normal,Buff,AttackBuff,Self,1,999 -400028,A 공격 명중 속도,Normal,Buff,AttackBuff,Self,1,999 -400029,A 방어 명중 속도,Normal,Buff,DefenseBuff,Self,1,999 -400030,B 올스텟,Normal,Buff,Buff,Self,1,999 -400031,B 공격,Normal,Buff,AttackBuff,Self,1,999 -400032,B 방어,Normal,Buff,DefenseBuff,Self,1,999 -400033,B 속도,Normal,Buff,SpeedBuff,Self,1,999 -400034,B 명중,Normal,Buff,HitBuff,Self,1,999 -400035,B 치명,Normal,Buff,CriticalBuff,Self,1,999 -400036,B 공격 방어,Normal,Buff,AttackBuff,Self,1,999 -400037,B 공격 명중,Normal,Buff,AttackBuff,Self,1,999 -400038,B 공격 속도,Normal,Buff,AttackBuff,Self,1,999 -400039,B 공격 치명,Normal,Buff,AttackBuff,Self,1,999 -400040,B 방어 명중,Normal,Buff,DefenseBuff,Self,1,999 -400041,B 방어 속도,Normal,Buff,DefenseBuff,Self,1,999 -400042,B 방어 치명,Normal,Buff,DefenseBuff,Self,1,999 -400043,B 명중 속도,Normal,Buff,HitBuff,Self,1,999 -400044,B 명중 치명,Normal,Buff,HitBuff,Self,1,999 -400045,B 속도 치명,Normal,Buff,SpeedBuff,Self,1,999 -400046,B 공격 방어 명중,Normal,Buff,AttackBuff,Self,1,999 -400047,B 공격 방어 속도,Normal,Buff,AttackBuff,Self,1,999 -400048,B 공격 방어 치명,Normal,Buff,AttackBuff,Self,1,999 -400049,B 공격 명중 속도,Normal,Buff,AttackBuff,Self,1,999 -400050,B 공격 명중 치명,Normal,Buff,AttackBuff,Self,1,999 -400051,B 공격 속도 치명,Normal,Buff,AttackBuff,Self,1,999 -400052,B 방어 명중 속도,Normal,Buff,DefenseBuff,Self,1,999 -400053,B 방어명중 치명,Normal,Buff,DefenseBuff,Self,1,999 -400054,B 방어 속도 치명,Normal,Buff,DefenseBuff,Self,1,999 -400055,B 명중 속도 치명,Normal,Buff,SpeedBuff,Self,1,999 -500001,분노,Normal,Attack,BlowAttack,Enemy,1,0 -500002,물어뜯기,Normal,Attack,BuffRemovalAttack,Enemy,1,0 -500003,할퀴기,Normal,Attack,BlowAttack,Enemy,1,0 -500004,할퀴기 Wave 4,Normal,Attack,BlowAttack,Enemy,1,0 -500005,할퀴기 Wave 5,Normal,Attack,BlowAttack,Enemy,1,0 -500011,분노,Normal,Attack,BlowAttack,Enemy,1,0 -500012,부식,Normal,Attack,BlowAttack,Enemy,1,0 -500013,연타,Normal,Attack,BlowAttack,Enemy,5,0 -500014,진노,Normal,Buff,CriticalBuff,Self,1,0 -500015,진노 Wave 4,Normal,Buff,CriticalBuff,Self,1,0 -500016,진노 Wave 5,Normal,Buff,CriticalBuff,Self,1,0 -510001,광폭화 Wave 1,Normal,Buff,Buff,Self,1,0 -510002,광폭화 Wave 2,Normal,Buff,Buff,Self,1,0 -510003,광폭화 Wave 3,Normal,Buff,Buff,Self,1,0 -510004,광폭화 Wave 4,Normal,Buff,Buff,Self,1,0 -510005,광폭화 Wave 5,Normal,Buff,Buff,Self,1,0 -600001,출혈,Normal,Debuff,Buff,Enemy,1,15 -700001,피해 감소 (고정),Normal,Buff,DamageReductionBuff,Self,1,15 -700002,피해 감소 (비율),Normal,Buff,DamageReductionBuff,Self,1,15 -700003,치명 데미지 증가,Normal,Buff,CriticalDamageBuff,Self,1,15 -700004,기절,Normal,Debuff,Buff,Enemy,1,15 -700005,기절,Normal,Debuff,Buff,Enemies,1,15 -700006,흡혈,Normal,Buff,Buff,Self,1,15 -700007,집중,Normal,Buff,Focus,Self,1,15 -700008,더블 어택,Normal,Attack,DoubleAttack,Enemy,2,15,true -700009,디버프 제거,Normal,Buff,Buff,Self,1,15 -700010,면역,Normal,Buff,Dispel,Self,1,15 -700011,HP 비례 데미지,Normal,Attack,ShatterStrike,Enemy,1,15 +100000,기본 공격,Normal,Attack,NormalAttack,Enemy,1,0, +100001,일격,Normal,Attack,BlowAttack,Enemy,1,1, +_100002,일격(전체),Normal,Attack,BlowAttack,Enemies,1,1 // 노멀 속성의 전체 일격은 추가하면 안 됩니다., +100003,연사,Normal,Attack,DoubleAttack,Enemy,2,1, +100005,광역 난사,Normal,Attack,AreaAttack,Enemies,5,1, +110000,불 공격,Fire,Attack,NormalAttack,Enemy,1,1, +110001,불꽃 일격,Fire,Attack,BlowAttack,Enemy,1,5, +110002,불꽃 일격(전체),Fire,Attack,BlowAttack,Enemies,1,13, +110003,불꽃 연사,Fire,Attack,DoubleAttack,Enemy,2,3, +110004,불꽃 연사(전체),Fire,Attack,DoubleAttack,Enemy,2,13, +110005,용암 해일,Fire,Attack,AreaAttack,Enemies,5,13, +110006,Flame Attack (ATK),Fire,Attack,AreaAttack,Enemies,5,13, +110007,Flame Attack (DEF),Fire,Attack,AreaAttack,Enemies,5,13, +110008,Barrage Aura (ATK),Fire,Attack,AreaAttack,Enemies,5,14, +120000,물 공격,Water,Attack,NormalAttack,Enemy,1,1, +120001,얼음 일격,Water,Attack,BlowAttack,Enemy,1,5, +120002,얼음 일격(전체),Water,Attack,BlowAttack,Enemies,1,13, +120003,얼음 연사,Water,Attack,DoubleAttack,Enemy,2,3, +120004,얼음 연사(전체),Water,Attack,DoubleAttack,Enemy,2,13, +120005,얼음 해일,Water,Attack,AreaAttack,Enemies,5,13, +130000,땅 공격,Land,Attack,NormalAttack,Enemy,1,1, +130001,모래 일격,Land,Attack,BlowAttack,Enemy,1,5, +130002,모래 일격(전체),Land,Attack,BlowAttack,Enemies,1,13, +130003,모래 연사,Land,Attack,DoubleAttack,Enemy,2,3, +130004,모래 연사(전체),Land,Attack,DoubleAttack,Enemy,2,13, +130005,모래 폭풍,Land,Attack,AreaAttack,Enemies,5,13, +130006,Sand Attack (ATK),Land,Attack,AreaAttack,Enemies,5,13, +130007,Sand Attack (DEF),Land,Attack,AreaAttack,Enemies,5,13, +140000,바람 공격,Wind,Attack,NormalAttack,Enemy,1,1, +140001,바람 일격,Wind,Attack,BlowAttack,Enemy,1,5, +140002,바람 일격(전체),Wind,Attack,BlowAttack,Enemies,1,13, +140003,바람 연사,Wind,Attack,DoubleAttack,Enemy,2,3, +140004,바람 연사(전체),Wind,Attack,DoubleAttack,Enemy,2,13, +140005,거대 태풍,Wind,Attack,AreaAttack,Enemies,5,13, +200000,체력 증가,Normal,Buff,HPBuff,Self,1,10, +_200001,체력 증가(전체),Normal,Buff,HPBuff,Ally,1,1 // 미구현, +210000,공격 강화,Normal,Buff,AttackBuff,Self,1,25, +_210001,공격 강화(전체),Normal,Buff,AttackBuff,Ally,1,1 // 미구현, +210002,공격 약화,Normal,Debuff,AttackBuff,Enemy,1,10, +210003,공격 약화(전체),Normal,Debuff,AttackBuff,Enemies,1,10, +210004,공격 강화,Normal,Buff,AttackBuff,Self,1,10, +210005,ATK Up (DEF),Normal,Buff,AttackBuff,Self,1,10, +210006,ATK Down (HIT),Normal,Debuff,AttackBuff,Enemy,1,10, +210007,ATK Down (SPD),Normal,Debuff,AttackBuff,Enemy,1,10, +210008,ATK All Down (ATK),Normal,Debuff,AttackBuff,Enemies,1,14, +210009,ATK All Down (DEF),Normal,Debuff,AttackBuff,Enemies,1,14, +210010,ATK Down (HP),Normal,Debuff,AttackBuff,Enemy,1,10, +210011,Double edged sword,Normal,Buff,AttackBuff,Self,1,10, +210012,ATK Down (Aegis Aura),Normal,Debuff,AttackBuff,Enemy,1,14, +220000,방어 강화,Normal,Buff,DefenseBuff,Self,1,25, +_220001,방어 강화(전체),Normal,Buff,DefenseBuff,Ally,1,1 // 미구현, +220002,방어 약화,Normal,Debuff,DefenseBuff,Enemy,1,10, +220003,방어 약화(전체),Normal,Debuff,DefenseBuff,Enemies,1,10, +220004,방어 강화,Normal,Buff,DefenseBuff,Self,1,10, +220005,DEF Up (ATK),Normal,Buff,DefenseBuff,Self,1,10, +220006,DEF Down (HP),Normal,Debuff,DefenseBuff,Enemy,1,10, +220007,DEF Down (HIT),Normal,Debuff,DefenseBuff,Enemy,1,10, +220008,DEF Down (SPD),Normal,Debuff,DefenseBuff,Enemy,1,10, +230000,치명 증가,Normal,Buff,CriticalBuff,Self,1,25, +_230001,치명 증가(전체),Normal,Buff,CriticalBuff,Ally,1,1 // 미구현, +230002,치명 감소,Normal,Debuff,CriticalBuff,Enemy,1,10, +230003,치명 감소(전체),Normal,Debuff,CriticalBuff,Enemies,1,10, +230004,치명 증가,Normal,Buff,CriticalBuff,Self,1,10, +230005,Frenzied Lv1,Normal,Buff,CriticalBuff,Self,1,10, +230006,Frenzied Lv2,Normal,Buff,CriticalBuff,Self,1,10, +230007,Frenzied Lv3,Normal,Buff,CriticalBuff,Self,1,10, +240000,명중 증가,Normal,Buff,HitBuff,Self,1,25, +_240001,명중 증가(전체),Normal,Buff,HitBuff,Ally,1,1 // 미구현, +240002,명중 감소,Normal,Debuff,HitBuff,Enemy,1,10, +240003,명중 감소(전체),Normal,Debuff,HitBuff,Enemies,1,10, +240004,명중 증가,Normal,Buff,HitBuff,Self,1,10, +240005,HIT Down (SPD),Normal,Debuff,HitBuff,Enemy,1,10, +240006,명중 증가(아우라),Normal,Buff,HitBuff,Self,1,10, +250000,속도 증가,Normal,Buff,SpeedBuff,Self,1,25, +_250001,속도 증가(전체),Normal,Buff,SpeedBuff,Ally,1,1 // 미구현, +250002,속도 감소,Normal,Debuff,SpeedBuff,Enemy,1,10, +250003,속도 감소(전체),Normal,Debuff,SpeedBuff,Enemies,1,10, +250004,속도 증가,Normal,Buff,SpeedBuff,Self,1,10, +250005,SPD Down (HP),Normal,Debuff,SpeedBuff,Enemy,1,10, +250006,SPD Down (HIT),Normal,Debuff,SpeedBuff,Enemy,1,10, +260000,Armor Penetration (ATK),Normal,Buff,Buff,Self,1,10, +260001,Armor Penetration (DEF),Normal,Buff,Buff,Self,1,10, +270000,Thorn (HP),Normal,Buff,Buff,Self,1,10, +270001,Thorn (HIT),Normal,Buff,Buff,Self,1,10, +270002,Thorn (SPD),Normal,Buff,Buff,Self,1,10, +300000,힐,Normal,Heal,Heal,Self,1,10, +300001,힐(전체),Normal,Heal,Heal,Ally,1,10, +300002,Heal (HP),Normal,Heal,Heal,Self,1,16, +300003,Heal (ATK),Normal,Heal,Heal,Self,1,16, +300004,Heal (DEF),Normal,Heal,Heal,Self,1,16, +300005,Heal (HIT),Normal,Heal,Heal,Self,1,16, +300006,Heal (SPD),Normal,Heal,Heal,Self,1,16, +400000,SS 올스텟,Normal,Buff,Buff,Self,1,999, +400001,SS 공격 치명,Normal,Buff,AttackBuff,Self,1,999, +400002,SS 방어 치명,Normal,Buff,DefenseBuff,Self,1,999, +400003,SS 속도 치명,Normal,Buff,SpeedBuff,Self,1,999, +400004,SS 명중 치명,Normal,Buff,HitBuff,Self,1,999, +400005,S 올스텟,Normal,Buff,Buff,Self,1,999, +400006,S 공격 치명,Normal,Buff,AttackBuff,Self,1,999, +400007,S 방어 치명,Normal,Buff,DefenseBuff,Self,1,999, +400008,S 속도 치명,Normal,Buff,SpeedBuff,Self,1,999, +400009,S 명중 치명,Normal,Buff,HitBuff,Self,1,999, +400010,S 공격 방어 치명,Normal,Buff,AttackBuff,Self,1,999, +400011,S 공격 속도 치명,Normal,Buff,AttackBuff,Self,1,999, +400012,S 공격 명중 치명,Normal,Buff,AttackBuff,Self,1,999, +400013,S 방어 속도 치명,Normal,Buff,DefenseBuff,Self,1,999, +400014,S 방어 명중 치명,Normal,Buff,DefenseBuff,Self,1,999, +400015,A 올스텟,Normal,Buff,Buff,Self,1,999, +400016,A 공격,Normal,Buff,AttackBuff,Self,1,999, +400017,A 방어,Normal,Buff,DefenseBuff,Self,1,999, +400018,A 속도,Normal,Buff,SpeedBuff,Self,1,999, +400019,A 명중,Normal,Buff,HitBuff,Self,1,999, +400020,A 공격 방어,Normal,Buff,AttackBuff,Self,1,999, +400021,A 공격 명중,Normal,Buff,AttackBuff,Self,1,999, +400022,A 공격 속도,Normal,Buff,AttackBuff,Self,1,999, +400023,A 방어 명중,Normal,Buff,DefenseBuff,Self,1,999, +400024,A 방어 속도,Normal,Buff,DefenseBuff,Self,1,999, +400025,A 명중 속도,Normal,Buff,HitBuff,Self,1,999, +400026,A 공격 방어 명중,Normal,Buff,AttackBuff,Self,1,999, +400027,A 공격 방어 속도,Normal,Buff,AttackBuff,Self,1,999, +400028,A 공격 명중 속도,Normal,Buff,AttackBuff,Self,1,999, +400029,A 방어 명중 속도,Normal,Buff,DefenseBuff,Self,1,999, +400030,B 올스텟,Normal,Buff,Buff,Self,1,999, +400031,B 공격,Normal,Buff,AttackBuff,Self,1,999, +400032,B 방어,Normal,Buff,DefenseBuff,Self,1,999, +400033,B 속도,Normal,Buff,SpeedBuff,Self,1,999, +400034,B 명중,Normal,Buff,HitBuff,Self,1,999, +400035,B 치명,Normal,Buff,CriticalBuff,Self,1,999, +400036,B 공격 방어,Normal,Buff,AttackBuff,Self,1,999, +400037,B 공격 명중,Normal,Buff,AttackBuff,Self,1,999, +400038,B 공격 속도,Normal,Buff,AttackBuff,Self,1,999, +400039,B 공격 치명,Normal,Buff,AttackBuff,Self,1,999, +400040,B 방어 명중,Normal,Buff,DefenseBuff,Self,1,999, +400041,B 방어 속도,Normal,Buff,DefenseBuff,Self,1,999, +400042,B 방어 치명,Normal,Buff,DefenseBuff,Self,1,999, +400043,B 명중 속도,Normal,Buff,HitBuff,Self,1,999, +400044,B 명중 치명,Normal,Buff,HitBuff,Self,1,999, +400045,B 속도 치명,Normal,Buff,SpeedBuff,Self,1,999, +400046,B 공격 방어 명중,Normal,Buff,AttackBuff,Self,1,999, +400047,B 공격 방어 속도,Normal,Buff,AttackBuff,Self,1,999, +400048,B 공격 방어 치명,Normal,Buff,AttackBuff,Self,1,999, +400049,B 공격 명중 속도,Normal,Buff,AttackBuff,Self,1,999, +400050,B 공격 명중 치명,Normal,Buff,AttackBuff,Self,1,999, +400051,B 공격 속도 치명,Normal,Buff,AttackBuff,Self,1,999, +400052,B 방어 명중 속도,Normal,Buff,DefenseBuff,Self,1,999, +400053,B 방어명중 치명,Normal,Buff,DefenseBuff,Self,1,999, +400054,B 방어 속도 치명,Normal,Buff,DefenseBuff,Self,1,999, +400055,B 명중 속도 치명,Normal,Buff,SpeedBuff,Self,1,999, +500001,분노,Normal,Attack,BlowAttack,Enemy,1,0, +500002,물어뜯기,Normal,Attack,BuffRemovalAttack,Enemy,1,0, +500003,할퀴기,Normal,Attack,BlowAttack,Enemy,1,0, +500004,할퀴기 Wave 4,Normal,Attack,BlowAttack,Enemy,1,0, +500005,할퀴기 Wave 5,Normal,Attack,BlowAttack,Enemy,1,0, +500011,분노,Normal,Attack,BlowAttack,Enemy,1,0, +500012,부식,Normal,Attack,BlowAttack,Enemy,1,0, +500013,연타,Normal,Attack,BlowAttack,Enemy,5,0, +500014,진노,Normal,Buff,CriticalBuff,Self,1,0, +500015,진노 Wave 4,Normal,Buff,CriticalBuff,Self,1,0, +500016,진노 Wave 5,Normal,Buff,CriticalBuff,Self,1,0, +510001,광폭화 Wave 1,Normal,Buff,Buff,Self,1,0, +510002,광폭화 Wave 2,Normal,Buff,Buff,Self,1,0, +510003,광폭화 Wave 3,Normal,Buff,Buff,Self,1,0, +510004,광폭화 Wave 4,Normal,Buff,Buff,Self,1,0, +510005,광폭화 Wave 5,Normal,Buff,Buff,Self,1,0, +600001,출혈,Normal,Debuff,Buff,Enemy,1,15, +700001,피해 감소 (고정),Normal,Buff,DamageReductionBuff,Self,1,15, +700002,피해 감소 (비율),Normal,Buff,DamageReductionBuff,Self,1,15, +700003,치명 데미지 증가,Normal,Buff,CriticalDamageBuff,Self,1,15, +700004,기절,Normal,Debuff,Buff,Enemy,1,15, +700005,기절,Normal,Debuff,Buff,Enemies,1,15, +700006,흡혈,Normal,Buff,Buff,Self,1,15, +700007,집중,Normal,Buff,Focus,Self,1,15, +700008,더블 어택,Normal,Attack,DoubleAttack,Enemy,2,15,TRUE +700009,면역,Normal,Buff,Dispel,Self,1,15, +700010,HP 비례 데미지,Normal,Attack,ShatterStrike,Enemy,1,15, \ No newline at end of file