Author Topic: Saving Inventory Server Side / Loading  (Read 6067 times)

WolfTechGames

  • Newbie
  • *
  • Thank You
  • -Given: 6
  • -Receive: 4
  • Posts: 35
    • View Profile
Saving Inventory Server Side / Loading
« on: November 15, 2015, 12:24:41 AM »
Hello,

I am trying to figure out how I could save and load everyone's inventory server side so that they only have that inventory on one specific server. I am working on a survival game and I don't want the players inventories to presist among servers. Appreciate everyone's help!

Thanks,
 WolfTechGames

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #1 on: November 18, 2015, 04:15:31 PM »
Simple. When the player connects, do a TNManager.LoadFile("Players/" + playersNickNameOrID, callback);

If the callback receives no data, then it's a new player. Otherwise, load the player data. In Windward once the player's data is loaded this way I then parse it (it's a saved DataNode), and set TNManager.playerData = parsedDataNode; -- which then automatically sends this to every other player.

Whenever you change the player's data (TNManager.playerDataNode), be sure to sync it via TNManager.SyncPlayerData(). Save it periodically and/or when about to disconnect by calling TNManager.SaveFile.

WolfTechGames

  • Newbie
  • *
  • Thank You
  • -Given: 6
  • -Receive: 4
  • Posts: 35
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #2 on: November 18, 2015, 11:40:50 PM »
Simple. When the player connects, do a TNManager.LoadFile("Players/" + playersNickNameOrID, callback);

If the callback receives no data, then it's a new player. Otherwise, load the player data. In Windward once the player's data is loaded this way I then parse it (it's a saved DataNode), and set TNManager.playerData = parsedDataNode; -- which then automatically sends this to every other player.

Whenever you change the player's data (TNManager.playerDataNode), be sure to sync it via TNManager.SyncPlayerData(). Save it periodically and/or when about to disconnect by calling TNManager.SaveFile.

Hey Aren thanks for the reply, I got the function itself to work but it seems to not load the correct data was wondering if you know why. Here is my code:
  1. //Load Data
  2.     public void loadInventory()
  3.     {
  4.         //Initiate Inventory
  5.         for (int i = 0; i < inventorySize; i++) { inventoryItems.Add(""); }
  6.  
  7.         //Add Player Inventory
  8.         TNManager.playerDataNode.SetChild("Inventory", inventoryItems);
  9.  
  10.         TNManager.LoadFile("Players/" + TNManager.playerName, onLoadFile);
  11.     }
  12.  
  13.     void onLoadFile(string filename, byte[] data)
  14.     {
  15.         if (data == null)
  16.         {
  17.             saveInventory();
  18.         }
  19.         else
  20.         {
  21.             MemoryStream stream = new MemoryStream(data);
  22.             StreamReader reader = new StreamReader(stream);
  23.             Debug.Log(reader.ReadToEnd());
  24.         }
  25.     }
  26.  
  27.     //Save Data
  28.     public void saveInventory()
  29.     {
  30.         MemoryStream stream = new MemoryStream();
  31.         StreamWriter writer = new StreamWriter(stream);
  32.         writer.Write(inventoryItems);
  33.         byte[] data = stream.ToArray();
  34.         writer.Close();
  35.         TNManager.SaveFile("Players/" + TNManager.playerName, data);
  36.  
  37.         //Debug
  38.         Debug.Log("Inventory Saved");
  39.     }

NOTE: inventoryItems is a string array

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #3 on: November 25, 2015, 06:19:40 PM »
What are you doing there? First... DataNode is fully capable of serialization, there is no need to use anything else. Second, why add "" strings? That won't work if you try to save them as a text-based DataNode. Binary will work fine.

DataNode.Write does the writing. DataNode.Read does the reading.

WolfTechGames

  • Newbie
  • *
  • Thank You
  • -Given: 6
  • -Receive: 4
  • Posts: 35
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #4 on: November 25, 2015, 10:56:49 PM »
I am trying to save the player inventory server side, i'm confused haha sorry. If you have some time could you or someone else show me a quick example :/ Sorry if I sound demanding. Thank you very much! :D

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #5 on: November 26, 2015, 07:31:29 AM »
So I think I nailed everything you want and followed Aren's advice. I'm not incredibly familiar with the DataNode class and I didn't try compiling / testing this, so hopefully it all works okay :)

  1. public class InventoryManager : TNBehaviour
  2. {
  3.         public int SaveInterval = 60;
  4.        
  5.         void Start()
  6.         {
  7.                 // save it periodically
  8.                 StartCoroutine(SaveInventoryToFile());
  9.         }
  10.        
  11.         void OnNetworkConnect (bool result, string message)
  12.         {
  13.                 if (result)
  14.                 {
  15.                         // when the player connects, load file
  16.                         TNManager.LoadFile("Players/" + TNManager.playerName, OnLoadFile);
  17.                 }
  18.                 else
  19.                 {
  20.                         Debug.LogError(message);
  21.                 }
  22.         }
  23.        
  24.         void OnLoadFile(string filename, byte[] data)
  25.         {
  26.                 if (data.Length == 0)
  27.                 {
  28.                         // if the callback receives no data then it's a new player.
  29.                         // initialize the inventory
  30.                         TNManager.playerDataNode.Add("Item 1");
  31.                         TNManager.playerDataNode.Add("Item 2");
  32.                         TNManager.playerDataNode.Add("Item 3");
  33.                        
  34.                         TNManager.SyncPlayerData();
  35.                 }
  36.                 else
  37.                 {
  38.                         // otherwise, load the data
  39.                         DataNode parsedDataNode = DataNode.Read(data);
  40.                         // setting TNManager.playerData automatically syncs
  41.                         TNManager.playerData = parsedDataNode;
  42.                 }
  43.         }
  44.        
  45.         IEnumerator SaveInventoryToFile()
  46.         {
  47.                 // save it periodically
  48.                 while(true)
  49.                 {
  50.                         try
  51.                         {
  52.                                 // if implicit casting works
  53.                                 //TNManager.SaveFile("Players/" + TNManager.playerName, TNManager.playerData);
  54.                                 // else put it in appropriate type
  55.                                 byte[] buf;
  56.                                 using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
  57.                                 {
  58.                                         using (System.IO.StreamWriter bw = new System.IO.StreamWriter(ms))
  59.                                         {
  60.                                                 TNManager.playerDataNode.Write(bw);
  61.                                                 buf = ms.ToArray();
  62.                                         }
  63.                                 }
  64.                                 // and save
  65.                                 TNManager.SaveFile("Players/" + TNManager.playerName, buf);
  66.                         }
  67.                         catch (Exception ex) { Debug.LogError(ex.Message); }
  68.                         yield return new WaitForSeconds(SaveInterval);
  69.                 }
  70.         }
  71. }
  72.  

It saves every minute. You'll have to add saving before disconnect.

The example gets rid of your inventoryItems. The inventory is now TNManager.playerDataNode. If you want to save other stuff in TNManager.playerDataNode you'll need to modify this a bit [make everything above branch off an "Inventory" child].

There's a lot you can do to build upon this example. Hopefully it gives you a start in the right direction.

WolfTechGames

  • Newbie
  • *
  • Thank You
  • -Given: 6
  • -Receive: 4
  • Posts: 35
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #6 on: November 26, 2015, 01:37:00 PM »
So I think I nailed everything you want and followed Aren's advice. I'm not incredibly familiar with the DataNode class and I didn't try compiling / testing this, so hopefully it all works okay :)

The example gets rid of your inventoryItems. The inventory is now TNManager.playerDataNode. If you want to save other stuff in TNManager.playerDataNode you'll need to modify this a bit [make everything above branch off an "Inventory" child].

There's a lot you can do to build upon this example. Hopefully it gives you a start in the right direction.

Thank you very much! This is what I was looking for, I have tested the code and it works completely fine so this is perfect. Now all I have to do is tweak it a bit to make sure it works with my system. Thanks Again! :D

WolfTechGames

  • Newbie
  • *
  • Thank You
  • -Given: 6
  • -Receive: 4
  • Posts: 35
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #7 on: November 26, 2015, 11:11:32 PM »
So I think I nailed everything you want and followed Aren's advice. I'm not incredibly familiar with the DataNode class and I didn't try compiling / testing this, so hopefully it all works okay :)

  1. public class InventoryManager : TNBehaviour
  2. {
  3.         public int SaveInterval = 60;
  4.        
  5.         void Start()
  6.         {
  7.                 // save it periodically
  8.                 StartCoroutine(SaveInventoryToFile());
  9.         }
  10.        
  11.         void OnNetworkConnect (bool result, string message)
  12.         {
  13.                 if (result)
  14.                 {
  15.                         // when the player connects, load file
  16.                         TNManager.LoadFile("Players/" + TNManager.playerName, OnLoadFile);
  17.                 }
  18.                 else
  19.                 {
  20.                         Debug.LogError(message);
  21.                 }
  22.         }
  23.        
  24.         void OnLoadFile(string filename, byte[] data)
  25.         {
  26.                 if (data.Length == 0)
  27.                 {
  28.                         // if the callback receives no data then it's a new player.
  29.                         // initialize the inventory
  30.                         TNManager.playerDataNode.Add("Item 1");
  31.                         TNManager.playerDataNode.Add("Item 2");
  32.                         TNManager.playerDataNode.Add("Item 3");
  33.                        
  34.                         TNManager.SyncPlayerData();
  35.                 }
  36.                 else
  37.                 {
  38.                         // otherwise, load the data
  39.                         DataNode parsedDataNode = DataNode.Read(data);
  40.                         // setting TNManager.playerData automatically syncs
  41.                         TNManager.playerData = parsedDataNode;
  42.                 }
  43.         }
  44.        
  45.         IEnumerator SaveInventoryToFile()
  46.         {
  47.                 // save it periodically
  48.                 while(true)
  49.                 {
  50.                         try
  51.                         {
  52.                                 // if implicit casting works
  53.                                 //TNManager.SaveFile("Players/" + TNManager.playerName, TNManager.playerData);
  54.                                 // else put it in appropriate type
  55.                                 byte[] buf;
  56.                                 using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
  57.                                 {
  58.                                         using (System.IO.StreamWriter bw = new System.IO.StreamWriter(ms))
  59.                                         {
  60.                                                 TNManager.playerDataNode.Write(bw);
  61.                                                 buf = ms.ToArray();
  62.                                         }
  63.                                 }
  64.                                 // and save
  65.                                 TNManager.SaveFile("Players/" + TNManager.playerName, buf);
  66.                         }
  67.                         catch (Exception ex) { Debug.LogError(ex.Message); }
  68.                         yield return new WaitForSeconds(SaveInterval);
  69.                 }
  70.         }
  71. }
  72.  

It saves every minute. You'll have to add saving before disconnect.

The example gets rid of your inventoryItems. The inventory is now TNManager.playerDataNode. If you want to save other stuff in TNManager.playerDataNode you'll need to modify this a bit [make everything above branch off an "Inventory" child].

There's a lot you can do to build upon this example. Hopefully it gives you a start in the right direction.

The file output doesn't overwrite the other file except it writes on top of it. So the file output looks something like this:
  1. Version = 12
  2.         InventorySize = 20
  3.         Inventory = IList<System.String>
  4.                 AK-47
  5.                 Glock
  6.                
  7.                 Glock
  8.                
  9.                
  10.                
  11.                
  12.                
  13.                
  14.                
  15.                
  16.                
  17.                
  18.                
  19.                
  20.                
  21.                
  22.                
  23.                
  24.         EquippedWeapon = ""
  25.         InventorySize = 20
  26.         Inventory = IList<System.String>
  27.                 AK-47
  28.                 Glock
  29.                
  30.                 Glock
  31.                
  32.                
  33.                
  34.                
  35.                
  36.                
  37.                
  38.                
  39.                
  40.                
  41.                
  42.                
  43.                
  44.                
  45.                
  46.                
  47.         EquippedWeapon = ""
  48.         InventorySize = 20
  49.         Inventory = IList<System.String>
  50.                 AK-47
  51.                 Glock
  52.                
  53.                 Glock
  54.                
  55.                
  56.                
  57.                
  58.                
  59.                
  60.                
  61.                
  62.                
  63.                
  64.                
  65.                
  66.                
  67.                
  68.                
  69.                
  70.         EquippedWeapon = ""
  71.         InventorySize = 20
  72.         Inventory = IList<System.String>
  73.                 AK-47
  74.                 Glock
  75.                
  76.                 Glock
  77.                
  78.                
  79.                
  80.                
  81.                
  82.                
  83.                
  84.                
  85.                
  86.                
  87.                
  88.                
  89.                
  90.                
  91.                
  92.                
  93.         EquippedWeapon = ""
  94.         InventorySize = 20
  95.         Inventory = IList<System.String>
  96.                 AK-47
  97.                 Glock
  98.                
  99.                 Glock
  100.                
  101.                
  102.                
  103.                
  104.                
  105.                
  106.                
  107.                
  108.                
  109.                
  110.                
  111.                
  112.                
  113.                
  114.                
  115.                
  116.         EquippedWeapon = ""
  117.         InventorySize = 20
  118.         Inventory = IList<System.String>
  119.                 AK-47
  120.                 Glock
  121.                
  122.                 Glock
  123.                
  124.                
  125.                
  126.                
  127.                
  128.                
  129.                
  130.                
  131.                
  132.                
  133.                
  134.                
  135.                
  136.                
  137.                
  138.                
  139.         EquippedWeapon = "Glock"
  140.         InventorySize = 20
  141.         Inventory = IList<System.String>
  142.                 AK-47
  143.                 Glock
  144.                
  145.                 Glock
  146.                
  147.                
  148.                
  149.                
  150.                
  151.                
  152.                
  153.                
  154.                
  155.                
  156.                
  157.                
  158.                
  159.                
  160.                
  161.                
  162.         EquippedWeapon = "Glock"
  163.         InventorySize = 20
  164.         Inventory = IList<System.String>
  165.                 AK-47
  166.                 Glock
  167.                
  168.                 Glock
  169.                
  170.                
  171.                
  172.                
  173.                
  174.                
  175.                
  176.                
  177.                
  178.                
  179.                
  180.                
  181.                
  182.                
  183.                
  184.                
  185.         EquippedWeapon = ""
  186.         InventorySize = 20
  187.         Inventory = IList<System.String>
  188.                 AK-47
  189.                 Glock
  190.                
  191.                 Glock
  192.                
  193.                
  194.                
  195.                
  196.                
  197.                
  198.                
  199.                
  200.                
  201.                
  202.                
  203.                
  204.                
  205.                
  206.                
  207.                
  208.         EquippedWeapon = ""
  209.         InventorySize = 20
  210.         Inventory = IList<System.String>
  211.                 AK-47
  212.                 Glock
  213.                
  214.                 Glock
  215.                
  216.                
  217.                
  218.                
  219.                
  220.                
  221.                
  222.                
  223.                
  224.                
  225.                
  226.                
  227.                
  228.                
  229.                
  230.                
  231.         EquippedWeapon = ""
  232.         InventorySize = 20
  233.         Inventory = IList<System.String>
  234.                 AK-47
  235.                 Glock
  236.                
  237.                 Glock
  238.                
  239.                
  240.                
  241.                
  242.                
  243.                
  244.                
  245.                
  246.                
  247.                
  248.                
  249.                
  250.                
  251.                
  252.                
  253.                
  254.         EquippedWeapon = "AK-47"
  255.  

cmifwdll

  • Global Moderator
  • Sr. Member
  • *****
  • Thank You
  • -Given: 0
  • -Receive: 149
  • Posts: 285
  • TNet Alchemist
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #8 on: November 27, 2015, 03:02:29 AM »
The function that writes to the file uses File.WriteAllBytes which overwrites the file (desired in this case).

So it must be something on your end. DataNode.AddChild blindly adds a new child, without checking to see if one with that name already exists. I'd suggest trying AddMissingChild or even SetChild. SetChild will set the value of a specified child, or create a new one if it doesn't exist. Also make sure you're not adding null or empty string values (that's why you're getting blank lines in your output).

If you want to access an existing child, use something like node.GetChild<Type>("NameOfChild"). So if I have an item saved as a DataNode I might access some of its properties like so:
  1. public enum ItemType { Pistol, Shotgun, Sniper };
  2. public class Item
  3. {
  4.         public ItemType type;
  5.         public string name;
  6.         public float damage;
  7. }
  8.  
  9. // set values
  10. node.SetChild("Type", type);
  11. node.SetChild("Name", name);
  12. node.SetChild("Damage", damage);
  13.  
  14. // access values
  15. type = node.GetChild<ItemType>("Type");
  16. name = node.GetChild<string>("Name");
  17. damage = node.GetChild<float>("Damage");
  18.  

Inventory systems can become extremely complex extremely quickly, so put some serious time into planning out exactly how you want it structured and exactly how you're going to use it. It might help to think of DataNode as an XML tree-like datatype.

WolfTechGames

  • Newbie
  • *
  • Thank You
  • -Given: 6
  • -Receive: 4
  • Posts: 35
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #9 on: November 30, 2015, 12:23:16 PM »
The function that writes to the file uses File.WriteAllBytes which overwrites the file (desired in this case).

So it must be something on your end. DataNode.AddChild blindly adds a new child, without checking to see if one with that name already exists. I'd suggest trying AddMissingChild or even SetChild. SetChild will set the value of a specified child, or create a new one if it doesn't exist. Also make sure you're not adding null or empty string values (that's why you're getting blank lines in your output).

If you want to access an existing child, use something like node.GetChild<Type>("NameOfChild"). So if I have an item saved as a DataNode I might access some of its properties like so:
  1. public enum ItemType { Pistol, Shotgun, Sniper };
  2. public class Item
  3. {
  4.         public ItemType type;
  5.         public string name;
  6.         public float damage;
  7. }
  8.  
  9. // set values
  10. node.SetChild("Type", type);
  11. node.SetChild("Name", name);
  12. node.SetChild("Damage", damage);
  13.  
  14. // access values
  15. type = node.GetChild<ItemType>("Type");
  16. name = node.GetChild<string>("Name");
  17. damage = node.GetChild<float>("Damage");
  18.  

Inventory systems can become extremely complex extremely quickly, so put some serious time into planning out exactly how you want it structured and exactly how you're going to use it. It might help to think of DataNode as an XML tree-like datatype.

Thanks mate, everything is working now. Since my system relies on blanks I made it so before it saves it saves all blanks as the word "Empty" and when it load it loads all "Empty" as a blank.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Saving Inventory Server Side / Loading
« Reply #10 on: December 01, 2015, 08:12:12 AM »
It's best to not save any blanks. RemoveChild the nodes you don't need instead.