diff --git a/.Lib9c.Tests/Action/DailyRewardTest.cs b/.Lib9c.Tests/Action/DailyRewardTest.cs index c8fc505a1b..98791d307a 100644 --- a/.Lib9c.Tests/Action/DailyRewardTest.cs +++ b/.Lib9c.Tests/Action/DailyRewardTest.cs @@ -80,11 +80,11 @@ public void Execute(bool legacy) var nextState = ExecuteInternal(previousStates, 2448); var nextGameConfigState = nextState.GetGameConfigState(); nextState.TryGetAvatarState(_agentAddress, _avatarAddress, out var nextAvatarState); - Assert.NotNull(nextAvatarState); - Assert.NotNull(nextAvatarState.inventory); - Assert.NotNull(nextAvatarState.questList); - Assert.NotNull(nextAvatarState.worldInformation); - Assert.Equal(nextGameConfigState.ActionPointMax, nextAvatarState.actionPoint); + var dailyRewardInfo = nextState.GetDailyRewardInfo(_avatarAddress); + Assert.NotNull(dailyRewardInfo); + Assert.Equal(2448L, dailyRewardInfo.ReceivedBlockIndex); + var actionPoint = nextState.GetActionPoint(_avatarAddress); + Assert.Equal(nextGameConfigState.ActionPointMax, actionPoint); var avatarRuneAmount = nextState.GetBalance(_avatarAddress, RuneHelper.DailyRewardRune); var expectedRune = RuneHelper.DailyRewardRune * nextGameConfigState.DailyRuneRewardAmount; diff --git a/Lib9c/Action/DailyReward.cs b/Lib9c/Action/DailyReward.cs index 34a8993306..1da82d57f6 100644 --- a/Lib9c/Action/DailyReward.cs +++ b/Lib9c/Action/DailyReward.cs @@ -36,10 +36,18 @@ public override IWorld Execute(IActionContext context) var started = DateTimeOffset.UtcNow; Log.Debug("{AddressesHex}DailyReward exec started", addressesHex); - if (!states.TryGetAvatarState(context.Signer, avatarAddress, out AvatarState avatarState)) + if (!states.TryGetDailyRewardInfo(context.Signer, avatarAddress, out var dailyRewardInfo)) { - throw new FailedLoadStateException( - $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); + var agentState = states.GetAgentState(context.Signer); + if (agentState is not null && agentState.avatarAddresses.ContainsValue(avatarAddress)) + { + dailyRewardInfo = + new DailyRewardModule.DailyRewardInfo(context.Signer, 0L); + } + else + { + throw new FailedLoadStateException("failed to load agent state."); + } } var gameConfigState = states.GetGameConfigState(); @@ -48,18 +56,17 @@ public override IWorld Execute(IActionContext context) throw new FailedLoadStateException($"{addressesHex}Aborted as the game config was failed to load."); } - if (context.BlockIndex < avatarState.dailyRewardReceivedIndex + gameConfigState.DailyRewardInterval) + if (context.BlockIndex < dailyRewardInfo.ReceivedBlockIndex + gameConfigState.DailyRewardInterval) { var sb = new StringBuilder() .Append($"{addressesHex}Not enough block index to receive daily rewards.") .Append( - $" Expected: Equals or greater than ({avatarState.dailyRewardReceivedIndex + gameConfigState.DailyRewardInterval}).") + $" Expected: Equals or greater than ({dailyRewardInfo.ReceivedBlockIndex + gameConfigState.DailyRewardInterval}).") .Append($" Actual: ({context.BlockIndex})"); throw new RequiredBlockIndexException(sb.ToString()); } - avatarState.dailyRewardReceivedIndex = context.BlockIndex; - avatarState.actionPoint = gameConfigState.ActionPointMax; + dailyRewardInfo.ReceivedBlockIndex = context.BlockIndex; if (gameConfigState.DailyRuneRewardAmount > 0) { @@ -71,7 +78,9 @@ public override IWorld Execute(IActionContext context) var ended = DateTimeOffset.UtcNow; Log.Debug("{AddressesHex}DailyReward Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetAvatarState(avatarAddress, avatarState); + return states + .SetDailyRewardInfo(avatarAddress, dailyRewardInfo) + .SetActionPoint(avatarAddress, gameConfigState.ActionPointMax); } protected override IImmutableDictionary PlainValueInternal => new Dictionary diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index 5b0908085d..1afba0f1a9 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -45,6 +45,8 @@ public static class Addresses public static readonly Address WorldInformation = new Address("000000000000000000000000000000000000001d"); public static readonly Address QuestList = new Address("000000000000000000000000000000000000001e"); public static readonly Address Collection = new Address("000000000000000000000000000000000000001f"); + public static readonly Address DailyReward = new Address("0000000000000000000000000000000000000020"); + public static readonly Address ActionPoint = new Address("0000000000000000000000000000000000000021"); public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); diff --git a/Lib9c/Module/ActionPointModule.cs b/Lib9c/Module/ActionPointModule.cs new file mode 100644 index 0000000000..a6801193c5 --- /dev/null +++ b/Lib9c/Module/ActionPointModule.cs @@ -0,0 +1,27 @@ +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; + +namespace Nekoyume.Module +{ + public static class ActionPointModule + { + public static long GetActionPoint(this IWorldState worldState, Address address) + { + var value = worldState.GetAccountState(Addresses.ActionPoint).GetState(address); + if (value is Integer integer) + { + return integer; + } + + return 0; + } + + public static IWorld SetActionPoint(this IWorld world, Address address, long actionPoint) + { + var account = world.GetAccount(Addresses.ActionPoint); + account = account.SetState(address, (Integer)actionPoint); + return world.SetAccount(Addresses.ActionPoint, account); + } + } +} diff --git a/Lib9c/Module/DailyRewardModule.cs b/Lib9c/Module/DailyRewardModule.cs new file mode 100644 index 0000000000..8021d52557 --- /dev/null +++ b/Lib9c/Module/DailyRewardModule.cs @@ -0,0 +1,69 @@ +using System; +using Bencodex; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Model.State; + +namespace Nekoyume.Module +{ + public static class DailyRewardModule + { + public class DailyRewardInfo : IBencodable + { + public Address AgentAddress; + public long ReceivedBlockIndex; + + public DailyRewardInfo(Address address, long blockIndex) + { + AgentAddress = address; + ReceivedBlockIndex = blockIndex; + } + + public IValue Bencoded => List.Empty.Add(AgentAddress.Serialize()).Add(ReceivedBlockIndex); + } + + public static DailyRewardInfo GetDailyRewardInfo(this IWorldState worldState, + Address avatarAddress) + { + IAccountState account = worldState.GetAccountState(Addresses.DailyReward); + var value = account.GetState(avatarAddress); + if (value is List l) + { + return new DailyRewardInfo(l[0].ToAddress(), (Integer)l[1]); + } + + throw new FailedLoadStateException(""); + } + + public static bool TryGetDailyRewardInfo(this IWorldState worldState, + Address agentAddress, Address avatarAddress, out DailyRewardInfo dailyRewardInfo) + { + dailyRewardInfo = null; + try + { + var temp= GetDailyRewardInfo(worldState, avatarAddress); + if (!temp.AgentAddress.Equals(agentAddress)) + { + return false; + } + + dailyRewardInfo = temp; + return true; + } + catch (FailedLoadStateException) + { + return false; + } + } + + public static IWorld SetDailyRewardInfo(this IWorld world, Address avatarAddress, + DailyRewardInfo dailyRewardInfo) + { + IAccount account = world.GetAccount(Addresses.DailyReward); + account = account.SetState(avatarAddress, dailyRewardInfo.Bencoded); + return world.SetAccount(Addresses.DailyReward, account); + } + } +}