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.
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.
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;
}
Mike Schultz
Warknall
Matthäus Schmidt
Warknall
natewelch_