using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PyTK.CustomElementHandler;
using Revitalize.Framework.Objects;
using Revitalize.Framework.Objects.Furniture;
using Revitalize.Framework.Utilities.Serialization.ContractResolvers;
using Revitalize.Framework.Utilities.Serialization.Converters;
using StardewValley;
using StardewValley.Objects;
namespace Revitalize.Framework.Utilities
{
///
/// Handles serialization of all objects in existence.
///
public class Serializer
{
///
/// The actual json serializer.
///
private JsonSerializer serializer;
///
/// All files to be cleaned up after loading.
///
private Dictionary> filesToDelete = new Dictionary>();
///
/// The items to remove for deletion.
///
private List- itemsToRemove = new List
- ();
///
/// The settings used by the seralizer
///
private JsonSerializerSettings settings;
public static NetFieldConverter NetFieldConverter;
///
/// Constructor.
///
public Serializer()
{
this.serializer = new JsonSerializer();
this.serializer.Formatting = Formatting.Indented;
this.serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
this.serializer.NullValueHandling = NullValueHandling.Include;
this.serializer.ContractResolver = new NetFieldContract();
NetFieldConverter = new NetFieldConverter();
this.addConverter(new Framework.Utilities.Serialization.Converters.RectangleConverter());
this.addConverter(new Framework.Utilities.Serialization.Converters.Texture2DConverter());
this.addConverter(new Framework.Utilities.Serialization.Converters.ItemCoverter());
this.addConverter(NetFieldConverter);
//this.addConverter(new Framework.Utilities.Serialization.Converters.CustomObjectDataConverter());
//this.addConverter(new Framework.Utilities.Serialization.Converters.NetFieldConverter());
//this.addConverter(new Framework.Utilities.Serialization.Converters.Vector2Converter());
//this.gatherAllFilesForCleanup();
this.settings = new JsonSerializerSettings();
foreach (JsonConverter converter in this.serializer.Converters)
{
this.settings.Converters.Add(converter);
}
this.settings.Formatting = Formatting.Indented;
this.settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
this.settings.NullValueHandling = NullValueHandling.Include;
this.settings.ContractResolver = new NetFieldContract();
}
///
/// Process all the save data for objects to be deleted by this mod.
///
private void gatherAllFilesForCleanup()
{
if (!Directory.Exists(Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData"))) Directory.CreateDirectory(Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData"));
this.filesToDelete.Clear();
string[] directories = Directory.GetDirectories(Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData"));
foreach (string playerData in directories)
{
string objectPath = Path.Combine(playerData, "SavedObjectInformation");
string[] objectFiles = Directory.GetFiles(objectPath);
foreach (string file in objectFiles)
{
string playerName = new DirectoryInfo(objectPath).Parent.Name;
if (this.filesToDelete.ContainsKey(playerName))
{
this.filesToDelete[playerName].Add(file);
//Revitalize.ModCore.log("Added File: " + file);
}
else
{
this.filesToDelete.Add(playerName, new List());
//Revitalize.ModCore.log("Added Player Key: " + playerName);
this.filesToDelete[playerName].Add(file);
//Revitalize.ModCore.log("Added File: " + file);
}
}
}
}
private void deleteFilesBeforeSave()
{
if (!Directory.Exists(Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData"))) Directory.CreateDirectory(Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData"));
this.filesToDelete.Clear();
string[] directories = Directory.GetDirectories(Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData"));
foreach (string playerData in directories)
{
string objectPath = Path.Combine(playerData, "SavedObjectInformation");
string[] objectFiles = Directory.GetFiles(objectPath);
foreach (string file in objectFiles)
{
string playerName = new DirectoryInfo(objectPath).Parent.Name;
if (playerName != this.getUniqueCharacterString()) return;
else
{
File.Delete(file);
}
}
}
}
///
/// Called after load to deal with internal file cleanUp
///
public void afterLoad()
{
ModCore.log("WHAT");
this.deleteAllUnusedFiles();
//this.removeNullObjects();
this.restoreModObjects();
}
///
/// Restore mod objects to inventories and world after load.
///
public void restoreModObjects()
{
//ModCore.log("Restore all mod objects!");
//Replace all items in the world.
List objsToRestore = new List();
foreach (var v in ModCore.ObjectGroups)
{
foreach (var obj in v.Value.objects.Values)
{
//(obj as CustomObject).replaceAfterLoad();
objsToRestore.Add(obj as CustomObject);
}
}
foreach(CustomObject o in objsToRestore)
{
(o as CustomObject).replaceAfterLoad();
}
//Replace all held items or items in inventories.
foreach (GameLocation loc in LocationUtilities.GetAllLocations())
{
//ModCore.log("Looking at location: " + loc);
foreach (StardewValley.Object c in loc.Objects.Values)
{
if (c is Chest)
{
List
- toRemove = new List
- ();
List
- toAdd = new List
- ();
foreach (Item o in (c as Chest).items)
{
if (o == null) continue;
if (o is Chest && o.Name != "Chest")
{
Item I = this.GetItemFromChestName(o.Name);
ModCore.log("Found a custom item in a chest!");
toAdd.Add(I);
toRemove.Add(o);
}
}
foreach (Item i in toRemove)
{
(c as Chest).items.Remove(i);
}
foreach (Item I in toAdd)
{
(c as Chest).items.Add(I);
}
}
else if(c is Chest && c.Name != "Chest")
{
loc.objects[c.TileLocation] = (StardewValley.Object)this.GetItemFromChestName(c.Name);
ModCore.log("Found a custom item that is a chest!");
}
else if (c is CustomObject)
{
if ((c as CustomObject).info.inventory == null) continue;
List
- toRemove = new List
- ();
List
- toAdd = new List
- ();
foreach (Item o in (c as CustomObject).info.inventory.items)
{
if (o == null) continue;
if (o is Chest && o.Name != "Chest")
{
Item I = this.GetItemFromChestName(o.Name);
toAdd.Add(I);
toRemove.Add(o);
}
}
foreach (Item i in toRemove)
{
(c as Chest).items.Remove(i);
}
foreach (Item I in toAdd)
{
(c as Chest).items.Add(I);
}
if (c.heldObject.Value != null)
{
if (c.heldObject.Value is Chest && c.heldObject.Value.Name != "Chest")
{
ModCore.log("Found a custom object as a held object!");
Item I = this.GetItemFromChestName(c.heldObject.Value.Name);
c.heldObject.Value = (StardewValley.Object)I;
}
}
}
else if (c is StardewValley.Object)
{
if (c.heldObject.Value != null)
{
if (c.heldObject.Value is Chest && c.heldObject.Value.Name != "Chest")
{
ModCore.log("Found a custom object as a held object!");
Item I = this.GetItemFromChestName(c.heldObject.Value.Name);
c.heldObject.Value = (StardewValley.Object)I;
}
}
}
}
}
List
- toAdd2 = new List
- ();
List
- toRemove2 = new List
- ();
foreach (Item I in Game1.player.Items)
{
if (I == null) continue;
else
{
if (I is Chest && I.Name != "Chest")
{
Item ret = this.GetItemFromChestName(I.Name);
toAdd2.Add(ret);
toRemove2.Add(I);
}
}
}
foreach (Item i in toRemove2)
{
Game1.player.Items.Remove(i);
}
foreach (Item I in toAdd2)
{
Game1.player.addItemToInventory(I);
}
}
///
/// Gets an Item recreated from PYTK's chest replacement objects.
///
///
///
public Item GetItemFromChestName(string JsonName)
{
//ModCore.log("Found a custom object in a chest!");
string jsonString = JsonName;
//ModCore.log(JsonName);
string dataSplit= jsonString.Split(new string[] { "<" }, StringSplitOptions.None)[1];
string backUpGUID = dataSplit.Split('|')[0];
string[] guidArr = jsonString.Split(new string[] { "|" }, StringSplitOptions.None);
foreach(string s in guidArr)
{
//ModCore.log(s);
}
string guidName = guidArr[guidArr.Length - 1];
guidName = guidName.Substring(5);
try
{
Guid g = Guid.Parse(guidName);
}
catch (Exception err)
{
Guid d = Guid.Parse(backUpGUID);
guidName = backUpGUID;
}
//ModCore.log("THE GUID IS:"+ guidName);
//ModCore.log(jsonString);
string type = jsonString.Split('|')[2];
Item I = (Item)ModCore.Serializer.DeserializeGUID(guidName, Type.GetType(type));
if (I is MultiTiledObject)
{
(I as MultiTiledObject).recreate();
}
return I;
}
public Item DeserializeFromFarmhandInventory(string JsonName)
{
//ModCore.log("Found a custom object in a chest!");
string jsonString = JsonName;
//ModCore.log(JsonName);
string dataSplit = jsonString.Split(new string[] { "<" }, StringSplitOptions.None)[2];
string backUpGUID = dataSplit.Split('|')[0];
string[] guidArr = jsonString.Split(new string[] { "|" }, StringSplitOptions.None);
string infoStr = jsonString.Split(new string[] { "<" }, StringSplitOptions.None)[0];
string guidStr= jsonString.Split(new string[] { "<" }, StringSplitOptions.None)[1];
CustomObjectData pyTkData = ModCore.Serializer.DeserializeFromJSONString(dataSplit);
Type t = Type.GetType(pyTkData.type);
string id = pyTkData.id;
//Need Item info
string guidName = backUpGUID;
string infoSplit = infoStr.Split('|')[3];
infoSplit = infoSplit.Substring(3);
BasicItemInformation info = ModCore.Serializer.DeserializeFromJSONString(infoSplit);
CustomObject clone = (CustomObject)ModCore.ObjectManager.getItemByIDAndType(id, t);
if (clone != null)
{
clone.info = info;
ModCore.log("Guid is????:"+guidStr);
clone.guid = Guid.Parse(guidStr);
return clone;
}
return null;
}
public void returnToTitle()
{
//this.gatherAllFilesForCleanup();
}
[Obsolete]
private void removeNullObjects()
{
List
- removalList = new List
- ();
foreach (Item I in Game1.player.Items)
{
if (I == null) continue;
if (I.DisplayName.Contains("Revitalize.Framework") && (I is Chest))
{
removalList.Add(I);
}
}
foreach (Item I in removalList)
{
Game1.player.Items.Remove(I);
}
}
///
/// Removes the file from all files that will be deleted.
///
///
///
private void removeFileFromDeletion(string playerDirectory, string fileName)
{
if (this.filesToDelete.ContainsKey(playerDirectory))
{
//Revitalize.ModCore.log("Removing from deletion: " + fileName);
this.filesToDelete[playerDirectory].Remove(fileName);
}
else
{
//Revitalize.ModCore.log("Found key: " + playerDirectory);
//Revitalize.ModCore.log("Found file: " + fileName);
}
}
///
/// Deletes unused object data.
///
private void deleteAllUnusedFiles()
{
foreach (KeyValuePair> pair in this.filesToDelete)
{
foreach (string file in pair.Value)
{
File.Delete(file);
}
}
}
///
/// Adds a new converter to the json serializer.
///
/// The type of json converter to add to the Serializer.
public void addConverter(JsonConverter converter)
{
this.serializer.Converters.Add(converter);
}
///
/// Deserializes an object from a .json file.
///
/// The type of object to deserialize into.
/// The path to the file.
/// An object of specified type T.
public T Deserialize(string p)
{
string json = "";
foreach (string line in File.ReadLines(p))
{
json += line;
}
using (StreamReader sw = new StreamReader(p))
using (JsonReader reader = new JsonTextReader(sw))
{
var obj = this.serializer.Deserialize(reader);
return obj;
}
}
///
/// Deserializes an object from a .json file.
///
/// The type of object to deserialize into.
/// The path to the file.
/// An object of specified type T.
public object Deserialize(string p, Type T)
{
string json = "";
foreach (string line in File.ReadLines(p))
{
json += line;
}
using (StreamReader sw = new StreamReader(p))
using (JsonReader reader = new JsonTextReader(sw))
{
object obj = this.serializer.Deserialize(reader, T);
return obj;
}
}
///
/// Serializes an object to a .json file.
///
///
///
public void Serialize(string path, object o)
{
using (StreamWriter sw = new StreamWriter(path))
using (JsonWriter writer = new JsonTextWriter(sw))
{
this.serializer.Serialize(writer, o);
}
}
///
/// Serialize a data structure into an file.
///
///
///
public void SerializeGUID(string fileName, object obj)
{
string path = Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData", Game1.player.Name + "_" + Game1.player.UniqueMultiplayerID, "SavedObjectInformation", fileName + ".json");
Directory.CreateDirectory(Path.GetDirectoryName(path));
this.Serialize(path, obj);
}
///
/// Deserialze a file into it's proper data structure.
///
/// The type of data structure to deserialze to.
/// The name of the file to deserialize from.
/// A data structure object deserialize from a json string in a file.
public object DeserializeGUID(string fileName, Type T)
{
string path = Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData", Game1.player.Name + "_" + Game1.player.UniqueMultiplayerID, "SavedObjectInformation", fileName + ".json");
this.removeFileFromDeletion((Game1.player.Name + "_" + Game1.player.UniqueMultiplayerID), path);
return this.Deserialize(path, T);
}
///
/// Deserialze a file into it's proper data structure.
///
/// The type of data structure to deserialze to.
/// The name of the file to deserialize from.
/// A data structure object deserialize from a json string in a file.
public T DeserializeGUID(string fileName)
{
string path = Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "SaveData", Game1.player.Name + "_" + Game1.player.UniqueMultiplayerID, "SavedObjectInformation", fileName + ".json");
//this.removeFileFromDeletion((Game1.player.Name + "_" + Game1.player.UniqueMultiplayerID),path);
if (File.Exists(path))
{
//ModCore.log("Deseralizing file:" + path);
return this.Deserialize(path);
}
else
{
throw new Exception("Can't deserialize file. Default returned. " + path);
}
}
///
/// Converts objects to json form.
///
///
///
public string ToJSONString(object o)
{
return JsonConvert.SerializeObject(o, this.settings);
}
///
/// Converts from json form to objects.
///
///
///
///
public T DeserializeFromJSONString(string info)
{
return JsonConvert.DeserializeObject(info, this.settings);
}
///
/// Converts from json form to objects.
///
///
///
///
public object DeserializeFromJSONString(string info, Type T)
{
return JsonConvert.DeserializeObject(info, T, this.settings);
}
///
/// Deserailizes a content file for the mod.
///
///
///
///
public T DeserializeContentFile(string pathToFile)
{
if (File.Exists(pathToFile))
{
return this.Deserialize(pathToFile);
}
else
{
return default(T);
}
}
///
/// Serializes a content file if it doesn't already exist. If it does exist this does nothing as to not override the content file.
///
/// The name to name the file. So a file named MyFile would be a MyFile.json
/// The actual to serialize.
/// The sub folder path inside of the Content folder for this mod.
public void SerializeContentFile(string fileName, object obj, string extensionFolder)
{
string path = Path.Combine(Revitalize.ModCore.ModHelper.DirectoryPath, "Content", extensionFolder, fileName + ".json");
Directory.CreateDirectory(Path.GetDirectoryName(path));
if (File.Exists(path)) return;
this.Serialize(path, obj);
}
///
/// Deletes all .json saved objects before saving.
///
///
///
public void DayEnding_CleanUpFilesForDeletion(object o, StardewModdingAPI.Events.DayEndingEventArgs sender)
{
//ModCore.log("Day ending now delete files!");
this.deleteFilesBeforeSave();
}
///
/// Gets the unique character path string.
///
///
public string getUniqueCharacterString()
{
return Game1.player.Name + "_" + Game1.player.UniqueMultiplayerID;
}
///
/// https://stackoverflow.com/questions/2742276/how-do-i-check-if-a-type-is-a-subtype-or-the-type-of-an-object
///
///
///
///
public bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
{
return potentialDescendant.IsSubclassOf(potentialBase)
|| potentialDescendant == potentialBase;
}
public bool IsSubclass(Type potentialBase, Type potentialDescendant)
{
return potentialDescendant.IsSubclassOf(potentialBase);
}
}
}