How to write/load Global Storage Objects from Client without User Id

Hello community members,

I have just started to use Nakama for 2 days. I have started to check the capability of Nakama Client SDK to understand the whole solution is suitable for me or not. I want to explain one of my scenario which I could not find a way to solve it in the Client part.

  1. In our game, users can create a game room by clicking create a game,
  2. In the lobby we show them simplified room id to share with other players like 231-123.
  3. Other players can join the same room with this simplified and readable game id.
  4. Our problem starts here, as you know Nakama generates a more complicated id for matches. To come up with this problem, we create a simplified game id and write it to storage by mapping with real match id generated by Nakama.
  5. So other users can reach this real match id by querying related collections with simplified game id.
  6. According to my researches; we can just reach storage objects if we put the owner user id of storage objects to the query.
  7. But in our case, other users don’t have the owner user id of the storage object.

My Questions

  1. I could not understand the logic behind this structure, if I put one storage object into the collection with a unique key and public read accessibility, why we need to have a user id to query this?

  2. Is there any possibility to create global storage objects which can query by all other users without the owner user id? If not, Is here any client-side solution to complete the above scenario in Nakama

I am sorry if there is a similar question from now.

Updates
As I understand from other storage related posts;
Storage engine using {collection + key + userId} as an unique identifier for each record. This is the reason i could not reach the storage object without userId.

Also when I check source code, i see that we cannot change userId as null or anything else from client side, but we can change it from server side. To make workaround, most probably I need to call custom Rpc funtion after match is created from client, I hope that I can set userID null and I can reach this storage object just using collection and key.

This approach is clear for me now, but I still wonder why client is limited to blanking userId null.

Thank you.

1 Like

Hi @msk welcome :wave:

Our problem starts here, as you know Nakama generates a more complicated id for matches. To come up with this problem, we create a simplified game id and write it to storage by mapping with real match id generated by Nakama.

The match ID in Nakama is a UUIDv4 type to ensure uniqueness when replicated between server instances. We do have an open feature request to add a more readable match ID although there is also a workaround if you use the authoritative multiplayer engine. It’s unclear from your post whether you use the relayed multiplayer feature or authoritative multiplayer engine in Nakama?

According to my researches; we can just reach storage objects if we put the owner user id of storage objects to the query.

You do not need to know the user ID of the storage object owner to find it in the storage engine. This is the reason that the storage engine has public read permissions available to set on storage objects; specifically to eliminate the need to know exactly how to look up the storage objects you want which are shared by other players.

What you want to achieve looks like it can be done with a storage collection called “active_matches” and a key named after the short match ID. The user who creates the match would write it as a storage object with public read to the collection. Any other player could then find the matches by performing a storage listing over the “active_matches” collection:

https://heroiclabs.com/docs/storage-collections/#list-objects

The challenge with the above design you’ll have is that you must somehow clear the collection of matches which are not played in some time. There’s an easier solution you can use if your matches are built with the authoritative multiplayer engine.

1 Like

Hi @novabyte :wave:t2:,
First of all thank you for developing this powerful open source platform for all game lovers. Nice to meet you.

It is good to hear there is open request about march ids, current UUIDv4 is also fine for general purposes. I can understand the reason as you said to provide uniqueness.

Actually I tried to use relayed multiplayer engine to keep server load minimum. But if need, I can think to use authoritative engine also.

What you want to achieve looks like it can be done with a storage collection called “active_matches” and a key named after the short match ID. The user who creates the match would write it as a storage object with public read to the collection. Any other player could then find the matches by performing a storage listing over the “active_matches” collection:

https://heroiclabs.com/docs/storage-collections/#list-objects

I have also tried this approach, but I used read object directly instead of listing objects. In my case, if I didn’t make mistake in code level, I could not read storage object although it has public read permission. I checked the accessibility from console also. To understand logic behind this, i have checked example game project for unity, and I could not find ant example usage without user id for read object.

You also advised list object instead of read object directly. Isn’t it possible to reach public storage object by reading one of them directly? Because for this case i always need to one record, if it is not possible, I need to query all active matches and try to find correct one in client side. It is suitable for beginning, but may cause performance and network issues when active matches and users are increased.

Thanks for your reply also :blush:

@msk Thanks for the kind words about the server technology. :bowing_man:

Actually I tried to use relayed multiplayer engine to keep server load minimum. But if need, I can think to use authoritative engine also.

I would not use server load as a decider on whether to use the relayed multiplayer feature or the authoritative multiplayer engine in the game server. Especially as we’ve got benchmarks which demonstrate great performance on really modest commodity hardware.

You should consider whether you need to optimize the game to prevent rage quits, disconnects, or bad actors that will tamper with the netcode if the game is client-authoritative (relayed). These should help you determine whether for your type of multiplayer game which you should choose to build on.

In my case, if I didn’t make mistake in code level, I could not read storage object although it has public read permission. I checked the accessibility from console also.

You cannot read a storage object that’s marked as public read without the full information about the object. This includes the user ID who owns it. You can list storage objects that are owned by other users which I mentioned above. This is the usual pattern for when you don’t know the owner of the object ahead of the read from the storage engine.

Isn’t it possible to reach public storage object by reading one of them directly? Because for this case i always need to one record, if it is not possible, I need to query all active matches and try to find correct one in client side.

I think there’s a part of your use case that’s unclear to me. You have a storage object that you write from one player which is a short code for the match ID but want to read it from another player but the other player should not know the user ID of the first player and also should not list over storage objects. :thinking:

How would these players ever be able to find each other to join the match by the short code?

Thanks for the detailed explanation @novabyte,

I totally agree with you about what we should care about when we select a relayed or authoritative engine for multiplayer. My concern was just related to my own server capability and my budget, on the other hand, I am really appreciated about performance or benchmark result of Nakama. But it is more clear now for me Thanks.

You cannot read a storage object that’s marked as public read without the full information about the object. This includes the user ID who owns it. You can list storage objects that are owned by other users which I mentioned above. This is the usual pattern for when you don’t know the owner of the object ahead of the read from the storage engine.

As you said, I have learned that I cannot reach public storage objects by directly reading them :slight_smile: I tried every way but at least I understood it is not possible. Then I applied your suggestion, I started to list all collection and then filter it by key to get just one of them in the list. If there is not any technical limitation or security concern or something prevents us from doing this, I hope we can discuss this feature to provide easy one record public storage reading.

I will try to tell my use case to make you more clear.

  1. Actually it is so similar to the Zoom mechanism,
  2. Players should know each other in real life,
  3. They will share game-id each other at the same place or via reading QR Code(for QR code UUIDV4 is not an issue also),
  4. To make this easy, I may create a friend mechanism but it is not really needed for game dynamics.
  5. In this case I don’t know the user id of the player who has a game room, they can just share gameId to join the same game room, and then they will start to play.
  6. For example, if you download my game, it will be enough to just send you my game id (111-222) to join my room.

In summary, now I solved my issue with your listing suggestion, but for similar cases and increase the capability of storage objects reading operation may reach storage objects which have public reading access. As I understand reading function is designed to return one record IApiStorageObject, without userId, we broke the uniqueness of the record so it is not supported.

Thanks for everything again, if this kind of feature is not possible, I will select your previous listing suggestion as a solution :blush: