Skip to content

Commit

Permalink
Merge pull request #47 from planetarium/store-all-avatar-from-every-t…
Browse files Browse the repository at this point in the history
…ransactions

Store all avatar from every transactions
  • Loading branch information
boscohyun authored May 10, 2024
2 parents fe555e0 + 5bdbdea commit 37a8a74
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 37 deletions.
126 changes: 93 additions & 33 deletions Mimir.Worker/BlockPoller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
using Mimir.Worker.Models;
using Mimir.Worker.Scrapper;
using Mimir.Worker.Services;
using Nekoyume;
using StrawberryShake;

namespace Mimir.Worker;

public class BlockPoller(IStateService stateService, HeadlessGQLClient headlessGqlClient, MongoDbStore mongoDbStore)
public class BlockPoller(IStateService stateService, IHeadlessGQLClient headlessGqlClient, MongoDbStore mongoDbStore)
{
public async Task RunAsync(CancellationToken cancellationToken)
{
Expand All @@ -18,52 +20,110 @@ public async Task RunAsync(CancellationToken cancellationToken)
var syncedBlockIndex = await mongoDbStore.GetLatestBlockIndex();
var currentBlockIndex = await stateService.GetLatestIndex();
var processBlockIndex = syncedBlockIndex + 1;

if (processBlockIndex >= currentBlockIndex)
{
await Task.Delay(TimeSpan.FromMilliseconds(3000), cancellationToken);
continue;
}

var rawArenaTxsResp = await headlessGqlClient.GetBattleArenaTransactions.ExecuteAsync(processBlockIndex);
await EveryAvatarAsync(processBlockIndex, stateGetter, cancellationToken);
await BattleArenaAsync(processBlockIndex, stateGetter, cancellationToken);
await mongoDbStore.UpdateLatestBlockIndex(processBlockIndex);
}
}

private async Task EveryAvatarAsync(
long processBlockIndex,
StateGetter stateGetter,
CancellationToken cancellationToken)
{
var operationResult = await headlessGqlClient.GetTransactionSigners.ExecuteAsync(
processBlockIndex,
cancellationToken);
if (operationResult.Data is null)
{
HandleErrors(operationResult);
return;
}

var txs = operationResult.Data.Transaction.NcTransactions;
if (txs is null || txs.Count == 0)
{
return;
}

if (rawArenaTxsResp.Data is null)
foreach (var tx in txs)
{
if (tx is null)
{
Serilog.Log.Error("Failed to get arena txs. errors:\n" +
string.Join("\n", rawArenaTxsResp.Errors.Select(x => "- " + x.Message)));
await mongoDbStore.UpdateLatestBlockIndex(syncedBlockIndex + 1);
continue;
}

try
{
var arenaTxs = rawArenaTxsResp.Data.Transaction
.NcTransactions!.Select(raw =>
TxMarshaler.DeserializeTransactionWithoutVerification(
Convert.FromBase64String(raw.SerializedPayload)))
.ToList();
var agentAddress = new Address(tx.Signer);
var avatarAddresses = Enumerable.Range(0, GameConfig.SlotCount)
.Select(e => Addresses.GetAvatarAddress(agentAddress, e));
var avatarDataArray = await Task.WhenAll(avatarAddresses.Select(stateGetter.GetAvatarData));
await mongoDbStore.BulkUpsertAvatarDataAsync(
avatarDataArray
.Where(e => e is not null)
.OfType<AvatarData>()
.ToList());
}
}

foreach (var arenaTx in arenaTxs)
{
var arenaAction = (Dictionary)arenaTx.Actions.First();
var arenaActionValues = (Dictionary)arenaAction["values"];
var enemyAvatarAddress = new Address(arenaActionValues["eaa"]);
var myAvatarAddress = new Address(arenaActionValues["maa"]);
private async Task BattleArenaAsync(
long processBlockIndex,
StateGetter stateGetter,
CancellationToken cancellationToken)
{
var rawArenaTxsResp = await headlessGqlClient.GetBattleArenaTransactions.ExecuteAsync(
processBlockIndex,
cancellationToken);
if (rawArenaTxsResp.Data is null)
{
HandleErrors(rawArenaTxsResp);
return;
}

var roundData = await stateGetter.GetArenaRoundData(processBlockIndex);
var enemyAvatarData = await stateGetter.GetAvatarData(enemyAvatarAddress);
var myAvatarData = await stateGetter.GetAvatarData(myAvatarAddress);
var myArenaData = await stateGetter.GetArenaData(roundData, myAvatarAddress);
var enemyArenaData = await stateGetter.GetArenaData(roundData, enemyAvatarAddress);
if (rawArenaTxsResp.Data.Transaction.NcTransactions is null)
{
return;
}

await mongoDbStore.BulkUpsertAvatarDataAsync(new List<AvatarData> { myAvatarData, enemyAvatarData });
await mongoDbStore.BulkUpsertArenaDataAsync(new List<ArenaData> { myArenaData, enemyArenaData });
}
}
finally
{
await mongoDbStore.UpdateLatestBlockIndex(syncedBlockIndex + 1);
}
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)
{
var arenaAction = (Dictionary)arenaTx.Actions[0];
var arenaActionValues = (Dictionary)arenaAction["values"];
var myAvatarAddress = new Address(arenaActionValues["maa"]);
var enemyAvatarAddress = new Address(arenaActionValues["eaa"]);

var roundData = await stateGetter.GetArenaRoundData(processBlockIndex);
var myAvatarData = await stateGetter.GetAvatarData(myAvatarAddress);
var enemyAvatarData = await stateGetter.GetAvatarData(enemyAvatarAddress);
var myArenaData = await stateGetter.GetArenaData(roundData, myAvatarAddress);
var enemyArenaData = await stateGetter.GetArenaData(roundData, enemyAvatarAddress);

await mongoDbStore.BulkUpsertAvatarDataAsync(
new[] { myAvatarData, enemyAvatarData }
.Where(e => e is not null)
.OfType<AvatarData>()
.ToList());
await mongoDbStore.BulkUpsertArenaDataAsync(
new[] { myArenaData, enemyArenaData }
.Where(e => e is not null)
.OfType<ArenaData>()
.ToList());
}
}

private static void HandleErrors(IOperationResult operationResult)
{
var errors = operationResult.Errors.Select(e => e.Message);
Serilog.Log.Error("Failed to get txs. response data is null. errors:\n{Errors}", errors);
}
}
7 changes: 3 additions & 4 deletions Mimir.Worker/Initializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ public Initializer(
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var started = DateTime.UtcNow;

await _tableSheetScrapper.ExecuteAsync(stoppingToken);
await _arenaScrapper.ExecuteAsync(stoppingToken);

var totalElapsedMinutes = DateTime.UtcNow.Subtract(started).Minutes;
_logger.LogInformation($"Finished Initializer background service. Elapsed {totalElapsedMinutes} minutes.");
_logger.LogInformation(
"Finished Initializer background service. Elapsed {TotalElapsedMinutes} minutes",
DateTime.UtcNow.Subtract(started).Minutes);

var poller = new BlockPoller(_stateService, _headlessGqlClient, _store);
await poller.RunAsync(stoppingToken);
Expand Down
1 change: 1 addition & 0 deletions Mimir.Worker/Mimir.Worker.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">

<PropertyGroup>
<RootNamespace>Mimir.Worker</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
8 changes: 8 additions & 0 deletions Mimir.Worker/Queries.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ query GetBattleArenaTransactions($blockIndex: Long!) {
}
}
}

query GetTransactionSigners($blockIndex: Long!) {
transaction {
ncTransactions(startingBlockIndex: $blockIndex, limit: 1, actionType: "^[a-zA-Z0-9]*$") {
signer
}
}
}
1 change: 1 addition & 0 deletions Mimir/Mimir.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<RootNamespace>Mimir</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down

0 comments on commit 37a8a74

Please sign in to comment.