Hey, really need help on that one. I guess Im missing something rather simple, but already spent the last few hours just trying to write and read a List with DynamicConfigFile.WriteObject().
I have two sets, the first one works, the other does not.
Here are the definitions :
the classesCode:DynamicConfigFile playersFile; DynamicConfigFile resourcesFile; public Dictionary<ulong, YattaPlayerData> playersData; public HashSet<YattaResourceData> resourcesData; void Init() { playersData = new Dictionary<ulong, YattaPlayerData>(); playersFile = Interface.GetMod().DataFileSystem.GetDatafile("EquityPlayerData"); playersFile.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; resourcesData = new HashSet<YattaResourceData>(); resourcesFile = Interface.GetMod().DataFileSystem.GetDatafile("EquityResourcesData"); resourcesFile.Settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }
Code:public class YattaPlayerData { public bool bearWarned = false; public bool wandStop = false; public ulong id = 0; public string wandMode = ""; public YattaPlayerData(BasePlayer bp) { if (bp != null) { id = bp.userID; wandMode = "none"; } else { wandMode = "empty"; } } } public class YattaResourcesData { public HashSet<YattaResourceData> data = new HashSet<YattaResourceData>(); } public class YattaResourceData { public bool found; public Vector3 pos; public Quaternion rot; public string prefab; public YattaResourceData(GameObject go) { pos = go.transform.position; rot = go.transform.rotation; prefab = go.name; } }
Reader & writers
Code:void LoadYattaPlayerData() { try { playersData = playersFile.ReadObject<Dictionary<ulong, YattaPlayerData>>(); Puts("CUSTOM PLAYER DATA READY ("+playersData.Count.ToString()+")"); } catch { Puts("ERROR =================== COULD NOT LOAD PLAYER DATA!"); } } void SaveYattaPlayerData() { playersFile.WriteObject(playersData); Puts("PLAYER DATA SAVED (" + playersData.Count.ToString() + ")"); } void LoadYattaResourceDatas() { try { resourcesData = resourcesFile.ReadObject<YattaResourcesData>().data; Puts("CUSTOM RESOURCES DATA READY (" + playersData.Count.ToString() + ")"); } catch (Exception e) { Puts("XXXXXXXX FAIL: {0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace); Puts("ERROR =================== COULD NOT LOAD RESOURCES DATA!"); } } void SaveYattaResourceDatas() { YattaResourcesData yrd = new YattaResourcesData(); yrd.data = resourcesData; resourcesFile.WriteObject(yrd); Puts("RESOURCES DATA SAVED ("+resourcesData.Count.ToString()+")"); }
So everything seems to works fine for playersData, but not for resourcesData. When I open the saved file for resourcesData, everything seems fine too. But when I open it I get an error whatever I do.
For information currently Im using a HashSet for resourcesData, but its just another try to make it work. It didnt work either when I tried to use a Dictionnary like the one I use for playersData, and no more luck with a simple List<YattaResourceData> which was the original intended storage mode.
Errors I get are either "Object reference not set to an instance of an object" on resourcesData = resourcesFile.ReadObject<YattaResourcesData>().data; (when im sure resourcesData, resourcesFile are defined and the file isnt empty) or something about it being a JSON array and having to convert it somehow. Tried whatever I could in that way with no luck.
Im losing hope on that one and im really out of ideas other than write everything as text and write my own parser.
I just want to save a List<YattaResourceData> T_T ...
Edit : I thought it could be becaue I use Vector3 & Quaternion in my 2nd class so I even tried saving all the floats as separate strings, no luck with that either.
Edit : Taking a break from spamming the forum to sleep, see ya in a few hours.
Solved File WriteObject driving me crazy @_@
Discussion in 'Rust Development' started by Yatta, Jul 5, 2016.
-
You're not trying to save a list, it looks like you're trying to save a HashSet. These can't be saved.
You didn't seem to add what your issue was when using a list, but using a HashSet is just going to cause issues too, just in a different way. -
As mentioned, im well aware im trying to save a hashset in this exemple. I tried that because while looking for a fix because I saw another plugin saving a Hashset. Before that I tried with dictionnary because it worked for my other class and before that - originally - it was just a list.
Saving worked in all cases iirc, its the reading that always caused either of the aforementioned errors. For the direct List saving, the error was a NullReferenceException on the line :
and neither resourcesData, resourcesFile, or the ReadObject methos are null at that point.Code:resourcesData = resourcesFile.ReadObject<List<YattaResourceData>>();
Edit : In some tries I also get the followin very explicit error :
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[Oxide.Plugins.Equity+YattaResourceData]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Which sounds really helpful but since im not doing anything with JSON myself (WriteObject/ReadObject methods being in charge) I have no idea what to do with that.Last edited by a moderator: Jul 5, 2016 -
I copied your code and changed it to a list instead of HashSet and saw no NREs, so I'm not really sure whats going on. I didn't however test with actual things saved, but it did successfully load the blank data file, I just didn't have very much time to test it with actual objects.
EDIT: That json error you pasted sounds like you're trying to directly save a HashSet, which, once again, is not natively possible in json, since HashSets are un-ordered, which I presume is why it gave the example of "1, 2, 3".Last edited by a moderator: Jul 5, 2016 -
I just tried and can confirm there is no error when saving an empty List but the ReadObject fails otherwise.
Tested with :
resulting .json :Code:resourcesData = new List<YattaResourceData>(); resourcesData.Add(new YattaResourceData(new GameObject())); resourcesFile.WriteObject(resourcesData);
Error : nullReferenceException / object reference not set to an instance of an object.Code:[ { "found": false, "pos": "0 0 0", "rot": "0 0 0 1", "prefab": "New Game Object" } ]
Despite all the methods and variations I tried so far, all I can say is something wrong happens during .ReadObject ... Im starting to think theres something wrong with my dlls / references but im not sure what to check. But on the other hand the other data I save/load has no problem so I just dont know
-
I'm pretty sure this line will generate a new empty file by itself
@Yatta I have never tried saving a List or Dictionary outright, I usually put it inside a class and haven't had any issues. I have attached a example data storage system I give to others as a example that you might find usefulCode:Interface.GetMod().DataFileSystem.GetDatafile("EquityResourcesData");Attached Files:
-
-
I already tried using an intermediate storage class with no luck. Based on your exemple I tried again, and here's what I have :
As usual, .json file looks fine, but reading fails on ReadObject because NullReferenceException.Code:class StoredData { public List<YattaResourceData> list = new List<YattaResourceData>(); } public class YattaResourceData { public bool found = false; public Vector3 pos; public Quaternion rot; public string prefab; public YattaResourceData(GameObject go) { pos = go.transform.position; rot = go.transform.rotation; prefab = go.name; } } void Init() { resourcesFile = Interface.Oxide.DataFileSystem.GetFile("EquityResourcesData"); } void LoadYattaResourceDatas() { Puts("LOADING CUSTOM RESOURCES DATA ..."); try { StoredData s = new StoredData(); s = resourcesFile.ReadObject<StoredData>(); resourcesData = s.list; Puts("CUSTOM RESOURCES DATA READY (" + playersData.Count.ToString() + ")"); } catch (Exception e) { Puts("XXXXXXXXXXXXX failed: {0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace); Puts("ERROR =================== COULD NOT LOAD RESOURCES DATA!"); dontSave = true; } } void SaveYattaResourceDatas() { StoredData s = new StoredData(); s.list = resourcesData; resourcesFile.WriteObject(s); Puts("RESOURCES DATA SAVED ("+resourcesData.Count.ToString()+")"); }
Could it be because of the data types I save (Vector3, Quaternion) ? I tried once with saving just floats or strings but it didnt work. Maybe I didnt use a storage class back then, or I missed something else ?
Note: I have much more code in the plugin, just showing whats relevant to the matter. Ill try to make a script with just what I just wrote to see see if I get a different result. -
You can't directly save a Vector3 (never tried Quaternion but I assume its the same) You would either need a converter or save each variable. Usually it produces a self referencing loop error though. I use a Vector3 converter I pinched from ZoneManager
. The NRE might be from trying to load the file that was
created from the previous version. Once you change it to store the variables make sure you delete the old data files before loading the plugin -
Ah you might help me about that. I suspected I needed to use that but im not sure how to do that. I stole a Vector3 converter from ZoneManager too and got a Quaternion one too in HumanNPC. I tried to add the same lines as they did in those scripts :
But it doesnt seem to do anything and im not sure if im expected to do more than that ?Code:resourcesFile.Settings.Converters = new JsonConverter[] { new UnityQuaternionConverter(), new UnityVector3Converter() }; -
As k1lly0u already essentially said, try saving the X, Y, and Z coordinates (as floats or a string, unless you have a reason to use something else) themselves, then you can create a vector3 using them. Probably the easiest way. Unless you have a reason to do it otherwise, I think this is the best way.Last edited by a moderator: Jul 5, 2016
-
Ok, just tried that. Everything in my class is now replaced with strings (I need precision for the positions), even the boolean :
All code updated to work with strings.Code:public class YattaResourceData { public string found = "false"; public string px, py, pz; public string qw, qx, qy, qz; public string prefab; public YattaResourceData(GameObject go) { SetPos(go.transform.position); SetRot(go.transform.rotation); prefab = go.name; } public Vector3 Pos() { return new Vector3(float.Parse(px), float.Parse(py), float.Parse(pz)); } public void SetPos(Vector3 v) { px = v.x.ToString(); py = v.y.ToString(); pz = v.z.ToString(); } public Quaternion Rot() { return new Quaternion(float.Parse(qx), float.Parse(qy), float.Parse(qz), float.Parse(qw)); } public void SetRot(Quaternion q) { qw = q.w.ToString(); qx = q.x.ToString(); qy = q.y.ToString(); qz = q.z.ToString(); } }
Resulting saved .json :
Result ... Still the same error. NullReferenceException.Code:{ "list": [ { "found": "false", "px": "117.7684", "py": "11.15466", "pz": "-81.81329", "qw": "0.8805754", "qx": "0", "qy": "0.473906", "qz": "0", "prefab": "assets/bundled/prefabs/autospawn/resource/field-tundra/field-bare-2.prefab" }, { "found": "false", "px": "-158.8376", "py": "39.74121", "pz": "-112.3795", "qw": "0.8317726", "qx": "0", "qy": "0.5551165", "qz": "0", "prefab": "assets/bundled/prefabs/autospawn/resource/beachside-deadtrees/field-bare-2.prefab" }, and so on ...
T_T
EDIT: Here's the full Equity.cs plugin with everything stripped to leave just enough to reproduce the problem :
Code:using System; using System.Collections.Generic;using Oxide.Core; using Oxide.Core.Configuration;using UnityEngine;namespace Oxide.Plugins { [Info("Equity", "Yatta", "0.0.1")] class Equity : RustPlugin { static Equity PluginInstance; DynamicConfigFile playersFile; DynamicConfigFile resourcesFile; void Init() { Puts("================================== INIT"); resourcesData = new List<YattaResourceData>(); resourcesFile = Interface.Oxide.DataFileSystem.GetFile("EquityResourcesData"); } void OnServerInitialized() { LoadYattaResourceDatas(); Puts("================================= SAVING ONE ITEM"); SaveYattaResourceDatas(); } public List<YattaResourceData> resourcesData; public class YattaResourceData { public string found = "false"; public string px, py, pz; public string qw, qx, qy, qz; public string prefab; public YattaResourceData(GameObject go) { SetPos(go.transform.position); SetRot(go.transform.rotation); prefab = go.name; } public Vector3 Pos() { return new Vector3(float.Parse(px), float.Parse(py), float.Parse(pz)); } public void SetPos(Vector3 v) { px = v.x.ToString(); py = v.y.ToString(); pz = v.z.ToString(); } public Quaternion Rot() { return new Quaternion(float.Parse(qx), float.Parse(qy), float.Parse(qz), float.Parse(qw)); } public void SetRot(Quaternion q) { qw = q.w.ToString(); qx = q.x.ToString(); qy = q.y.ToString(); qz = q.z.ToString(); } } class StoredData { public List<YattaResourceData> list = new List<YattaResourceData>(); } void SaveYattaResourceDatas() { Puts("================================== SAVE"); resourcesData = new List<YattaResourceData>(); resourcesData.Add(new YattaResourceData(new GameObject())); StoredData s = new StoredData(); s.list = resourcesData; Puts("Trying to save " + s.list.Count.ToString() + " items"); resourcesFile.WriteObject(s); Puts("RESOURCES DATA SAVED (" + resourcesData.Count.ToString() + ")"); } void LoadYattaResourceDatas() { Puts("================================== LOAD"); try { StoredData s = new StoredData(); Puts("Trying to read data into " + s.list.ToString()); s = resourcesFile.ReadObject<StoredData>(); resourcesData = s.list; Puts("CUSTOM RESOURCES DATA READY (" + resourcesData.Count.ToString() + ")"); } catch (Exception e) { Puts("FAIL: {0}{1}{2}", e.Message, Environment.NewLine, e.StackTrace); } } }}Last edited by a moderator: Jul 5, 2016 -
How are you loading the data exactly? Get the file first then load the data? If you post or pm me your plugin I'll be able to tell you what's wrong. Also if you use floats you won't have to parse it back when you want to use the data
Last edited by a moderator: Jul 5, 2016 -
FINALLY !
Found the culprit and solution !
So you know how you're not supposed to store GameObjects in the classes you save ? I was aware of it and not doing that. But you should not even MENTION GameObjects in that class. The problem was with my following constructor code :
Replacing it with the following code fixed the problem :Code:public YattaResourceData(GameObject go) { SetPos(go.transform.position); SetRot(go.transform.rotation); prefab = go.name; }
Well, TIL. Thank guys for supporting me with that oneCode:public YattaResourceData(Vector3 pos, Quaternion rot, string name) { SetPos(pos); SetRot(rot); prefab = name; }
Last edited by a moderator: Jul 6, 2016
