Return empty when write in db and try to read with ReadStorageObjectsAsync

Hello,
I’m working on a process that calls an rpc that reads and writes to the database to update storage (a monster) information in the game I’m developing.

The game has a screen where it’s possible to update some monster stats like health, strength, etc. With the rpc call, the database is updated, and these stats are correctly displayed on that screen to the player.

The problem arises when the player tries to use an item that allows them to redistribute points. This item makes a call to the server to reset the stats to their initial values (which are random for each monster). The call to reset works perfectly, but the read, using ReadStorageObjectsAsync, returns an empty array, even though the same call is made for the point distribution.

I am using client version 3.9.0 and backend version 3.16.0

  1. Code used to make the ReadStorageObjectsAsync call
 private async UniTask<IEnumerable<MonsterDbo>> LoadMonstersFromDb(string[] monstersIds)
        {
            List<MonsterDbo> monsters = new List<MonsterDbo>();
            const string monstersCollection = "Monsters";
            IApiStorageObjects storageObjectList = null;
            StorageObjectId[] ids = new StorageObjectId[monstersIds.Count()];

            for (int i = 0; i < monstersIds.Length; i++)
            {
                ids[i] = new StorageObjectId { Collection = monstersCollection, Key = "nftId-" + monstersIds[i] };
            }

            try
            {
                storageObjectList = await _client.ReadStorageObjectsAsync(_session, ids);
            }
            catch (Exception exception)
            {
                ErrorResponse(exception.Message);
            }
            ...
        }
  1. Output logs when there is a write, of the stats on the monster.
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.992Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:387  handleBeforeReadStorageObjects"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.992Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:388  ctx map[string]interface {}{\"..."}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.992Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:389  data map[string]interface {}{\"objectIds\":[]interface {}{map[string]interface {}{\"collection\":\"Monsters\", \"key\":\"nftId-2901\"}}}"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.992Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:390  handleBeforeReadStorageObjects FINISHED"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.993Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:402 handleAfterReadStorageObjects"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.993Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:403  ctx map[string]interface {}{\"..."}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.993Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:404  data map[string]interface {}{\"objects\":[]interface {}{map[string]interface {}{\"collection\":\"Monsters\", \"createTime\":\"2023-06-20T11:51:56Z\", \"key\":\"nftId-2901\", \"permissionRead\":2, \"updateTime\":\"2023-11-09T20:15:11Z\", \"userId\":\"00000000-0000-0000-0000-000000000000\",...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.993Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:405  request map[string]interface {}{\"objectIds\":[]interface {}{map[string]interface {}{\"collection\":\"Monsters\", \"key\":\"nftId-2901\"}}}"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:15:11.993Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:406 handleAfterReadStorageObjects FINISHED"...}
  1. Output logs when there is a read, when the error occurs.
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:387  handleBeforeReadStorageObjects"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:388  ctx map[string]interface {}{\"..."
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:389  data map[string]interface {}{\"objectIds\":[]interface {}{map[string]interface {}{\"collection\":\"Monsters\", \"key\":\"nftId-2901\"}}}"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:390  handleBeforeReadStorageObjects FINISHED"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:402 handleAfterReadStorageObjects"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:403  ctx map[string]interface {}{\"..."}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:404  data map[string]interface {}{}","api_id":"readstorageobjects","mode":"after"}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:405  request map[string]interface {}{\"objectIds\":[]interface {}{map[string]interface {}{\"collection\":\"Monsters\", \"key\":\"nftId-2901\"}}}"...}
backend-nakama-1 {"level":"info","ts":"2023-11-09T20:44:43.685Z","caller":"server/runtime_javascript_logger.go:74","msg":"file: main.ts:406 handleAfterReadStorageObjects FINISHED"...}

Image of the first point showing the result of ReadStorageObjectsAsync when it works fine.

Image of the second point showing the result of ReadStorageObjectsAsync when the error occurs

Updating Nakama version to 3.19 and Nakama common to 1.30 still gives the same error

Hello @gabrielmontoto,

Please share the code snippet for how you’re writing the object.

Hi, here is the code:

const outBattleUseItemEffects: IdMap<ItemOutOfBattleEffect> = {
        [ItemEffectId.RedistributePointsEffect]: {
            outOfBattleEffect: redistributePointsOutOfBattleEffect,
        },
    }
function redistributePointsOutOfBattleEffect(
        ctx: nkruntime.Context,
        nk: nkruntime.Nakama,
        logger: nkruntime.Logger,
        item: Item,
        amount: number,
        monster?: Monster
    ): MultiUpdateWrite {
        let canUse = false

        if (!monster) throw MONSTER_NOT_FOUND
        let currentMonster: Monster = monster

        for (let i = 0; i < currentMonster.attributes.length; i++) {
            if (currentMonster.attributes[i].baseValue !== currentMonster.attributes[i].initialValue) {
                canUse = true
            }
        }

        if (!canUse) throw REDISTRIBUTE_POINTS_CANNOT_USE

        for (let i = 0; i < currentMonster.attributes.length; i++) {
            currentMonster.attributes[i].baseValue = currentMonster.attributes[i].initialValue
            currentMonster.attributes[i].currentValue = currentMonster.attributes[i].initialValue
        }

        const writeKey: nkruntime.StorageWriteRequest = {
            collection: monstersCollectionName,
            key: keyPrefixNft + currentMonster.nftId,
            userId: globalUserId,
            value: currentMonster,
        }

        return {
            accountUpdate: null,
            storageWriteKeys: [writeKey],
            walletUpdate: [],
        }
    }

second part

function useItemRpc(ctx: nkruntime.Context, logger: nkruntime.Logger, nk: nkruntime.Nakama, payload: string): string {
    if (!ctx.userId) {
        throw USE_ITEM_USER_ID_UNDEFINED
    }

    if (!payload) {
        throw USE_ITEM_PAYLOAD_UNDEFINED
    }

    let parsedPayload: UseItemPayload = JSON.parse(payload) as UseItemPayload

    let itemQuantity = parsedPayload.quantity || 1
    if (itemQuantity == 0) itemQuantity = 1

    const removeItemQuantity = Math.abs(itemQuantity) * -1
    const removeItemStack: ItemStack = {
        quantity: removeItemQuantity,
        targetId: parsedPayload.itemId,
    }

    let multiUpdateKeys = outBattleUseItemEffects[parsedPayload.effectId].outOfBattleEffect(ctx, nk, logger, item, itemQuantity, monster)

    const itemChangeset = ItemStackChangesetWriteKeys(ctx.userId, nk, [removeItemStack], logger) // This is a inventory/item changeset

    if (!itemChangeset) {
        throw ITEM_EFFECT_DOES_NOT_HAVE_ITEM
    }

    multiUpdateKeys.storageWriteKeys.push(...itemChangeset)

    nk.multiUpdate(null, multiUpdateKeys.storageWriteKeys, multiUpdateKeys.walletUpdate)

    response.success = true
    return JSON.stringify(response)
}

Could you please work out a single test case or code to reproduce this? We’ve not encountered this issue before nor am I able to reproduce it.