Skip to content

Commit

Permalink
Merge pull request #73 from Atralupus/feat/patch-table-sync
Browse files Browse the repository at this point in the history
Implement patch table sync in block poller
  • Loading branch information
Atralupus authored May 21, 2024
2 parents 71a4afa + 60474c1 commit b086ac3
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 17 deletions.
125 changes: 108 additions & 17 deletions Mimir.Worker/BlockPoller.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
using Bencodex;
using Bencodex.Types;
using HeadlessGQL;
using Libplanet.Common;
using Libplanet.Crypto;
using Libplanet.Types.Tx;
using Mimir.Worker.Models;
using Mimir.Worker.Scrapper;
using Mimir.Worker.Services;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Model.State;
using Nekoyume.TableData;
using StrawberryShake;

namespace Mimir.Worker;

public class BlockPoller(IStateService stateService, IHeadlessGQLClient headlessGqlClient, MongoDbStore mongoDbStore)
public class BlockPoller(
IStateService stateService,
IHeadlessGQLClient headlessGqlClient,
MongoDbStore mongoDbStore
)
{
public async Task RunAsync(CancellationToken cancellationToken)
{
Expand All @@ -28,18 +37,21 @@ public async Task RunAsync(CancellationToken cancellationToken)

await EveryAvatarAsync(processBlockIndex, stateGetter, cancellationToken);
await BattleArenaAsync(processBlockIndex, stateGetter, cancellationToken);
await EveryPatchTableAsync(processBlockIndex, cancellationToken);
await mongoDbStore.UpdateLatestBlockIndex(processBlockIndex);
}
}

private async Task EveryAvatarAsync(
long processBlockIndex,
StateGetter stateGetter,
CancellationToken cancellationToken)
CancellationToken cancellationToken
)
{
var operationResult = await headlessGqlClient.GetTransactionSigners.ExecuteAsync(
processBlockIndex,
cancellationToken);
cancellationToken
);
if (operationResult.Data is null)
{
HandleErrors(operationResult);
Expand All @@ -60,25 +72,99 @@ private async Task EveryAvatarAsync(
}

var agentAddress = new Address(tx.Signer);
var avatarAddresses = Enumerable.Range(0, GameConfig.SlotCount)
var avatarAddresses = Enumerable
.Range(0, GameConfig.SlotCount)
.Select(e => Addresses.GetAvatarAddress(agentAddress, e));
var avatarDataArray = await Task.WhenAll(avatarAddresses.Select(stateGetter.GetAvatarData));
var avatarDataArray = await Task.WhenAll(
avatarAddresses.Select(stateGetter.GetAvatarData)
);
await mongoDbStore.BulkUpsertAvatarDataAsync(
avatarDataArray
.Where(e => e is not null)
.OfType<AvatarData>()
.ToList());
avatarDataArray.Where(e => e is not null).OfType<AvatarData>().ToList()
);
}
}

private async Task EveryPatchTableAsync(
long processBlockIndex,
CancellationToken cancellationToken
)
{
var rawPatchTableTxsResp = await headlessGqlClient.GetPatchTableTransactions.ExecuteAsync(
processBlockIndex,
cancellationToken
);
if (rawPatchTableTxsResp.Data is null)
{
HandleErrors(rawPatchTableTxsResp);
return;
}
var sheetTypes = typeof(ISheet)
.Assembly.GetTypes()
.Where(type =>
type.Namespace is { } @namespace
&& @namespace.StartsWith($"{nameof(Nekoyume)}.{nameof(Nekoyume.TableData)}")
&& !type.IsAbstract
&& typeof(ISheet).IsAssignableFrom(type)
);

var patchTableTxs = rawPatchTableTxsResp
.Data?.Transaction?.NcTransactions.Where(raw => raw is not null)
.Select(raw =>
TxMarshaler.DeserializeTransactionWithoutVerification(
Convert.FromBase64String(raw!.SerializedPayload)
)
)
.ToList();
foreach (var patchTableTx in patchTableTxs)
{
var patchTableAction = (Dictionary)patchTableTx.Actions[0];
var patchTableActionValues = (Dictionary)patchTableAction["values"];
var tableName = ((Text)patchTableActionValues["table_name"]).ToDotnetString();

var sheetType = sheetTypes.Where(type => type.Name == tableName).FirstOrDefault();

if (sheetType == null)
{
throw new TypeLoadException(
$"Unable to find a class type matching the table name '{tableName}' in the specified namespace."
);
}
var sheetInstance = Activator.CreateInstance(sheetType);
if (sheetInstance is not ISheet sheet)
{
throw new InvalidCastException($"Type {sheetType.Name} cannot be cast to ISheet.");
}
var sheetAddress = Addresses.TableSheet.Derive(tableName);
var sheetState = await stateService.GetState(sheetAddress);
if (sheetState is not Text sheetValue)
{
throw new InvalidOperationException($"Expected sheet state to be of type 'Text'.");
}

sheet.Set(sheetValue.Value);

var sheetData = new TableSheetData(
sheetAddress,
tableName,
sheet,
sheetState.ToDotnetString(),
ByteUtil.Hex(new Codec().Encode(sheetState))
);

await mongoDbStore.InsertTableSheets(sheetData);
}
}

private async Task BattleArenaAsync(
long processBlockIndex,
StateGetter stateGetter,
CancellationToken cancellationToken)
CancellationToken cancellationToken
)
{
var rawArenaTxsResp = await headlessGqlClient.GetBattleArenaTransactions.ExecuteAsync(
processBlockIndex,
cancellationToken);
cancellationToken
);
if (rawArenaTxsResp.Data is null)
{
HandleErrors(rawArenaTxsResp);
Expand All @@ -90,10 +176,13 @@ private async Task BattleArenaAsync(
return;
}

var arenaTxs = rawArenaTxsResp.Data.Transaction.NcTransactions
.Where(raw => raw is not null)
.Select(raw => TxMarshaler.DeserializeTransactionWithoutVerification(
Convert.FromBase64String(raw!.SerializedPayload)))
var arenaTxs = rawArenaTxsResp
.Data.Transaction.NcTransactions.Where(raw => raw is not null)
.Select(raw =>
TxMarshaler.DeserializeTransactionWithoutVerification(
Convert.FromBase64String(raw!.SerializedPayload)
)
)
.ToList();
foreach (var arenaTx in arenaTxs)
{
Expand All @@ -112,12 +201,14 @@ await mongoDbStore.BulkUpsertAvatarDataAsync(
new[] { myAvatarData, enemyAvatarData }
.Where(e => e is not null)
.OfType<AvatarData>()
.ToList());
.ToList()
);
await mongoDbStore.BulkUpsertArenaDataAsync(
new[] { myArenaData, enemyArenaData }
.Where(e => e is not null)
.OfType<ArenaData>()
.ToList());
.ToList()
);
}
}

Expand Down
8 changes: 8 additions & 0 deletions Mimir.Worker/Queries.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ query GetBattleArenaTransactions($blockIndex: Long!) {
}
}

query GetPatchTableTransactions($blockIndex: Long!) {
transaction {
ncTransactions(startingBlockIndex: $blockIndex, limit: 1, actionType: "^patch_table_sheet") {
serializedPayload
}
}
}

query GetTransactionSigners($blockIndex: Long!) {
transaction {
ncTransactions(startingBlockIndex: $blockIndex, limit: 1, actionType: "^[a-zA-Z0-9]*$") {
Expand Down

0 comments on commit b086ac3

Please sign in to comment.