I am trying to create an object structure like this Universe - contains a collection of Worlds Worlds contains a collection of areas.
All 3 of these types extend from a base class which has a property of 'ParentObject'. Worlds will reference the Universe via this property. Areas will reference the world via this property.
When running a test harness application.
Universe universe = Universe.GetInstance();
var world = universe.CreateWorld();
var area = world.CreateArea();
area.UpdateIpfs();
UpdateIpfs on the area object - then recurses upwards from area to world to universe - serializing each tier and then adding to the Ipfs network. (Thats just where I am storing the json data)
In order to get the objects back I do
Universe universe = Universe.GetInstance("QmZnaSaDNnmqhUrE8kFHeu9PGGStAr2D4q3Vt88yHwFvzG");
var world = universe.Worlds[0];
var area = world.Areas[0];
Stepping through the code I can see the json content is this before deserialization:
{
"$id": "1",
"ParentObject": null,
"Worlds": [
{
"$id": "2",
"ParentObject": {
"$ref": "1"
},
"Time": "2019-10-06T23:13:56.6002056+01:00",
"Name": null,
"Description": null,
"Areas": [
{
"$id": "3",
"EventScripts": {
"$id": "4"
},
"ParentObject": {
"$ref": "2"
},
"Name": null,
"IsInterior": false,
"IsUnderground": false,
"IsArtificial": false,
"Description": null,
"X": 0,
"Y": 0,
"Z": 0,
"AreaObjects": [],
"ObjType": "BloodlinesLib.Engine.World.Area, BloodlinesLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Hash": "QmbM86GZn6w9JadBM143fDYwGisgNPGXke3bFxpXzrfgJh"
}
],
"ObjType": "BloodlinesLib.Engine.World.World, BloodlinesLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Hash": "QmRnEfndUDjCTifY694YuftPNWSRHQQJn9WwZmSpUBRJmv"
}
],
"ObjType": "BloodlinesLib.Engine.World.Universe, BloodlinesLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Hash": null
}
I can see that the ParentObject for the area object is $ref: 2 Which seems to match the Id of the World object.
But when I deserialize - the ParentObject on the 'Area' object is equal to Null. However the ParentObject on World object is correctly assigned.
Anyone have any ideas why my ParentObject is not being deserialized properly for the Area's ParentObject.
My serialization and deserialize code is here:
public void UpdateIpfs()
{
JsonSerializerSettings sets = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ContractResolver = new NonPublicPropertiesResolver()
};
var obj = Convert.ChangeType(this, ObjType);
if(ObjType == typeof(Universe))
{
Hash = null;
}
var strContent = JsonConvert.SerializeObject(obj, Formatting.Indented, sets);
var ipfs = new IpfsClient();
byte[] data = Encoding.UTF8.GetBytes(strContent);
data = CompressionHelper.Compress(data);
using (MemoryStream ms = new MemoryStream(data))
{
Hash = ipfs.FileSystem.AddAsync(ms).Result.Id.Hash.ToString();
}
if(ParentObject != null)
{
ParentObject.UpdateIpfs();
}
OnUpdate?.Invoke(this);
}
and
public static T LoadFromIpfs<T>(string hash)
{
JsonSerializerSettings sets = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ContractResolver = new NonPublicPropertiesResolver()
};
var ipfs = new IpfsClient();
byte[] data;
using (MemoryStream ms = new MemoryStream())
{
ipfs.FileSystem.ReadFileAsync(hash).Result.CopyTo(ms);
data =ms.ToArray();
}
data = CompressionHelper.Decompress(data);
string content = Encoding.UTF8.GetString(data);
T obj = JsonConvert.DeserializeObject<T>(content, sets);
return obj;
}
Universe.cs
public class IpfsObject
{
public IpfsObject ParentObject;
public Type ObjType { get; set; }
public string Hash { get; set; }
public static T LoadFromIpfs<T>(string hash)
{
JsonSerializerSettings sets = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
var ipfs = new IpfsClient();
byte[] data;
using (MemoryStream ms = new MemoryStream())
{
ipfs.FileSystem.ReadFileAsync(hash).Result.CopyTo(ms);
data =ms.ToArray();
}
data = CompressionHelper.Decompress(data);
string content = Encoding.UTF8.GetString(data);
T obj = JsonConvert.DeserializeObject<T>(content, sets);
return obj;
}
public delegate void OnUpdateDelegate(object obj);
public event OnUpdateDelegate OnUpdate;
public void UpdateIpfs()
{
JsonSerializerSettings sets = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
};
var obj = Convert.ChangeType(this, ObjType);
if(ObjType == typeof(Universe))
{
Hash = null;
}
var strContent = JsonConvert.SerializeObject(obj, Formatting.Indented, sets);
var ipfs = new IpfsClient();
byte[] data = Encoding.UTF8.GetBytes(strContent);
data = CompressionHelper.Compress(data);
using (MemoryStream ms = new MemoryStream(data))
{
Hash = ipfs.FileSystem.AddAsync(ms).Result.Id.Hash.ToString();
}
if(ParentObject != null)
{
ParentObject.UpdateIpfs();
}
OnUpdate?.Invoke(this);
}
}
public class Universe : IpfsObject
{
public Universe()
{
Worlds = new List<World>();
this.ObjType = typeof(Universe);
OnUpdate += Universe_OnUpdate;
}
private void Universe_OnUpdate(object obj)
{
IpfsObject ipfsObj = obj as IpfsObject;
Console.WriteLine("Universe updated: "+ ipfsObj.Hash);
File.WriteAllText("UniverseHash.txt", ipfsObj.Hash);
}
public World CreateWorld()
{
World world = new World(this);
Worlds.Add(world);
return world;
}
public List<World> Worlds { get; set; }
public static Universe GetInstance(string hash = null)
{
if(_universe == null)
{
if(hash == null)
{
_universe = new Universe();
}
else
{
_universe = IpfsObject.LoadFromIpfs<Universe>(hash);
_universe.Hash = hash;
}
}
return _universe;
}
private static Universe _universe;
}
public class World : Ipfs.IpfsObject
{
public World(Universe universe)
{
Time = DateTime.Now;
ParentObject = universe;
this.ObjType = typeof(World);
Areas = new List<Area>();
}
public Area CreateArea(bool findFreespace = false)
{
Area area = new Area(this);
Areas.Add(area);
if (findFreespace)
{
bool b = false;
Random rand = new Random(Guid.NewGuid().GetHashCode());
while (!b)
{
b = area.SetPosition(rand.Next(-500, 500), rand.Next(-500, 500), 0);
}
}
area.UpdateIpfs();
return area;
}
public void SetName(string name)
{
Name = name;
UpdateIpfs();
}
public void SetDescription(string desc)
{
Description = desc;
UpdateIpfs();
}
public DateTime Time { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Area> Areas { get; set; }
}
public class Area : IpfsObject
{
public Area(World worldParent)
{
this.ObjType = typeof(Area);
ParentObject = worldParent;
AreaObjects = new List<AreaObject>();
}
public string Name { get; private set; }
public bool IsInterior { get; private set; }
public bool IsUnderground { get; private set; }
public bool IsArtificial { get; private set; }
public string Description { get; private set; }
public void SetName(string name)
{
Name = name;
UpdateIpfs();
}
public void SetDescription(string desc)
{
Description = desc;
UpdateIpfs();
}
public void SetInterior(bool interior)
{
IsInterior = interior;
UpdateIpfs();
}
public void SetUnderground(bool underground)
{
IsUnderground = underground;
UpdateIpfs();
}
public void SetArtificial(bool artificial)
{
IsArtificial = artificial;
UpdateIpfs();
}
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
public bool SetPosition(int x,int y, int z)
{
World world = (World)ParentObject;
var area = world.Areas.FirstOrDefault(e => e.X == x && e.Y == y && e.Z == z);
if(area != null)
{
return false;
}
this.X = x;
this.Y = y;
this.Z = z;
UpdateIpfs();
return true;
}
}