Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Networking Inventory

Discussion in 'Multiplayer' started by Corva-Nocta, Feb 26, 2018.

  1. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I made myself a roughly functional inventory system, but the tutorials I am following are designed for single player. I have gotten most of the code working fine for multiplayer, but I'm having some trouble adding items to the inventory. I think it is a simialr issue to things like having a player directly affect a gameobject in that I need to find the network id before applying effects. Does anyone know how to grab the specific inventory for an individual player?

    Here is some code for the section that does not work. This is all on one Canvas ganeObject called Inventory:
    Code (csharp):
    1. ItemDatabase database;
    2. public List <GameObject> Slots = new List <GameObject >();
    3. public List <Item> Items = new List <Item>();
    4. public GameObject slots;
    5.  
    6. void Start ()
    7. {
    8.    database = GameObject.FindGameObjectWithTag ("ItemDatabase").GetComponent <ItemDatabase>();
    9.    addItem (0); //this is the code I want to have work
    10.    addItem (1);
    11. }
    12.  
    13. public void addItem (int id)
    14. {
    15.    for (int i = 0; i < database.items.Count; i++)
    16.    {
    17.       if (database.items [i].itemID == id)
    18.       {
    19.          Item item = database.items [i];
    20.  
    21.          addItemAtEmptySlot (item);
    22.       }
    23.    }
    24. }
    25.  
    26. void addItemAtEmptySlot (Item item)
    27. {
    28.    for (int i = 0; i < Items.Count; i++)
    29.    {
    30.       if (Items [i].itemName == null)
    31.       {
    32.          Items [i] = item;
    33.          break;
    34.       }
    35.    }
    36. }
    That's all the code that has to deal with adding items to tge inventory. Right now when I start the game and host/client I can see my inventory, even toggle it off and one individually for players, but I can't figure out how to go from there. I have no errors so its very difficult to figure out the next step.

    Any help at all would be great! Thanks!
     
  2. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Update:

    I checked the inventory object while the game is running, it appears that the items are getting added, but the sprite for it is not updating. Even if I set the empty area to the sprite chosen in the editor it doesn't show up.

    Let me see what I can figure out and get back to it. This seems like a weird problem

    I do still want to know how to target a specific player to add items to the inventory. Once I figure this tiny bug out that will be my next question
     
    Last edited: Feb 26, 2018
  3. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Update:

    Alright I got the images to show and update (horray) but now I have a different problem. Now it runs the first addItem code runs continually until the inventory slots are all full. I would think the code should only run once, not once for each slot. Help?!
     
  4. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Alright, I finally figured it out. Turns out there was a line of code I had left out. Now I have a separate issue, but since I need to figure out more than just this, I'll make a new thread for it
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I don't see in your code that you are involving networking at all.
     
  6. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    You are correct, I actually have little to no networking on this code but other aspects of the project make it work to some degree. I have figured out the other parts of the problem and now have circled back to this part of the problem. I need to establish the inventories as separate objects for each player. Right now the code works just fine for the host, but does not work when trying to ass items to clients. I will now post the full code:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5. using UnityEngine.UI;
    6. using UnityEngine.Networking;
    7.  
    8. public class Inventory : NetworkBehaviour
    9. {
    10.     public List<GameObject> Slots = new List<GameObject>();
    11.     public List<Item> Items = new List<Item>();
    12.     public GameObject slots;
    13.     ItemDatabase database;
    14.     int x = -110;
    15.     int y = 110;
    16.  
    17.     public GameObject draggedItemGameObject;
    18.     public bool draggingItem = false;
    19.     public Item draggedItem;
    20.     public int indexOfDraggedItem;
    21.  
    22.     void Start()
    23.     {
    24.         int slotamount = 0;
    25.  
    26.         database = GameObject.FindGameObjectWithTag("ItemDatabase").GetComponent<ItemDatabase>();
    27.         for (int i = 1; i < 6; i++)
    28.         {
    29.             for (int k = 1; k < 6; k++)
    30.             {
    31.                 GameObject slot = (GameObject)Instantiate(slots);
    32.                 slot.GetComponent<SlotScript>().slotNumber = slotamount;
    33.                 Slots.Add(slot);
    34.                 Items.Add(new Item());
    35.                 slot.transform.SetParent(gameObject.transform); //used to have slot.transform.parent = gameObject.transform;
    36.                 slot.name = "slot" + i + "." + k;
    37.                 slot.GetComponent<RectTransform>().localPosition = new Vector3(x, y, 0);
    38.                 x = x + 55;
    39.                 if (k == 5)
    40.                 {
    41.                     x = -110;
    42.                     y = y - 55;
    43.                 }
    44.                 slotamount++;
    45.             }
    46.         }
    47.     }
    48.  
    49.     void Update()
    50.     {
    51.         if (draggingItem)
    52.         {
    53.             Vector3 posi = (Input.mousePosition - GameObject.FindGameObjectWithTag("Canvas").GetComponent<RectTransform>().localPosition);
    54.             draggedItemGameObject.GetComponent<RectTransform>().localPosition = new Vector3(posi.x + 30, posi.y - 30, posi.z);  //change vector3 to "posi" if item stays in the center of the mouse
    55.         }
    56.     }
    57.  
    58.     public void showDraggedItem (Item item, int slotnumber)
    59.     {
    60.         indexOfDraggedItem = slotnumber;
    61.         draggedItemGameObject.SetActive(true);
    62.         draggedItem = item;
    63.         draggingItem = true;
    64.         draggedItemGameObject.GetComponent<Image>().sprite = item.itemIcon;
    65.     }
    66.  
    67.     public void closeDraggedItem ()
    68.     {
    69.         draggingItem = false;
    70.         draggedItemGameObject.SetActive(false);
    71.     }
    72.  
    73.     public void addExistingItem(Item item)
    74.     {
    75.         if (item.itemType == Item.ItemType.Stackable)
    76.         {
    77.             checkIfItemExists(item.itemID, item);
    78.         }
    79.         else
    80.         {
    81.             addItemAtEmptySlot(item);
    82.         }
    83.     }
    84.  
    85.     public void checkIfItemExists(int itemID, Item item)
    86.     {
    87.         for (int i = 0; i < Items.Count; i++)
    88.         {
    89.             if(Items[i].itemID == itemID)
    90.             {
    91.                 Items[i].itemValue = Items[i].itemValue + item.itemValue;
    92.                 break;
    93.             }
    94.             else if(i == Items.Count - 1)
    95.             {
    96.                 addItemAtEmptySlot(item);
    97.             }
    98.         }
    99.     }
    100.  
    101.     public void addItem (int id)
    102.     {
    103.         database = GameObject.FindGameObjectWithTag("ItemDatabase").GetComponent<ItemDatabase>();
    104.         for (int i = 0; i < database.items.Count; i++)
    105.         {
    106.             if (database.items[i].itemID == id)
    107.             {
    108.                 Item item = database.items[i];
    109.  
    110.                 if (database.items[i].itemStackable == true) //database.items[i].itemType == Item.ItemType.Stackable
    111.                 {
    112.                     checkIfItemExists(id, item);
    113.                     break;
    114.                 }
    115.                 else
    116.                 {
    117.                     addItemAtEmptySlot(item);
    118.                 }
    119.             }
    120.         }
    121.     }
    122.  
    123.     void addItemAtEmptySlot (Item item)
    124.     {
    125.         for (int i = 0; i < Items.Count; i++)
    126.         {
    127.             if (Items[i].itemName == null)
    128.             {
    129.                 Items[i] = item;
    130.                 break;
    131.             }
    132.         }
    133.     }
    134. }
    this all on a Panel object which is a child of a Canvas, which is a child of my Player prefab. The Slots in the script are prefabs as well. The Inventory Panel does have a Network Identity component attached to it. I also do not know what I need to add to make it function properly in multiplayer. Again, everything works perfectly for the host, but not at all for the clients.

    this is what the code looks like on something like a Tree which will give a single resource when harvested:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4.  
    5. public class Tree : ResourceNode
    6. {
    7.     public GameObject playerUsed;
    8.     public int numberHarvested;
    9.  
    10.     public void Gather(int woodCutDmg)
    11.     {
    12.         if (!isServer)
    13.         {
    14.             return;
    15.         }
    16.  
    17.         currentNodeHealth -= woodCutDmg;
    18.  
    19.         if (currentNodeHealth <= 0)
    20.         {
    21.             RpcOnGather(playerUsed.GetComponent<NetworkIdentity>().netId);
    22.         }
    23.     }
    24.  
    25.     [ClientRpc]
    26.     void RpcOnGather(NetworkInstanceId netId)
    27.     {
    28.         GameObject player = NetworkServer.FindLocalObject(netId);
    29.         if (player)
    30.         {
    31.             Inventory inventory = player.GetComponentInChildren<Inventory>();
    32.             if (inventory != null)
    33.             {
    34.                 inventory.addItem(0);
    35.             }
    36.         }
    37.         Destroy(gameObject);
    38.     }
    39. }
    If anyone can help at all that would be great! I'm trying to add !isLocalPlayer checks in random places, but I really don't know how to make it work properly.
     
    Last edited: Feb 27, 2018
  7. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    If you're going with the UNet HLAPI, I'd start with going through the manual and the various tutorials available and then circle back to this. If your inventory is an inventory of networked GameObjects, generally you'd want the host in charge of it instead of the client who's player owns the inventory.
     
  8. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    There are exactly 0 tutorials that work with this sadly. I've been looking for 2 days haha. I do think I just got a lead though, my problem might be that I'm not using ClientRPC calls to send the data to the player. I'm going to play around with that and see what happens. If you have any suggestions for using them please feel free to comment!
     
  9. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I don't mean inventory tutorials, just UNET tutorials in general. There's lots of ways you can handle an inventory system. If the inventory is just data, you can handle it all with Commands and RPC's, or a SyncListStruct and Commands from the client to update that synclist. You could also serialize everything however you want and just send it in a UNET Message.
     
  10. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    @Joe-Censored is making a lot of sense. Using the API to keep an inventory in sync, could just be updating "what" + slot # + (maybe) item count..

    No networking code really has to handle how it's displayed or anything like that.
     
  11. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I think Commands and RPCs are going to work best for me. I got Commands to work to send data to the object so I would like to stick to what I know.

    I am unfamiliar with serializing (seeing as I know very little about coding already) but that does sound like an easy option. I may have to look into that if I want to optimize code later (which I will)
     
  12. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I thought it should be very little code involving actual networking since the examples for health bars basically don't require any network coding. It takes some, but very little. I would think sinve all of this code is on an object that is the child of a spawned player, it shouldn't take too much to update each one locally as needed
     
  13. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    On serializing, consider a little cheat I'm doing in one of my inventory systems. When I went to write my network serializer I started writing one to serialize everything to a byte array to send in a Unet Message, but then I was thinking that I have this CSV serializer already set up to record the inventory to disk and it isn't really all that bad as far as the number of bytes it uses. So I just updated my CSV handler to optionally just return a single string of the whole CSV, I run that through a simple encryption, and send the string as a message. So this saves me from updating two different serializers/deserializers as I make changes to my inventory system as the game develops.

    I might go ahead with the byte array network serializer once the game settles down just for max space efficiency over the network, but this is working just fine right now.
     
  14. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Very interesting. I will definitely have to look into that a little later. It sounds like a very simple way to get it working, very optimized.
     
  15. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Alright so I tried changing my OnGather function to a ClientRpc (I updated the code above to show that) but it still is not running the code on the client players. I feel like I'm missing some tiny piece of code but I have no idea what it is. I'll keep poking around
     
  16. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    I tried adding a ton of Debug.Logs to see where my code is failing. I have it narrowed down, but I'm not really sure what to do next.

    Code (csharp):
    1. [ClientRpc]
    2. void RpcOnGather (NetworkInstanceId netId)
    3. {
    4.    GameObject player = NetworkServer.FindLocalObject (netId);
    5.    Debug.Log (player);
    6.    Debug.Log (netId);
    7.    if (player)
    8.    {
    9.       Inventory inventory = player.GetComponentInChildren <Inventory>();
    10.       Debug.Log ("Got" + inventory);
    11.       if (inventory)
    12.       {
    13.          Debug.Log ("addItem");
    14.          inventory.addItem (0);
    15.       }
    16.    }
    17.    Destroy (gameObject);
    18. }
    The Debug.Log (player) returns null for client, but the Debug.Log (netId) returns the proper netId. Debug.Log ("Got" + inventory) returns null as well but it returns it on the host, not the clients.

    So now that I know what the problems are.... I don't know how to fix it. Hm....

    Any ideas?
     
    Last edited: Feb 27, 2018
  17. Corva-Nocta

    Corva-Nocta

    Joined:
    Feb 7, 2013
    Posts:
    801
    Ok I know where the core of my problem is stemming from. When my Player interacts with an object I use a Command to send the player GameObject to that object so I can reference it latet. But, as well all know, GameObjects can not be sent through Commands. Something I completely overlooked.

    So if I can just find how to send the GameObject or something similar so I can get a reference to my Player then I'm good to go! I'll research the answer but if you know how to do this please don't hesitate to chime in