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.
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.
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 :)
public class InventoryManager : TNBehaviour { public int SaveInterval = 60; void Start() { // save it periodically StartCoroutine(SaveInventoryToFile()); } void OnNetworkConnect (bool result, string message) { if (result) { // when the player connects, load file TNManager.LoadFile("Players/" + TNManager.playerName, OnLoadFile); } else { Debug.LogError(message); } } void OnLoadFile(string filename, byte[] data) { if (data.Length == 0) { // if the callback receives no data then it's a new player. // initialize the inventory TNManager.playerDataNode.Add("Item 1"); TNManager.playerDataNode.Add("Item 2"); TNManager.playerDataNode.Add("Item 3"); TNManager.SyncPlayerData(); } else { // otherwise, load the data DataNode parsedDataNode = DataNode.Read(data); // setting TNManager.playerData automatically syncs TNManager.playerData = parsedDataNode; } } IEnumerator SaveInventoryToFile() { // save it periodically while(true) { try { // if implicit casting works //TNManager.SaveFile("Players/" + TNManager.playerName, TNManager.playerData); // else put it in appropriate type byte[] buf; { { TNManager.playerDataNode.Write(bw); buf = ms.ToArray(); } } // and save TNManager.SaveFile("Players/" + TNManager.playerName, buf); } catch (Exception ex) { Debug.LogError(ex.Message); } } } }
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 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:
public enum ItemType { Pistol, Shotgun, Sniper }; public class Item { public ItemType type; public string name; public float damage; } // set values node.SetChild("Type", type); node.SetChild("Name", name); node.SetChild("Damage", damage); // access values type = node.GetChild<ItemType>("Type"); name = node.GetChild<string>("Name"); damage = node.GetChild<float>("Damage");
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.