Skip to content

Commit

Permalink
Add summary
Browse files Browse the repository at this point in the history
  • Loading branch information
ipdae committed Dec 27, 2024
1 parent 88ebc53 commit 4c90b55
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 21 deletions.
27 changes: 17 additions & 10 deletions Lib9c/Action/ClaimPatrolReward.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
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.Event;

namespace Nekoyume.Action
Expand All @@ -25,6 +23,10 @@ namespace Nekoyume.Action
public class ClaimPatrolReward : ActionBase
{
public const string TypeIdentifier = "claim_patrol_reward";

/// <summary>
/// The address of the avatar to receive the patrol reward.
/// </summary>
public Address AvatarAddress;

public ClaimPatrolReward()
Expand All @@ -40,6 +42,9 @@ public override IWorld Execute(IActionContext context)
GasTracer.UseGas(1);
var signer = context.Signer;
var states = context.PreviousState;

// Validate that the avatar address belongs to the signer.
// This ensures that only the owner of the avatar can claim the patrol reward for it.
if (!Addresses.CheckAvatarAddrIsContainedInAgent(signer, AvatarAddress))
{
throw new InvalidAddressException();
Expand All @@ -61,14 +66,17 @@ public override IWorld Execute(IActionContext context)
// validate
states.TryGetPatrolRewardClaimedBlockIndex(AvatarAddress, out var claimedBlockIndex);
var row = patrolRewardSheet.FindByLevel(avatarLevel, context.BlockIndex);

// Ensure rewards cannot be claimed too frequently.
// If the last claimed block index is set and the current block index is less than the allowed interval, throw an exception.
if (claimedBlockIndex > 0L && claimedBlockIndex + row.Interval > context.BlockIndex)
{
throw new RequiredBlockIndexException();
}

// mit rewards
// mint rewards
var random = context.GetRandom();
var favs = new List<FungibleAssetValue>();
var fav = new List<FungibleAssetValue>();
var items = new List<(int id, int count)>();
foreach (var reward in row.Rewards)
{
Expand All @@ -83,23 +91,22 @@ public override IWorld Execute(IActionContext context)
{
var currency = Currencies.GetMinterlessCurrency(ticker);
var recipient = Currencies.PickAddress(currency, signer, AvatarAddress);
var fav = currency * reward.Count;
states = states.MintAsset(context, recipient, fav);
favs.Add(fav);
var asset = currency * reward.Count;
states = states.MintAsset(context, recipient, asset);
fav.Add(asset);
}
}

var mailBox = avatarState.mailBox;
var mail = new PatrolRewardMail(context.BlockIndex, random.GenerateRandomGuid(), context.BlockIndex, favs, items);
var mail = new PatrolRewardMail(context.BlockIndex, random.GenerateRandomGuid(), context.BlockIndex, fav, items);
mailBox.Add(mail);
mailBox.CleanUp();
avatarState.mailBox = mailBox;

// set states
states = states
return states
.SetAvatarState(AvatarAddress, avatarState, setAvatar: true, setInventory: true, setWorldInformation: false, setQuestList: false)
.SetPatrolRewardClaimedBlockIndex(AvatarAddress, context.BlockIndex);
return states;
}

public override IValue PlainValue => Dictionary.Empty
Expand Down
37 changes: 36 additions & 1 deletion Lib9c/Model/Mail/PatrolRewardMail.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,34 @@

namespace Nekoyume.Model.Mail
{
/// <summary>
/// Represents a patrol reward mail that contains rewards such as fungible assets and items.
/// </summary>
public class PatrolRewardMail : Mail
{
/// <summary>
/// The type of mail. Defaults to Auction.
/// </summary>
public override MailType MailType => MailType.Auction;

/// <summary>
/// A list of fungible asset values included in the reward.
/// </summary>
public List<FungibleAssetValue> FungibleAssetValues = new ();

/// <summary>
/// A list of item rewards where each entry contains the item ID and count.
/// </summary>
public List<(int id, int count)> Items = new ();

/// <summary>
/// Constructor for creating a PatrolRewardMail with specified parameters.
/// </summary>
/// <param name="blockIndex">The block index at which the mail is created.</param>
/// <param name="id">The unique identifier for the mail.</param>
/// <param name="requiredBlockIndex">The block index required to claim the mail.</param>
/// <param name="fungibleAssetValues">The list of fungible asset rewards.</param>
/// <param name="items">The list of item rewards.</param>
public PatrolRewardMail(long blockIndex, Guid id, long requiredBlockIndex,
List<FungibleAssetValue> fungibleAssetValues, List<(int id, int count)> items)
: base(blockIndex, id, requiredBlockIndex)
Expand All @@ -22,6 +43,10 @@ public PatrolRewardMail(long blockIndex, Guid id, long requiredBlockIndex,
Items = items;
}

/// <summary>
/// Constructor for deserializing a PatrolRewardMail from a Bencodex dictionary.
/// </summary>
/// <param name="serialized">The serialized dictionary representation of the mail.</param>
public PatrolRewardMail(Dictionary serialized) : base(serialized)
{
if (serialized.ContainsKey("f"))
Expand All @@ -39,13 +64,24 @@ public PatrolRewardMail(Dictionary serialized) : base(serialized)
}
}

/// <summary>
/// Reads the mail contents into the specified mail object.
/// </summary>
/// <param name="mail">The mail object to populate with this mail's data.</param>
public override void Read(IMail mail)
{
mail.Read(this);
}

/// <summary>
/// The type identifier for the PatrolRewardMail class.
/// </summary>
protected override string TypeId => nameof(PatrolRewardMail);

/// <summary>
/// Serializes the PatrolRewardMail into a Bencodex-compatible format.
/// </summary>
/// <returns>A serialized representation of the mail.</returns>
public override IValue Serialize()
{
var dict = (Dictionary)base.Serialize();
Expand All @@ -62,6 +98,5 @@ public override IValue Serialize()

return dict;
}

}
}
26 changes: 25 additions & 1 deletion Lib9c/Module/PatrolRewardModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@

namespace Nekoyume.Module
{
/// <summary>
/// Provides utility methods for handling patrol reward states.
/// </summary>
public static class PatrolRewardModule
{
/// <summary>
/// Retrieves the block index at which the patrol reward was last claimed for a given avatar.
/// </summary>
/// <param name="worldState">The current world state.</param>
/// <param name="avatarAddress">The address of the avatar.</param>
/// <returns>The block index when the patrol reward was last claimed.</returns>
/// <exception cref="FailedLoadStateException">Thrown if the state could not be loaded.</exception>
public static long GetPatrolRewardClaimedBlockIndex(this IWorldState worldState, Address avatarAddress)
{
var value = worldState.GetAccountState(Addresses.PatrolReward).GetState(avatarAddress);
Expand All @@ -15,9 +25,16 @@ public static long GetPatrolRewardClaimedBlockIndex(this IWorldState worldState,
return integer;
}

throw new FailedLoadStateException("");
throw new FailedLoadStateException("Failed to load the patrol reward claimed block index state.");
}

/// <summary>
/// Tries to retrieve the block index at which the patrol reward was last claimed for a given avatar.
/// </summary>
/// <param name="worldState">The current world state.</param>
/// <param name="avatarAddress">The address of the avatar.</param>
/// <param name="blockIndex">Outputs the block index when the patrol reward was last claimed.</param>
/// <returns><c>true</c> if the block index was successfully retrieved; otherwise, <c>false</c>.</returns>
public static bool TryGetPatrolRewardClaimedBlockIndex(this IWorldState worldState, Address avatarAddress, out long blockIndex)
{
blockIndex = 0L;
Expand All @@ -33,6 +50,13 @@ public static bool TryGetPatrolRewardClaimedBlockIndex(this IWorldState worldSta
}
}

/// <summary>
/// Sets the block index at which the patrol reward was last claimed for a given avatar.
/// </summary>
/// <param name="world">The current world instance.</param>
/// <param name="avatarAddress">The address of the avatar.</param>
/// <param name="blockIndex">The block index to set.</param>
/// <returns>The updated world instance.</returns>
public static IWorld SetPatrolRewardClaimedBlockIndex(this IWorld world, Address avatarAddress, long blockIndex)
{
var account = world.GetAccount(Addresses.PatrolReward);
Expand Down
34 changes: 25 additions & 9 deletions Lib9c/TableData/Event/PatrolRewardSheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@
using System.Linq;
using static Nekoyume.TableData.TableExtensions;


namespace Nekoyume.TableData.Event
{
/// <summary>
/// Represents the PatrolRewardSheet, which defines patrol reward policies and rules.
/// </summary>
[Serializable]
public class PatrolRewardSheet : Sheet<int, PatrolRewardSheet.Row>
{
/// <summary>
/// Represents a reward model consisting of item counts, IDs, and tickers.
/// </summary>
[Serializable]
public class RewardModel
{
Expand All @@ -23,20 +28,27 @@ public RewardModel(int count, int itemId, string ticker)
Ticker = ticker;
}
}

/// <summary>
/// Represents a row in the PatrolRewardSheet, defining patrol reward policies for specific levels and intervals.
/// </summary>
[Serializable]
public class Row: SheetRow<int>
public class Row : SheetRow<int>
{
public const int MaxRewardCount = 100;
public override int Key => Id;
public int Id { get; set; }

public long StartedBlockIndex { get; set; }
public long EndedBlockIndex { get; set; }
public long Interval { get; set; }
public int MinimumLevel { get; set; }
public int? MaxLevel { get; set; }
public List<RewardModel> Rewards { get; set; } = new ();

/// <summary>
/// Sets the row data using a list of string fields.
/// </summary>
/// <param name="fields">The fields to parse into the row.</param>
public override void Set(IReadOnlyList<string> fields)
{
Id = ParseInt(fields[0]);
Expand Down Expand Up @@ -66,27 +78,28 @@ public override void Set(IReadOnlyList<string> fields)
{
break;
}

}
}
}

/// <summary>
/// Find reward policy row by avatar level. if it can't find level range, return lowest level policy.
/// Finds the reward policy row based on avatar level and block index.
/// </summary>
/// <param name="level">avatar level</param>
/// <param name="blockIndex">current block index</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException">can't find block index contains policy.</exception>
/// <param name="level">The avatar's level.</param>
/// <param name="blockIndex">The current block index.</param>
/// <returns>The corresponding reward policy row.</returns>
/// <exception cref="InvalidOperationException">Thrown if no activated policy matches the criteria.</exception>
public Row FindByLevel(int level, long blockIndex)
{
var orderedRows = Values
.Where(r => r.StartedBlockIndex <= blockIndex && blockIndex <= r.EndedBlockIndex)
.OrderByDescending(i => i.MinimumLevel).ToList();

if (!orderedRows.Any())
{
throw new InvalidOperationException("can't find activated policy");
}

foreach (var row in orderedRows)
{
if (row.MinimumLevel <= level)
Expand All @@ -98,6 +111,9 @@ public Row FindByLevel(int level, long blockIndex)
return orderedRows.Last();
}

/// <summary>
/// Initializes a new instance of the <see cref="PatrolRewardSheet"/> class.
/// </summary>
public PatrolRewardSheet() : base(nameof(PatrolRewardSheet))
{
}
Expand Down

0 comments on commit 4c90b55

Please sign in to comment.