¿Cómo acceder a la lista dinámica desde web3?

En mi aplicación, los jugadores pueden crear y unirse a juegos. Guardo cada juego en un mapeo (uint => Juego) y envío un evento GameCreated cada vez que se crea un juego. Mi problema es obtener de manera eficiente una lista de juegos abiertos (aún no unidos) en el lado del cliente.

Podría iterar a través de todos los objetos del juego y filtrar aquellos que se han unido, pero esto no es escalable. Al principio pensé que escuchar los eventos de GameCreated ayudaría, pero aun así tendría que filtrar usando los eventos de GameJoined, ya que no creo que pueda modificar los registros que ya se han registrado. Esto tiene el mismo problema de escalabilidad. Podría mitigarlo obteniendo solo registros de la semana pasada o lo que sea, pero realmente no me gusta esta opción.

Consideré hacer una matriz de tamaño dinámico donde cada elemento es la identificación del juego de un juego abierto, pero para actualizar esta lista, necesitaría poder eliminar las entradas cuando se uniera a un juego, lo que requeriría almacenar el índice en cada Objeto de juego. Pero luego, cuando se eliminó una entrada, todos esos otros objetos del juego tendrían que actualizarse con el nuevo índice y es simplemente un desastre.

Idealmente, esta aplicación recibirá mucho tráfico algún día, por lo que necesito una solución eficiente. ¿Hay algo obvio que me estoy perdiendo aquí? ¿Hay alguna solución mejor que hayas encontrado? Desearía poder iterar a través de un mapeo solo con los juegos abiertos actualmente, pero no creo que eso sea posible.

Respuestas (2)

¿Cuántos juegos esperas tener?

Solidity puede devolver una matriz completa a la vez, por lo que puede mantener cada juego en una matriz, devolverlo todo con una llamada y luego usar el filtrado del lado del cliente. Puede intentar simular una función que devuelve, digamos, 10,000 juegos y ver si el viejo JS .filter() lo manejará lo suficientemente rápido. (Aunque no sé qué tan rápido el nodo podrá devolver la lista completa).

Pero digamos que eso no funciona. Mi siguiente sugerencia sería una lista enlazada. No es nativo de Solidity, y es posible que deba escribir una biblioteca para facilitar su uso, pero podrá agregar y eliminar en un tiempo constante. No estoy seguro de cuánto más (¿o menos?) caro sería en gasolina, pero sospecho que no sería mucho más caro.

¿Las matrices en solidez no tienen que tener una longitud fija?
Dado que tendría que acceder secuencialmente a los elementos de la lista vinculada, no creo que realmente ofrezca una mejora con respecto a la actualización de una serie de identificadores de juegos.
@Mike: las matrices pueden tener una longitud variable, pero actualmente un contrato no puede llamar a una función que devuelve una matriz de ancho variable. Esto está programado para cambiar en la metrópolis.
Desafortunadamente, Solidity solo puede devolver una matriz de estructuras internamente. Para obtener todos los juegos de una función de Solidity, creo que tendría que convertir cada Juego en una matriz de valores y devolver una matriz de matrices, lo que suena muy costoso.
@Warkgnall solo es caro en cuanto a gasolina si se trata de una llamada en cadena. Si es una función constante, entonces puede hacer .call() gratis, que es lo que parece que quiere hacer de todos modos.

Necesitas usar una lista doblemente enlazada como esta:

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;
}