Skip to content

Commit

Permalink
Handle dungeon conquest
Browse files Browse the repository at this point in the history
  • Loading branch information
Atralupus committed Apr 16, 2024
1 parent 10bb5dd commit 8b8a4f4
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 19 deletions.
8 changes: 8 additions & 0 deletions backend/app/Savor22b.Tests/Action/ActionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,12 @@ public TradeInventoryState DeriveTradeInventoryStateDelta(IAccountStateDelta sta
: throw new Exception();
return tradeInventoryState;
}

public GlobalDungeonState DeriveGlobalDungeonStateDelta(IAccountStateDelta stateDelta) {
var globalDungeonStateEncoded = stateDelta.GetState(GlobalDungeonState.StateAddress);
GlobalDungeonState globalDungeonState = globalDungeonStateEncoded is Bencodex.Types.Dictionary bdict
? new GlobalDungeonState(bdict)
: throw new Exception();
return globalDungeonState;
}
}
68 changes: 58 additions & 10 deletions backend/app/Savor22b.Tests/Action/RegisterTradeGoodActionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Savor22b.Tests.Action;
using Libplanet.Assets;
using Libplanet.State;
using Savor22b.Action;
using Savor22b.Model;
using Savor22b.States;
using Savor22b.States.Trade;
using Xunit;
Expand Down Expand Up @@ -45,11 +46,7 @@ public void RegisterTradeGoodActionExecute_Success_Food()
);

var afterState = DeriveRootStateFromAccountStateDelta(stateDelta);

var afterTradeInventoryStateEncoded = stateDelta.GetState(TradeInventoryState.StateAddress);
TradeInventoryState afterTradeInventoryState = afterTradeInventoryStateEncoded is Bencodex.Types.Dictionary bdict
? new TradeInventoryState(bdict)
: throw new Exception();
var afterTradeInventoryState = DeriveTradeInventoryStateDelta(stateDelta);

Assert.True(afterState.InventoryState.RefrigeratorStateList.Count == 1);

Expand Down Expand Up @@ -94,11 +91,7 @@ public void RegisterTradeGoodActionExecute_Success_Items()
);

var afterState = DeriveRootStateFromAccountStateDelta(stateDelta);

var afterTradeInventoryStateEncoded = stateDelta.GetState(TradeInventoryState.StateAddress);
TradeInventoryState afterTradeInventoryState = afterTradeInventoryStateEncoded is Bencodex.Types.Dictionary bdict
? new TradeInventoryState(bdict)
: throw new Exception();
var afterTradeInventoryState = DeriveTradeInventoryStateDelta(stateDelta);

Assert.Empty(afterState.InventoryState.ItemStateList);

Expand All @@ -115,6 +108,52 @@ public void RegisterTradeGoodActionExecute_Success_Items()
}
}

[Fact]
public void RegisterTradeGoodActionExecute_Success_DungeonConquest()
{
var stateDelta = CreatePresetStateDelta();
var beforeState = DeriveRootStateFromAccountStateDelta(stateDelta);

var dungeonId = beforeState.UserDungeonState.DungeonConquestHistories[0].DungeonId;

var action = new RegisterTradeGoodAction(
FungibleAssetValue.Parse(
Currencies.KeyCurrency,
"10"
),
dungeonId
);

stateDelta = action.Execute(
new DummyActionContext
{
PreviousStates = stateDelta,
Signer = SignerAddress(),
Random = random,
Rehearsal = false,
BlockIndex = 1,
}
);

var afterRootState = DeriveRootStateFromAccountStateDelta(stateDelta);
var afterTradeInventoryState = DeriveTradeInventoryStateDelta(stateDelta);
var afterGlobalDungeonState = DeriveGlobalDungeonStateDelta(stateDelta);

Assert.False(afterGlobalDungeonState.IsDungeonConquestAddress(dungeonId, SignerAddress()));

var tradeGood = afterTradeInventoryState.TradeGoods.First(g => g.Value.SellerAddress == SignerAddress()).Value;

if (tradeGood is DungeonConquestGoodState dungeonConquestGoodState)
{
Assert.Equal(dungeonConquestGoodState.Price, FungibleAssetValue.Parse(Currencies.KeyCurrency, "10"));
Assert.Equal(dungeonConquestGoodState.DungeonId, dungeonId);
}
else
{
throw new Exception();
}
}

private IAccountStateDelta CreatePresetStateDelta()
{
IAccountStateDelta state = new DummyState();
Expand Down Expand Up @@ -154,6 +193,15 @@ private IAccountStateDelta CreatePresetStateDelta()

rootState.SetInventoryState(inventoryState);

rootState.SetUserDungeonState(
rootState.UserDungeonState.AddDungeonConquestHistory(
new DungeonConquestHistoryState(1, 1, 1)
)
);
var globalDungeonState = new GlobalDungeonState();
globalDungeonState = globalDungeonState.SetDungeonConquestAddress(1, SignerAddress());

state = state.SetState(GlobalDungeonState.StateAddress, globalDungeonState.Serialize());
return state.SetState(signerAddress, rootState.Serialize());
}
}
36 changes: 36 additions & 0 deletions backend/app/Savor22b/Action/RegisterTradeGoodAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,31 @@ public RegisterTradeGoodAction(FungibleAssetValue price, Guid foodStateId)
Price = price;
FoodStateId = foodStateId;
ItemStateIds = null;
DungeonId = null;
}

public RegisterTradeGoodAction(FungibleAssetValue price, ImmutableList<Guid> itemStateIds)
{
Price = price;
FoodStateId = null;
ItemStateIds = itemStateIds;
DungeonId = null;
}

public RegisterTradeGoodAction(FungibleAssetValue price, int dungeonId)
{
Price = price;
FoodStateId = null;
ItemStateIds = null;
DungeonId = dungeonId;
}

public FungibleAssetValue Price;

public Guid? FoodStateId;

public int? DungeonId;

public ImmutableList<Guid>? ItemStateIds;

protected override IImmutableDictionary<string, IValue> PlainValueInternal =>
Expand All @@ -42,13 +54,15 @@ public RegisterTradeGoodAction(FungibleAssetValue price, ImmutableList<Guid> ite
[nameof(Price)] = Price.ToBencodex(),
[nameof(FoodStateId)] = FoodStateId.Serialize(),
[nameof(ItemStateIds)] = ItemStateIds is null ? Null.Value : (List)ItemStateIds.Select(i => i.Serialize()),
[nameof(DungeonId)] = DungeonId.Serialize(),
}.ToImmutableDictionary();

protected override void LoadPlainValueInternal(IImmutableDictionary<string, IValue> plainValue)
{
Price = plainValue[nameof(Price)].ToFungibleAssetValue();
FoodStateId = plainValue[nameof(FoodStateId)].ToGuid();
ItemStateIds = plainValue[nameof(ItemStateIds)] is Null ? null : ((List)plainValue[nameof(ItemStateIds)]).Select(e => e.ToGuid()).ToImmutableList();
DungeonId = plainValue[nameof(DungeonId)].ToInteger();
}

public override IAccountStateDelta Execute(IActionContext ctx)
Expand All @@ -66,6 +80,9 @@ public override IAccountStateDelta Execute(IActionContext ctx)
TradeInventoryState tradeInventoryState = states.GetState(TradeInventoryState.StateAddress) is Dictionary tradeInventoryStateEncoded
? new TradeInventoryState(tradeInventoryStateEncoded)
: new TradeInventoryState();
GlobalDungeonState globalDungeonState = states.GetState(GlobalDungeonState.StateAddress) is Dictionary globalDungeonStateEncoded
? new GlobalDungeonState(globalDungeonStateEncoded)
: new GlobalDungeonState();

var inventoryState = rootState.InventoryState;

Expand Down Expand Up @@ -93,12 +110,31 @@ public override IAccountStateDelta Execute(IActionContext ctx)

tradeInventoryState = tradeInventoryState.RegisterGood(itemsGoodState);
}
else if (DungeonId is not null)
{
var dungeonConquestGoodState = new DungeonConquestGoodState(
ctx.Signer,
ctx.Random.GenerateRandomGuid(),
Price,
DungeonId.Value);

if (!globalDungeonState.IsDungeonConquestAddress(DungeonId.Value, ctx.Signer))
{
throw new PermissionDeniedException("Permission denied");
}

globalDungeonState = globalDungeonState.RemoveDungeonConquestAddress(DungeonId.Value);

tradeInventoryState = tradeInventoryState.RegisterGood(dungeonConquestGoodState);
}
else
{
throw new InvalidValueException($"ItemStateIds or FoodStateId required");
}

rootState.SetInventoryState(inventoryState);

states = states.SetState(GlobalDungeonState.StateAddress, globalDungeonState.Serialize());
states = states.SetState(TradeInventoryState.StateAddress, tradeInventoryState.Serialize());
return states.SetState(ctx.Signer, rootState.Serialize());
}
Expand Down
4 changes: 2 additions & 2 deletions backend/app/Savor22b/Constants/Addresses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ public static class Addresses
);

public static readonly Address TradeStoreAddress = new Address(
"0000000000000000000000000000000000000002");
"0000000000000000000000000000000000000003");

public static readonly Address TradeStoreVaultAddress = new Address(
"0000000000000000000000000000000000000003");
"0000000000000000000000000000000000000004");
}
9 changes: 9 additions & 0 deletions backend/app/Savor22b/States/GlobalDungeonState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ namespace Savor22b.States;

using Bencodex.Types;
using Libplanet;
using Savor22b.Constants;

public class GlobalDungeonState : State
{
public static readonly Address StateAddress = Addresses.DungeonDataAddress;

public Dictionary<string, Address> DungeonStatus { get; private set; }

public GlobalDungeonState()
Expand Down Expand Up @@ -64,6 +67,12 @@ public GlobalDungeonState SetDungeonConquestAddress(int dungeonId, Address addre
return new GlobalDungeonState(DungeonStatus);
}

public GlobalDungeonState RemoveDungeonConquestAddress(int dungeonId)
{
DungeonStatus.Remove(dungeonId.ToString());
return new GlobalDungeonState(DungeonStatus);
}

public bool IsDungeonConquestAddress(int dungeonId, Address address)
{
return DungeonStatus.TryGetValue(dungeonId.ToString(), out Address conquestAddress)
Expand Down
36 changes: 36 additions & 0 deletions backend/app/Savor22b/States/Trade/DungeonConquestGoodState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Savor22b.States.Trade;

using System;
using Bencodex.Types;
using Libplanet.Assets;
using Libplanet;
using Libplanet.Headless.Extensions;

public class DungeonConquestGoodState : TradeGood
{
public int DungeonId { get; private set; }

public DungeonConquestGoodState(
Address sellerAddress,
Guid productId,
FungibleAssetValue price,
int dungeonId)
: base(sellerAddress, productId, price, nameof(DungeonConquestGoodState))
{
DungeonId = dungeonId;
}

public DungeonConquestGoodState(Dictionary serialized)
: base(serialized)
{
DungeonId = serialized[nameof(DungeonId)].ToInteger();
}

public override IValue Serialize()
{
var baseSerialized = base.Serialize() as Dictionary;
baseSerialized = baseSerialized.Add((Text)nameof(DungeonId), DungeonId.Serialize());

return baseSerialized;
}
}
27 changes: 20 additions & 7 deletions backend/app/Savor22b/States/Trade/TradeGoodFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,43 @@ public static TradeGood CreateInstance(Dictionary serialized)
return CreateFoodGood(serialized);
case nameof(ItemsGoodState):
return CreateItemGood(serialized);
case nameof(DungeonConquestGoodState):
return CreateDungeonConquestGood(serialized);
default:
throw new ArgumentException($"Unsupported TradeGood type: {type}");
}
}

private static TradeGood CreateFoodGood(Dictionary serialized)
{
Address sellerAddress = serialized[nameof(TradeGood.SellerAddress)].ToAddress();
Guid productId = serialized[nameof(TradeGood.ProductStateId)].ToGuid();
FungibleAssetValue price = serialized[nameof(TradeGood.Price)].ToFungibleAssetValue();

var food = new RefrigeratorState((Dictionary)serialized["Food"]);
var (sellerAddress, productId, price) = DeserializeTradeGoodMetaData(serialized);

return new FoodGoodState(sellerAddress, productId, price, food);
}

private static TradeGood CreateItemGood(Dictionary serialized)
{
var items = ((List)serialized["Items"]).Select(dict => new ItemState((Dictionary)dict)).ToImmutableList();
var (sellerAddress, productId, price) = DeserializeTradeGoodMetaData(serialized);

return new ItemsGoodState(sellerAddress, productId, price, items);
}

private static TradeGood CreateDungeonConquestGood(Dictionary serialized)
{
var dungeonId = serialized["DungeonId"].ToInteger();
var (sellerAddress, productId, price) = DeserializeTradeGoodMetaData(serialized);

return new DungeonConquestGoodState(sellerAddress, productId, price, dungeonId);
}

private static (Address, Guid, FungibleAssetValue) DeserializeTradeGoodMetaData(Dictionary serialized)
{
Address sellerAddress = serialized[nameof(TradeGood.SellerAddress)].ToAddress();
Guid productId = serialized[nameof(TradeGood.ProductStateId)].ToGuid();
FungibleAssetValue price = serialized[nameof(TradeGood.Price)].ToFungibleAssetValue();

var items = ((List)serialized["Items"]).Select(dict => new ItemState((Dictionary)dict)).ToImmutableList();

return new ItemsGoodState(sellerAddress, productId, price, items);
return (sellerAddress, productId, price);
}
}

0 comments on commit 8b8a4f4

Please sign in to comment.