Wie greife ich von web3 auf die dynamische Liste zu?

In meiner Anwendung können Spieler Spiele erstellen und daran teilnehmen. Ich speichere jedes Spiel in einem Mapping (uint => Game) und sende ein GameCreated-Ereignis, wenn ein Spiel erstellt wird. Mein Problem besteht darin, auf der Clientseite effizient eine Liste offener (noch nicht beigetretener) Spiele zu erhalten.

Ich könnte alle Spielobjekte durchlaufen und diejenigen herausfiltern, die verbunden wurden, aber dies ist nicht skalierbar. Zuerst dachte ich, dass das Abhören von GameCreated-Ereignissen helfen würde, aber ich müsste immer noch mit GameJoined-Ereignissen herausfiltern, da ich nicht glaube, dass ich bereits protokollierte Protokolle ändern kann. Dies hat das gleiche Skalierbarkeitsproblem. Ich könnte es abmildern, indem ich nur Protokolle der letzten Woche oder was auch immer bekomme, aber ich mag diese Option nicht wirklich.

Ich habe überlegt, ein Array mit dynamischer Größe zu erstellen, bei dem jedes Element die Spiel-ID eines offenen Spiels ist, aber um diese Liste zu aktualisieren, müsste ich in der Lage sein, Einträge zu löschen, wenn einem Spiel beigetreten wird, was das Speichern des Index in jedem erfordern würde Spielobjekt. Aber dann, wenn ein Eintrag gelöscht wurde, müssten all diese anderen Spielobjekte mit dem neuen Index aktualisiert werden, und es ist nur ein Durcheinander.

Idealerweise wird diese Anwendung eines Tages viel Verkehr bekommen, also brauche ich eine effiziente Lösung. Gibt es etwas Offensichtliches, das ich hier vermisse? Gibt es bessere Problemumgehungen, auf die Sie gestoßen sind? Ich wünschte, ich könnte ein Mapping nur mit den aktuell geöffneten Spielen durchlaufen, aber ich glaube nicht, dass das möglich ist.

Antworten (2)

Wie viele Spiele erwarten Sie?

Solidity kann ein ganzes Array auf einmal zurückgeben, sodass Sie jedes Spiel in einem Array behalten, alles mit einem Aufruf zurückgeben und dann die clientseitige Filterung verwenden können. Sie könnten versuchen, eine Funktion nachzuahmen, die beispielsweise 10.000 Spiele zurückgibt, und zu sehen, ob das einfache alte JS .filter() schnell genug damit umgehen kann. (Obwohl ich nicht weiß, wie schnell der Knoten die gesamte Liste zurückgeben kann.)

Aber sagen wir mal, das geht nicht. Mein nächster Vorschlag wäre eine verknüpfte Liste. Es ist nicht in Solidity nativ, und Sie müssen möglicherweise eine Bibliothek dafür schreiben, um die Verwendung zu vereinfachen, aber Sie könnten es in konstanter Zeit hinzufügen und löschen. Ich bin mir nicht sicher, wie viel mehr (oder weniger?) Benzin kosten würde, aber ich vermute, dass es nicht so viel teurer wäre.

Müssen Arrays in Solidität nicht eine feste Länge haben?
Da ich nacheinander auf Elemente in der verknüpften Liste zugreifen müsste, glaube ich nicht, dass es wirklich eine Verbesserung gegenüber der Aktualisierung einer Reihe von Spiel-IDs bietet.
@Mike: Arrays können eine variable Länge haben, aber derzeit kann ein Vertrag keine Funktion aufrufen, die ein Array mit variabler Breite zurückgibt. Das soll sich in der Metropole ändern.
Leider kann Solidity intern nur ein Array von Strukturen zurückgeben . Um alle Spiele aus einer Solidity-Funktion zu erhalten, müsste ich jedes Spiel in ein Array von Werten konvertieren und ein Array von Arrays zurückgeben, was sehr teuer klingt.
@Warkgnall es ist nur dann teuer, wenn es sich um einen On-Chain-Anruf handelt. Wenn es sich um eine konstante Funktion handelt, können Sie .call() kostenlos ausführen, was sich so anhört, als würden Sie es sowieso tun wollen.

Sie müssen eine doppelt verknüpfte Liste wie folgt verwenden:

struct GameListItem {
  uint prev;
  uint next;
}

mapping (uint => GameListItem) public openGamesList;
uint public firstOpenGame;

function addOpenGame (uint gameID) internal {
    require (gameID != 0, "Game ID is NULL");
    GameListItem storage item = openGamesList [gameID];
    require (item.next == 0, "Game is already in the list");

    if (firstOpenGame == 0) {
        item.next = gameID;
        item.prev = gameID;
        firstOpenGame = gameID;
    } else {
        GameListItem storage first = openGamesList [firstOpenGame];
        uint lastID = first.prev;
        GameListItem storage last = openGamesList [lastID];

        item.next = firstOpenGame;
        item.prev = lastID;
        first.prev = gameID;
        last.next = gameID;
    }
}

function removeOpenGame (uint gameID) internal {
    require (gameID != 0, "Game ID is NULL");
    GameListItem storage item = openGamesList [gameID];
    uint nextID = item.next;
    require (nextID != 0, "Game is not in the list");

    uint prevID = item.prev;
    if (nextID == gameID)
        firstOpenGame = 0;
    else {
        GameListItem storage next = openGamesList [nextID];
        GameListItem storage prev = openGamesList [prevID];

        next.prev = prevID;
        prev.next = nextID;

        if (firstOpenGame == gameID) firstOpenGame = nextID;
    }

    item.next = 0;
    item.prev = 0;
}