Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Grabbing item from the inventory

Discussion in 'Cloud Code' started by Corva-Nocta, Feb 9, 2023.

  1. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Hi all,
    I'm working on some server side logic and am trying to get the player inventory, I think cloud code is going to be the best way for me to go but I am having some trouble getting the cloud code to work. I might just be looking in the wrong place, so if this does exist somewhere please let me know!

    So I have this in my cloud code:
    Code (csharp):
    1.  
    2. const { InventoryApi } = require("@unity-services/economy-2.3");
    3. module.exports = async ({params, context, logger}) => {
    4. const { projectId, playerId } = context;
    5. const inventory = new InventoryApi(context);
    6. const result = await inventory.getPlayerInventory({ projectId, playerId });
    7. return result.data;
    8. }
    which seems like an obvious starting point. So I am guessing I need to take that "result" const and check to see if it has an item. Let's just say its the sword item from the inventory examples. So if I want to check if the player has "sword" for do I do that here in the cloud code? Even better, if I just want the custom parameters to be sent back (like the "rarity" in the economy example) so I can just call this cloud code script with a playerID and an item name and it would just return the parameters (assuming the player has the item)

    Any help at all here would be greatly appreciated!
     
  2. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Also just realized I posted this to Cloud Save and not Cloud Code.... oops! If any mods want to delete this one feel free. Gonna make the post on the proper board now.
     
    SebT_Unity likes this.
  3. Laurie-Unity

    Laurie-Unity

    Unity Technologies

    Joined:
    Mar 5, 2020
    Posts:
    220
    Hi @Corva-Nocta,

    I've moved your post over to the Cloud Code forum.

    You are on the right track.

    One thing to point out though,
    CustomData
    refers to parameters that you define and attach to an Inventory Item definition. When a player picks up an inventory item they are holding an Instance of it that does not contain any custom data but does contain an empty
    InstanceData
    object. Take a look at the Custom Data & Instance Data documentation.

    You would therefore probably want to copy the
    Custom Data
    or certain parameters from it, from the item definition to the
    Instance Data
    as the player receives the item, so you can access and potentially modify the instance's configuration over time. e.g. think about decreasing durability as the item is used.

    With that in mind, here are some code snippets that might help.


    A Cloud Code script to retrieve player inventory

    Code (CSharp):
    1. /*
    2. *  -------- Cloud Code Script ----------------
    3. *  Get Player Inventory - iterate over pages
    4. * --------------------------------------------
    5. */
    6. const { InventoryApi } = require("@unity-services/economy-2.4");
    7.  
    8. module.exports = async ({ params, context, logger }) => {
    9.  
    10.   const {
    11.     projectId,
    12.     playerId,
    13.     accessToken
    14.   } = context;
    15.  
    16.   const inventoryApi = new InventoryApi(context);
    17.  
    18.   // Default to pages of 20 inventory items if none specified as input params.pageSize
    19.   const pageSize = params.pageSize != undefined ? params.pageSize : 20 ;
    20.   let pages = 0 ;
    21.   let inv = [];
    22.  
    23.   // get a page worth of inventory items
    24.   let playerInventory = await inventoryApi.getPlayerInventory({projectId, playerId, limit:pageSize});
    25.  
    26.   // When we have results
    27.   while( playerInventory.data.results.length > 0) {
    28.  
    29.       // Add them to the local inv array
    30.       playerInventory.data.results.forEach( i => inv.push(i));
    31.  
    32.       // Grab the playersInventoryItemId of the last one
    33.       lastPlayersInventoryItemId = playerInventory.data.results[playerInventory.data.results.length -1].playersInventoryItemId;
    34.  
    35.       // Then go looking for another page worth
    36.       playerInventory = await inventoryApi.getPlayerInventory({projectId, playerId, limit: pageSize, after: lastPlayersInventoryItemId});
    37.         pages ++;
    38.   }
    39.  
    40.   // Return the JSON result to the client
    41.   return {
    42.     inventory: inv,
    43.     pages: pages
    44.   };
    45. };

    Is called from this C# client side script
    Code (CSharp):
    1.     public async void GetInventoryFromCloudCodeButtonClicked()
    2.     {
    3.         Dictionary<string, object> requestParams = new Dictionary<string, object>();
    4.         requestParams.Add("pageSize", 15);
    5.    
    6.         ResultType response = await CloudCodeService.Instance.CallEndpointAsync<ResultType>("getInventory", requestParams);  
    7.         InventoryResponse[] playerInventoryArray = response.inventory;
    8.  
    9.         Debug.Log($"Returned {playerInventoryArray.Count()} items of inventory");
    10.         Debug.Log($"pages {response.pages}");
    11.  
    12.         foreach(InventoryResponse playerInventoryItem in playerInventoryArray) {
    13.             Debug.Log($"itemId : {playerInventoryItem.inventoryItemId} ");
    14.             Debug.Log($"playersInventoryItemId : {playerInventoryItem.playersInventoryItemId }");
    15.  
    16.             string instanceData = playerInventoryItem.instanceData != null ? playerInventoryItem.instanceData.ToString() : "NULL";
    17.             Debug.Log($"InstanceData : {instanceData} ");
    18.        
    19.         }
    20.     }
    Assuming these two classes have been defined to work with the repsonse from the Cloud Code script.

    Code (CSharp):
    1.     private class ResultType
    2.     {
    3.         public InventoryResponse[] inventory;
    4.         public int pages;
    5.     }
    6.  
    7.     private class InventoryResponse
    8.     {
    9.         public JObject created;
    10.         public JObject instanceData;
    11.         public string inventoryItemId;
    12.         public JObject modified;
    13.         public string playersInventoryItemId;
    14.         public string writeLock;
    15.     }
    I hope that helps
     
    Last edited: Feb 14, 2023
    Corva-Nocta likes this.
  4. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    So I am trying to use some of the code you posted, seems fairly straight forward but I am getting a tiny error. I want to combine the cloud code for grabbing the inventory with the code of grabbing another player's inventory item. Here's what I've got:

    Code (csharp):
    1. const { InventoryApi } = require("@unity-services/economy-2.3");
    2. module.exports = async ({ params, context, logger }) => {
    3.   const { projectId, playerId } = context;
    4.   const { playerID } = params;
    5.   const inventory = new InventoryApi(context);
    6.  
    7.   const playerCheck = await inventory.getPlayerInventory({
    8.     projectId,
    9.     playerId: { playerID },
    10.     inventoryItemIds: "PLAYERDATA" });
    11.  
    12.   if (playerCheck.data.results.length > 0)
    13.   {
    14.     return playerCheck.data;
    15.   }
    16.   else
    17.   {
    18.     const createdInv = await inventory.addInventoryItem({
    19.       projectId,
    20.       playerID,
    21.       addInventoryRequest:
    22.       {
    23.         inventoryItemId: "PLAYERDATA",
    24.         instanceData:
    25.         {
    26.           "Chunk": 0,
    27.           "World": "Main",
    28.           "X": 0,
    29.           "Y": 0,
    30.           "Z": 0
    31.         }
    32.       }
    33.     });
    34.     return playerCheck.data;
    35.   }
    36. }
    playerID is a param that is required to run the code.

    and using this code in C#:
    Code (csharp):
    1. public async void GetPlayerInfo(string playerName)
    2.     {
    3.         Dictionary<string, object> requestParams = new Dictionary<string, object>();
    4.         requestParams.Add("playerID", playerName);
    5.         PlayerData pData = await CloudCodeService.Instance.CallEndpointAsync<PlayerData>("InitializePlayer", requestParams);
    6.         Debug.Log(pData);
    7.     }
    but I get this error in the editor:
    ArgumentException: Failed to lookup discriminator value for problems/invocation/axios. Possible values: problems/basic, problems/invocation, BasicErrorResponse, InvocationErrorResponse
    Unity.Services.CloudCode.Internal.Models.RunScript422OneOf.GetConcreteType (System.String type) (at Library/PackageCache/com.unity.services.cloudcode@2.1.2/Runtime/com.unity.services.cloudcode.internal/Models/RunScript422OneOf.cs:69)

    The rest of the error seems to be pretty much this same thing.

    Not sure why this isn't working. I tried a few different things that the errors were giving me about the "playerId" being null or incorrect, so I changed it to its current state. I think its done correctly, I'm not getting the error about the playerId anymore at least, but I have no idea what this error means. Any ideas?
     
  5. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    It is currently not possible to access another player's inventory (or add items to it). I hope they will support this soon. Adding items from a Game Server as rewards to players is a common use case.

    My current workaround is to use CloudSave and add a "pending items" field. When the player retrieves their inventory via CloudCode, it will process the pending items into their actual inventory.

    You can probably do something similar for your case by building the list of the player's items and store it in CloudSave each time they log out. It won't be realtime, but it may be good enough.
     
  6. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Ugh really? What is the point of the economy system then? lol. Not gonna lie, that's a bit frustrating.

    I started with doing things through client side, which was ok but super inefficient and annoying, and also just for testing before doing server side. Guess I'll just have to stick to client side (But at least I know how to do cloud code for it now!)

    The cloud save option might work though. I am only saving and loading the inventory when a player logs in/out, so its not needed to be done constantly. I'll see what I can figure out for it.
     
  7. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    As long as the player doesn't have an enormous inventory, it may work just fine for you. All you need to save are the item ids. A hundred items may only use KBs of data.
     
  8. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I think I actually do have it working now! Economy 2.3 doesn't let it work, but Economy 2.4 appears to have it working. Well sort of. Its working when I run the test code, but when I look back in the inventory its not there, which might just be a timing bug. When I run the code again the item is definitely there, so I think going through the Find Player option just doesn't update very fast. But everything appear to be working for dealing with other playerIDs

    Just tested in the editor, and it does work! We can do server side saving and altering now!
     
    Last edited: Feb 24, 2023
  9. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    Hmm...I'll give it a try. Not sure when 2.4 was released.
     
  10. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I just gave it a try with 2.4 and was not successful. Are you certain you're retrieving/modifying the items of another player's account and not your own?

    I got the following error which states that I'm not permitted to access this user's data:
    "response": {
    "code": 53,
    "detail": "you are not permitted to access this user's data",
    "instance": null,
    "status": 403,
    "title": "Error",
    "type": "problems/basic"
    }
     
  11. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Hm, odd. That was definitely the same error I was getting. I now don't get the error, and it seems to be working, but I'll do some more testing. As far as I can tell it's not doing anything to the server's profile, it seems to be working with the other player ID. The odd part that I do see though is that the new player doesn't appear when I go to find a player. So I am really not sure what is going on.

    I'll try to get some tests this weekend to see what's going on. I'm traveling a bunch for work but I'll try to get what I can
     
  12. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    Oh, it seems to be working now. This opens the door to a lot of things.

    I have to have this line:
    const economy = new InventoryApi(context);

    instead of:
    const economy = new InventoryApi({ accessToken });
     
    vaudevillian8 likes this.
  13. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    If you want the code I am currently running:

    Code (csharp):
    1. const { InventoryApi } = require("@unity-services/economy-2.4");
    2.  
    3. module.exports = async ({ params, context, logger }) => {
    4.   const { projectId } = context;
    5.   const { playerName } = params;
    6.   const inventory = new InventoryApi(context);
    7.  
    8.   const playerCheck = await inventory.getPlayerInventory({
    9.     projectId,
    10.     playerId: { playerName },
    11.     inventoryItemIds: "PLAYERDATA" });
    12.  
    13.   if (playerCheck.data.results.length > 0)
    14.   {
    15.     return playerCheck.data;
    16.   }
    17.   else
    18.   {
    19.     const createdInv = await inventory.addInventoryItem({
    20.       projectId,
    21.       playerId: { playerName },
    22.       addInventoryRequest:
    23.       {
    24.         inventoryItemId: "PLAYERDATA",
    25.         instanceData:
    26.         {
    27.           "Chunk": 0,
    28.           "World": "Main",
    29.           "X": 0,
    30.           "Y": 0,
    31.           "Z": 0
    32.         }
    33.       }
    34.     });
    35.     return playerCheck.data;
    36.   }
    37. }
    no major changes, but I changed playerID to playerName to make sure I wasn't confusing myself. When I run it things seem to work just fine, but I am not 100% positive I'm just adjusting the server's data and not the other player.
     
    vaudevillian8 likes this.
  14. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    It's working now. The difference here is that you can now pass in your service token to make changes to other player accounts which wasn't possible in 2.3.
     
  15. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Awesome!!! Glad it's working!

    Can you see your other players in the Find Player tab? I still can't and it worries me haha. I'm thinking it is probably because I'm not passing a service token, just the name of the user.

    Do I need to create a new player profile when a new player connects to the server to be able to get the service token? Or is that something that works automatically?
     
  16. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I'm not sure what you mean by "Find Player tab". The service token is part of the context object which is automatically included when you pass it like this "const inventory = new InventoryApi(context)".
     
  17. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Oh right! On the UGS dashboard under the Economy section, the tab for Find Player. When I run the cloud code, I don't see players added through the cloud code when I run it from Unity. It does create player profiles when I run through the cloud code test though
     
  18. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I think when running from the website, the accounts are temporary. You should still be able to search for the user id.
     
  19. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Yeah the accounts only last for an hour, but the data that gets created for them lasts forever. If I create an inventory for example, after that hour time limit the inventory will persist.

    So the weird part I'm having is even when I try searching with the user ID accounts created through Unity don't show up. It's a bit odd. I'm thinking that I need to involve passing the user token like you do to make that work. Currently I'm not using any information involving the user token. Though if I remember correctly, aren't user tokens also a temporary id?
     
  20. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I'm not passing in the token at all. All you should need is the user id (or player id). The user ids are usually much shorter than the token. Tokens are not the same as user ids.

    Tokens will expire and gives you temporary access to a resource. The user id identifies an account and are permanent.
     
  21. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    That's how I thought they worked, so that's good I had it right! Currently I'm just passing the userID, so that should be all I need. Just not sure why they don't show up with find player.

    Might just be an odd little bug. I've noticed when I do some things on the economy service it doesn't update the display automatically, but the information is changed. I can run a script that will change or add inventory items and the script shows that it worked (in Unity or running the demo) but searching through the inventory on Find Player doesn't show the update immediately. It doesn't appear to be consistent when it happens. So I'm thinking it's just a display error that I'm overthinking. Gonna try to test it more when I get the chance this weekend.
     
  22. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I haven't ran into this issue yet. How long is the wait before changes get propagated? I'm sure the system uses "eventual consistency" so it may take a second before things are updated.
     
  23. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    That's what I thought but it seems it never works when doing it the current way.

    Much earlier in the process when I was adding inventory items and such to the temp ID created by the Dashboard website, it would take anywhere from 2 seconds to a minute to update the player list. Not really a bit deal, it seemed to only happen when I was using a brand new ID. I am guessing it was taking time to create the new profile.

    Now however I am not using the random ID created by the site, I am giving it an ID by using the params. But when I do it this way for an account that wasn't there on the Find Player tab, I don't see a new player created. Even days later. As far as I can tell all the code is working, adding items and updating items, it just doesn't display the player. So its a little annoying, but appears to at least be working.
     
  24. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    You can't specify your own user ID to Unity Gaming Service. Unity generates the User IDs randomly.
     
  25. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I am currently just passing an ID and using that to be the profile ID. So Unity gives me a random ID like uidshf, but as a param I am giving it "testcorva", so all the info is saved under "testcorva", but there is no profile named"testcorva" that appears when I go to find a player.

    When I run the code, it also does not create a profile for the temporary ID (so uidshf in this example) which is what I want. If I were to remove the part of the code that is applying all the code to the ID from params, it would normally create a new save (in this case as uidshf)
     
  26. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I'm still confused why you're passing in "testcorva" or "uidshf". The user ID is (randomly) generated from Unity Services so you have no control over it. I'm not sure if you actually mean the user id. Are you referring to item IDs or cloudsave keys?
     
  27. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    yes the user id, which I am now getting from the params.
     
  28. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I haven't experienced this issue. Maybe I just don't understand the problem fully without seeing the code. When I create new accounts and populate the inventory or cloudsave, I see them immediately.
     
  29. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Oh that might be part of it, I'm not using cloudsave, just cloudcode.

    When I run this code with the randomly generated ID:
    Code (csharp):
    1. const { InventoryApi } = require("@unity-services/economy-2.4");
    2.  
    3. module.exports = async ({ params, context, logger }) => {
    4.   const { projectId, playerId } = context;
    5.   const inventory = new InventoryApi(context);
    6.  
    7.   const playerCheck = await inventory.getPlayerInventory({
    8.     projectId,
    9.     playerId,
    10.     inventoryItemIds: "PLAYERDATA" });
    11.  
    12.   if (playerCheck.data.results.length > 0)
    13.   {
    14.     return playerCheck.data;
    15.   }
    16.   else
    17.   {
    18.     const createdInv = await inventory.addInventoryItem({
    19.       projectId,
    20.       playerId,
    21.       addInventoryRequest:
    22.       {
    23.         inventoryItemId: "PLAYERDATA",
    24.         instanceData:
    25.         {
    26.           "Chunk": 0,
    27.           "World": "Main",
    28.           "X": 0,
    29.           "Y": 0,
    30.           "Z": 0
    31.         }
    32.       }
    33.     });
    34.     return playerCheck.data;
    35.   }
    36. }
    then it will create a new player with the ID that was randomly generated, which I can find using Find Player. Takes a few seconds sometimes, but generally its pretty quick.

    However if I run this code (even with the randomly generated ID) that uses a player name that I am passing to it:
    Code (csharp):
    1. const { InventoryApi } = require("@unity-services/economy-2.4");
    2.  
    3. module.exports = async ({ params, context, logger }) => {
    4.   const { projectId } = context;
    5.   const { playerName } = params;
    6.   const inventory = new InventoryApi(context);
    7.  
    8.   const playerCheck = await inventory.getPlayerInventory({
    9.     projectId,
    10.     playerId: { playerName },
    11.     inventoryItemIds: "PLAYERDATA" });
    12.  
    13.   if (playerCheck.data.results.length > 0)
    14.   {
    15.     return playerCheck.data;
    16.   }
    17.   else
    18.   {
    19.     const createdInv = await inventory.addInventoryItem({
    20.       projectId,
    21.       playerId: { playerName },
    22.       addInventoryRequest:
    23.       {
    24.         inventoryItemId: "PLAYERDATA",
    25.         instanceData:
    26.         {
    27.           "Chunk": 0,
    28.           "World": "Main",
    29.           "X": 0,
    30.           "Y": 0,
    31.           "Z": 0
    32.         }
    33.       }
    34.     });
    35.     return playerCheck.data;
    36.   }
    37. }
    It will do the same creation of a player with data that I can grab and modify, however it will not show up when I go to Find Player. I can run other scripts that do the same idea with replacing playerId with playerId: {playerName} and I can add items and update items under the passed playerName (regardless of what the randomly generated ID is)
     
  30. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    So the playerName is not actually a user id that was generated by UGS (Unity Gaming Service)? If so, why are you creating new accounts this way? Accounts should only be created using Unity Authentication.
     
  31. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Correct, not actually created by UGS

    My origional thought was that new accounts could be created by a server, effectively turning it into a database system that is controlled by the server. Authentication works great for having data that is kept client side, but is proving very difficult if I want it to be all server side. Now I'm trying to find a good approach that will work.

    I don't want to grab data client side since the server still needs to know what the player has. Meaning if I want to save everything on the client side created authentication, I'll have to grab that data client side then send it to the server. Kinda backwards from the way it should be done.

    I'm trying to find if there is a way to make it work with cloud code to figure out how to keep everything cohesive.
     
  32. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I think the way you're doing it is not actually the right way. It can still be server authoritative even if you used Authentication. Everything else can run through CloudCode to keep it server authoritative. What you're doing is kind of a hacky solution that will likely break once Unity roll out new updates.

    The end-user should always authenticate to access data to their game. If anyone can simply pass a user id to access an account, they can easily hijack someone else's account. This would be a major security vulnerability.

    Also, Unity now has REST APIs to block client-side access to resources (such as cloudsave or economy):
    https://forum.unity.com/threads/cloud-save-data-only-accessible-by-server-code.1398967/#post-8856109
     
    Last edited: Mar 7, 2023
  33. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    That is almost 100% true haha. Its the only way I can figure out how to make it work with the system that they have in place. Kinda was hoping it would be a little more catered to server side database, but I can see the utility in what they have built. Its just not quite what I'm looking for I think, but just barely works the way I want. And integrates well which is the part I love. I was trying to get a MySQL database working before which was ok, but the integration made it annoying to deal with.

    That I would be very interested in! I don't need much to be server side authoritative, mostly just the saving and loading of the inventory really, and the saving and loading of the player. And pretty much just need it to work when a player connects to the server or leaves the server.

    What I couldn't find was a way to make this work from the server, in that if I have a player that logs out, how would I have the position and inventory of the player get saved? Having it saved client side is pretty simple, just send some code to the economy or maybe even cloud code to save it. But when that same player loads back in, I have to get the info from the client side system, then send it to the server, which seems like it would be pretty easy to get around down the road. Unless there is a way to do all of that through cloud code?

    Or is there a way to just log in with my player, does the client side Authenticate, then sends a signal to the server to tell it that the player logged in and to get its information? There are just a ton of ways I can think to do this, but not sure what works with this system.

    Or in a more pseudocode system:
    //player logs in with Authenticate (client side)
    //Authenticate tells the server to connect us
    //we connect to the server
    //server grabs our data, like inventory and position
    //server spawns our chatacter and sends player info back to player
    //player gets info about their player and can statt playing

    The idea is once I get it working make it so you have to send username and password to get access to an account, so still safe. Would be much safer with the authenticate systems that are in place though, but I don't see how I can effectively get that to work with a server authoritative game.

    That is interesting. I wonder if I can modify my system to work with that. I haven't messed with cloudsave much yet, maybe I need to start looking there.
     
    Last edited: Mar 7, 2023
  34. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    Actually I got everything to work with their system with all the server-side logic for critical stuff. The main benefit of a managed system is scalability. A typical database system cannot easily scale with millions of users with tens of thousands of users accessing it at the same time.

    When you say server side, do you mean UGS or your own Game Servers? Any changes made using the CloudSave or Economy API is persistent. You can modify any player's inventory however you please using CloudCode. For game states, you can also modify any player's save state using CloudSave.

    When the player connects to your Game Server, you can pass the player's user id (from Authentication) to the Game Server. The game server can then make calls to CloudCode to modify the player's inventory & save states (CloudSave).

    Doing things this way will incur a lot of read/writes which can be costly. You don't really want Game Servers to be constantly reading/writing to UGS. What you want to do is to "sign" the player's items via cloudcode from the client and pass that to the Game Server for it to get the player's inventory. When the player disconnects, the Game Server can then save the player's state to CloudSave.
     
  35. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Oh that is good to know! Gives me hope I can eventually get this system up and running haha. I don't think I have a ton that needs to work witht the UGS, so I'm fairly certain its possible. Largely inventory and equipment management is what I need. Which the economy system seems ok at.

    My own server. I have two projects, one that is all the server side code, and one that is all the client side code. I know there are ways to put them both into a single project, but I tried it and greatly prefer to keep them separate. Helps me keep my projects organized in my head.

    The current setup is the player sends the playername (later password too) from the client to the server (TCP/UDP connection). This both connects the client to the server, and sends the name. Then the server checks if there is a player under that name (if not, makes one) with cloud code. Then checks the player's inventory. Then sends the player information and the inventory from the server to the client directly (TCP/UDP connection) That then spawns the player on the client side and can then play the game. Right now everything works, except for the backend parts which is why I have been here on the forums so much haha. The backend systems are good, I just need to get familiar with them.

    Oh I didn't know you could do it that way. That honestly sounds like a way better system than what I am doing lol. I should try that out. Yeah if I can use cloud code to directly modify a player's inventory, the same system they use to authenticate with, then it should be really easy for me to get the system working.

    (I should preface: I didn't know it worked that way through the lens of my game, which is split into two projects. So All the Economy and cloud code is also split into two projects. So if I want to affect the client projects, I'll need to pass the playerId and the projectId, which I don't know if you can do yet)

    Do you have the server grab the player inventory AND the client grab the player inventory as well? That would be two cloud code calls for the same info. Or should I just have the server grab that info then send it back to the player with the TCP/UDP connection?

    My plan right now is to only need the UGS when a player first connects and when logging out. I don't want to keep updating the inventory every time a player grabs an item for instance. The server tracks things like the player inventory really well, so I don't need to worry about that. I really just need it to grab the stuff that is saved.
     
    Last edited: Mar 7, 2023
  36. lsaeteurn

    lsaeteurn

    Joined:
    Jan 26, 2023
    Posts:
    93
    I have my players first authenticate with UGS and then call CloudCode to generate a token used for authenticating with the Game Server. When player connects with Game Server, it sends the user id along with the token that was generated from the user id. The token is used to validate the user id of the player.

    I generate the token using a secret key and SHA-256. The secret key is only known to Game Servers and CloudCode.

    Example:
    GenerateUserIdToken() {
    return SHA(userId + secretKey + timestamp) + "." + timestamp;
    }

    The Game Server uses the secret key to validate the player's user id (you can't simply trust that the player is who they say they are). You want to make sure all server-side CloudCode scripts have a signature field which you can validate with the secret key & SHA-256.

    I have my players call a CloudCode to get their inventory. You can use SHA-256 & secret key to sign the inventory items. The player can then send the inventory to the Game Server, and the Game Server can validate that the items came from UGS by validating with SHA-256 & secret key.

    A lot of this stuff requires some knowledge of cryptography and network security.

    In regards to using multiple projects, I'm able to link more than 1 projects to the same Project ID in UGS, so you shouldn't need to worry about passing in the project id or environment id to CloudCode.
     
    Last edited: Mar 7, 2023
  37. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    oh nice! I was using pretty much the same system before, but not through cloud code and without the security. It was finicky with the way I had it set up, but I think with cloud code it will help a ton. Good to know I was on the right track for the general process!

    I'll give it a go with cloud code on the client side later this week and see how it goes. Appreciate all the help!
     
  38. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
    Hey can someone help me our here, how can I get the list of all the inventory items a specific player has and run some code if he has a specific inventory item and also how can I get a boolean value for cloud save
     
  39. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
     
  40. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Shouldn't be too hard (I say that and its always harder than you think lol) I'll post some code here, but it might be slightly off since I am doing this off memory.

    Code (csharp):
    1. const { InventoryApi } = require("@unity-services/economy-2.3");
    2. module.exports = async ({params, context, logger}) => {
    3. const { projectId, playerId } = context;
    4. const inventory = new InventoryApi(context);
    5. const result = await inventory.getPlayerInventory({ projectId, playerId });
    6. return result.data;
    This is the base code for grabbing a player's inventory. It'll create a reference to the player that is actually calling the code. If you want code that targets a different player's inventory, you will probably have to create a parameter (or param) to pass the player's id that you want.

    Code (csharp):
    1. const { InventoryApi } = require("@unity-services/economy-2.3");
    2. module.exports = async ({params, context, logger}) => {
    3. const { projectId } = context;
    4. const playerID = { params };
    5. const inventory = new InventoryApi(context);
    6. const result = await inventory.getPlayerInventory({ projectId, playerId });
    7. return result.data;
    now that you have a reference to the inventory, we need to sort through the inventory.

    Code (csharp):
    1. const playerCheck = await inventory.getPlayerInventory({
    2.     projectId,
    3.     playerId: { playerID },
    4.     inventoryItemIds: "PLAYERDATA" })
    after that, you can run your code for having the item! There are probably other ways to do this, maybe even a little easier, but this is the code that I have been able to find.

    Code (csharp):
    1. if (playerCheck.data.results.length > 0)
    2.   {
    3.     //do stuff
    4.   }
    As for the bool of the Cloud Save, no idea. I haven't messed with Cloud Save yet. Sorry!
     
  41. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
    Oh thanks but I was actually talking about c#, but I'll see if I can use cloud code to do it
     
  42. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
    I've tried the code out is there a way to get only the inventory items id's like in a list or something
     
  43. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I don't know about doing it as a list, I've only ever tried one item at a time haha. I imagine you would put the code that looks for single item into a for loop, and if it finds the item just adds it to an array or list (not sure which would be better for cloud code) which should do it. Then you just need to return that array or list.
     
  44. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Oh! Well in that case, this page should help:

    Player inventory (unity.com)

    I used the InstanceDataSerializable section at the bottom, but the rest is good. The page will show you how to grab all the items (up to 30 at a time) or you can just grab by specific name. This code you can probably put into a for loop like I mentioned before, which should also work.
     
  45. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
    Alright I'll give it a try sorry but one last question how about in the cloud code is it possible to put it in an array or list there because what I'm working on is a weapon selection script that cheacks if the player a specific inventory item that correspond to a list of the weapons and unlocks them if he does have the inventory item if not they stay locked
     
  46. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I would imagine a similar system would be needed, at least concept wise.

    Create a bool.

    A for loop that grabs up to 30 items at a time and checks them for the item you are looking for. If there is the item, change the bool to true (and leave the for loop probably)

    Then outside the for loop send the status of the bool. What that code like exactly I'm not sure (not able to get on my laptop and try some code at the moment)

    If you need to send the whole list, I have heard it is possible to send a whole array of info but I'm not super familiar with it just yet. I need to do that for another part of my code so if I can do that soon I'll try to post what I can.
     
  47. Mid_D_Man

    Mid_D_Man

    Joined:
    Jan 26, 2022
    Posts:
    22
    Ok thanks, I will try it when I start coding aging right now I'm focusing of the drawings fir the game but if you do find anything out please let me know
     
    Last edited: Mar 24, 2023
    Corva-Nocta likes this.