From 655a5eceb061175c8d85f383234716ad1b803283 Mon Sep 17 00:00:00 2001 From: Entoarox Date: Thu, 4 Jul 2019 16:37:24 +0200 Subject: [PATCH] EF 2.4.4, Tons of bugfixes --- Framework/Core/EntoaroxFrameworkMod.cs | 231 +++++++------------------ Framework/EntoaroxFrameworkAPI.cs | 46 ++--- Framework/RELEASE-NOTES.md | 7 + Framework/manifest.json | 2 +- 4 files changed, 96 insertions(+), 190 deletions(-) diff --git a/Framework/Core/EntoaroxFrameworkMod.cs b/Framework/Core/EntoaroxFrameworkMod.cs index e075f6a..36afdc8 100644 --- a/Framework/Core/EntoaroxFrameworkMod.cs +++ b/Framework/Core/EntoaroxFrameworkMod.cs @@ -32,7 +32,6 @@ internal class EntoaroxFrameworkMod : Mod /********* ** Fields *********/ - private static bool SkipSerializer = false; private static readonly List Farms = new List { "standard", "river", "forest", "hilltop", "wilderniss" }; private static string Verify; internal static JsonSerializer Serializer; @@ -124,9 +123,6 @@ internal class EntoaroxFrameworkMod : Mod /// Provides simplified APIs for writing mods. public override void Entry(IModHelper helper) { - // Check if the serializer override should be skipped - if (typeof(SaveGame).GetField("contract") == null) - SkipSerializer = true; Serializer = new JsonSerializer(); Serializer.Converters.Add(new RectangleConverter()); Serializer.Formatting = Formatting.None; @@ -154,19 +150,20 @@ public override void Entry(IModHelper helper) .Add("player_warp", "player_warp | Warps the player to the given position in the game.", this.Commands); } - + helper.Events.GameLoop.GameLaunched += this.OnGameLaunched; helper.Events.GameLoop.UpdateTicked += this.OnUpdateTicked; helper.Events.GameLoop.SaveLoaded += this.OnSaveLoaded; helper.Events.GameLoop.Saving += this.OnSaving; helper.Events.GameLoop.Saved += this.OnSaved; helper.Events.Input.ButtonReleased += this.OnButtonReleased; - if (SkipSerializer) + // Check if the serializer override should be skipped + if (typeof(SaveGame).GetField("contract") != null) { this.Monitor.Log("Detected incompatible SDV version for serializer function, function is disabled.", LogLevel.Warn); return; } - helper.Events.GameLoop.GameLaunched += this.OnGameLaunched; + helper.Events.GameLoop.GameLaunched += this.SetupSerializer; helper.Events.GameLoop.UpdateTicked += this.EnforceSerializer; } @@ -469,7 +466,7 @@ private void Commands(string command, string[] args) /// The event arguments. private void OnGameLaunched(object sender, GameLaunchedEventArgs e) { - this.SetupSerializer(); + EntoaroxFrameworkAPI.Ready(); } /// Raised after the game state is updated (≈60 times per second). @@ -545,53 +542,30 @@ private void OnSaving(object sender, SavingEventArgs e) var locations = new Dictionary>(); foreach (var loc in this.GetAllLocations()) { - foreach (Chest chest in loc.objects.ToList()[0].Where(a => a.Value is Chest).Select(a => (Chest)a.Value)) - { - chest.items.Set(this.Serialize(data, chest.items.ToList())); - } - var objs = loc.objects.ToList()[0]; - loc.objects.Clear(); - loc.objects.Add(this.Serialize(data, objs)); + foreach (Chest chest in loc.Objects.Values.OfType()) + this.Serialize(data, chest.items); + this.Serialize(data, loc.Objects); if (loc.terrainFeatures != null) - { - loc.terrainFeatures.Set(this.Serialize(features, loc, loc.terrainFeatures.ToList()[0])); - } + this.Serialize(features, loc, loc.terrainFeatures); } foreach (var location in Game1.locations.Where(a => a is ICustomItem).ToArray()) { Game1.locations.Remove(location); this.Serialize(locations, location); } - Game1.player.Items = this.Serialize(data, Game1.player.Items.ToList()); - var house = (Game1.getLocationFromName("FarmHouse") as FarmHouse); - house.fridge.Value?.items.Set(this.Serialize(data, house.fridge.Value.items.ToList())); - this.Monitor.Log("Found and serialized [" + data.Count + "] Item instances", LogLevel.Trace); - this.Monitor.Log("Found and serialized [" + features.Count + "] TerrainFeature instances", LogLevel.Trace); - this.Monitor.Log("Found and serialized [" + locations.Count + "] GameLocation instances", LogLevel.Trace); - string path = Path.Combine(Constants.CurrentSavePath, "Entoarox.Framework"); - this.Helper.Data.WriteJsonFile(Path.Combine(path, "CustomItems.json"), data); - this.Helper.Data.WriteJsonFile(Path.Combine(path, "CustomTerrain.json"), features); - this.Helper.Data.WriteJsonFile(Path.Combine(path, "CustomLocations.json"), locations); - ItemEvents.FireAfterSerialize(); - this.Monitor.Log("Packing complete", LogLevel.Trace); - /* - this.Monitor.Log("Packing custom objects...", LogLevel.Trace); - ItemEvents.FireBeforeSerialize(); - Dictionary data = new Dictionary(); - foreach (GameLocation loc in Game1.locations) - { - foreach (Chest chest in loc.Objects.Values.OfType()) - this.Serialize(data, chest.items); - } - this.Serialize(data, Game1.player.Items); FarmHouse house = Game1.getLocationFromName("FarmHouse") as FarmHouse; - if (house.fridge.Value != null) this.Serialize(data, house.fridge.Value.items); + this.Monitor.Log("Found and serialized [" + data.Count + "] Item instances", LogLevel.Trace); + this.Monitor.Log("Found and serialized [" + features.Count + "] TerrainFeature instances", LogLevel.Trace); + this.Monitor.Log("Found and serialized [" + locations.Count + "] GameLocation instances", LogLevel.Trace); + string path = Path.Combine(Constants.CurrentSavePath, "Entoarox.Framework"); this.Helper.Data.WriteSaveData("custom-items", data); + this.Helper.Data.WriteSaveData("custom-features", features); + this.Helper.Data.WriteSaveData("custom-locations", locations); ItemEvents.FireAfterSerialize(); - */ + this.Monitor.Log("Packing complete", LogLevel.Trace); } } @@ -614,8 +588,19 @@ private void OnSaved(object sender, SavedEventArgs e) // read data this.Monitor.Log("Unpacking custom objects...", LogLevel.Trace); ItemEvents.FireBeforeDeserialize(); + Dictionary> locations = this.Helper.Data.ReadSaveData>>("custom-locations") ?? new Dictionary>(); + foreach (var location in locations) + { + if (location.Value.Item1) + return; + Game1.locations.Remove(Game1.getLocationFromName(location.Key)); + Game1.locations.Add(location.Value.Item2.Restore()); + } IDictionary data = this.Helper.Data.ReadSaveData>("custom-items") ?? new Dictionary(); this.RestoreItems(data); + List> features = this.Helper.Data.ReadSaveData>>("custom-features") ?? new List>(); + foreach(var feature in features) + Game1.getLocationFromName(feature.Item1)?.terrainFeatures.Add(feature.Item2, feature.Item3.Restore()); ItemEvents.FireAfterDeserialize(); } #region Functions @@ -629,51 +614,46 @@ private IEnumerable GetAllLocations() yield return loc; } } - private SerializableDictionary Serialize(List> data, GameLocation location, SerializableDictionary features) + private void Serialize(List> data, GameLocation location, StardewValley.Network.NetVector2Dictionary> features) { - var output = new SerializableDictionary(); - foreach (var pair in features) - { - if (pair.Value is ICustomItem) + foreach(SerializableDictionary dict in features) + foreach (var pair in dict) { - data.Add(new Tuple(location.Name, pair.Key, new InstanceState(pair.Value.GetType().AssemblyQualifiedName, JToken.FromObject(pair.Value, Serializer)))); + if (pair.Value is ICustomItem) + { + data.Add(new Tuple(location.Name, pair.Key, new InstanceState(pair.Value.GetType().AssemblyQualifiedName, JToken.FromObject(pair.Value, Serializer)))); + features.Remove(pair.Key); + } } - else - output.Add(pair.Key, pair.Value); - } - return output; } - private SerializableDictionary Serialize(Dictionary data, SerializableDictionary items) + private void Serialize(Dictionary data, StardewValley.Network.OverlaidDictionary items) { - var output = new SerializableDictionary(); - foreach (var item in items) - { - if (item.Value is ICustomItem) + foreach (var dict in items) + foreach (var item in dict) { - string id = Guid.NewGuid().ToString(); - int counter = 0; - while (data.ContainsKey(id) && counter++ < 25) - id = Guid.NewGuid().ToString(); - if (counter >= 25) - throw new TimeoutException("Unable to assign a GUID to all items!"); - SObject obj = new SObject() + if (item.Value is ICustomItem) { - Stack = item.Value.getStack(), - ParentSheetIndex = 0, - Type = id, - Name = "(Entoarox.Framework.ICustomItem)", - Price = item.Value.salePrice(), - }; - if (item.Value is Placeholder pitm) - data.Add(id, new InstanceState(pitm.Id, pitm.Data)); - else - data.Add(id, new InstanceState(item.GetType().AssemblyQualifiedName, JToken.FromObject(item, Serializer))); - output.Add(item.Key, obj); + string id = Guid.NewGuid().ToString(); + int counter = 0; + while (data.ContainsKey(id) && counter++ < 25) + id = Guid.NewGuid().ToString(); + if (counter >= 25) + throw new TimeoutException("Unable to assign a GUID to all items!"); + SObject obj = new SObject() + { + Stack = item.Value.getStack(), + ParentSheetIndex = 0, + Type = id, + Name = "(Entoarox.Framework.ICustomItem)", + Price = item.Value.salePrice(), + }; + if (item.Value is Placeholder pitm) + data.Add(id, new InstanceState(pitm.Id, pitm.Data)); + else + data.Add(id, new InstanceState(item.GetType().AssemblyQualifiedName, JToken.FromObject(item, Serializer))); + items[item.Key] = obj; + } } - else - output.Add(item.Key, item.Value); - } - return output; } private GameLocation Serialize(Dictionary> data, GameLocation location, bool building = false) { @@ -685,93 +665,6 @@ private GameLocation Serialize(Dictionary> da data.Add(building ? guid : location.Name, new Tuple(building, new InstanceState(location.GetType().AssemblyQualifiedName, JToken.FromObject(location, Serializer)))); return output; } - private List Serialize(Dictionary data, List items) - { - List output = new List(); - foreach (Item item in items) - { - if (item is ICustomItem) - { - string id = Guid.NewGuid().ToString(); - int counter = 0; - while (data.ContainsKey(id) && counter++ < 25) - id = Guid.NewGuid().ToString(); - if (counter >= 25) - throw new TimeoutException("Unable to assign a GUID to all items!"); - SObject obj = new SObject() - { - Stack = item.getStack(), - ParentSheetIndex = 0, - Type = id, - Name = "(Entoarox.Framework.ICustomItem)", - Price = item.salePrice(), - }; - if (item is Placeholder pitm) - data.Add(id, new InstanceState(pitm.Id, pitm.Data)); - else - data.Add(id, new InstanceState(item.GetType().AssemblyQualifiedName, JToken.FromObject(item, Serializer))); - output.Add(obj); - } - else - output.Add(item); - } - return output; - } - private List Deserialize(Dictionary data, List items) - { - List output = new List(); - foreach (Item item in items) - { - if (item is SObject itm && itm.name.Equals("(Entoarox.Framework.ICustomItem)")) - { - if (data.ContainsKey(itm.Type)) - { - var result = data[itm.Type].Restore(); - if (result == null) - continue; - if (result is SObject obj) - obj.reloadSprite(); - output.Add(result); - } - else - { - output.Add(item); - this.Monitor.Log("Unable to deserialize custom item, GUID does not exist: " + itm.Type, LogLevel.Error); - } - } - else - output.Add(item); - } - return output; - } - private SerializableDictionary Deserialize(Dictionary data, SerializableDictionary items) - { - var output = new SerializableDictionary(); - foreach (var item in items) - { - if (item.Value is SObject itm && itm.name.Equals("(Entoarox.Framework.ICustomItem)")) - { - if (data.ContainsKey(itm.Type)) - { - var result = data[itm.Type].Restore(); - if (result == null) - continue; - result.reloadSprite(); - output.Add(item.Key, result); - } - else - { - output.Add(item.Key, item.Value); - this.Monitor.Log("Unable to deserialize custom item, GUID does not exist: " + itm.Type, LogLevel.Error); - } - } - else - output.Add(item.Key, item.Value); - } - return output; - } - #endregion - private void RestoreItems(IDictionary data) { foreach (GameLocation loc in Game1.locations) @@ -858,11 +751,11 @@ private void Deserialize(IDictionary data, IList it } } } - + #endregion functions /**** ** Serializer ****/ - private void SetupSerializer() + private void SetupSerializer(object sender, EventArgs e) { this.MainSerializer = new XmlSerializer(typeof(SaveGame), EntoaroxFrameworkMod.SerialiserTypes.Concat(EntoaroxFrameworkMod.SerializerTypes).ToArray()); this.FarmerSerializer = new XmlSerializer(typeof(Farmer), EntoaroxFrameworkMod.FarmerTypes.Concat(EntoaroxFrameworkMod.SerializerTypes).ToArray()); @@ -871,7 +764,7 @@ private void SetupSerializer() this.EnforceSerializer(null, null); } - private void EnforceSerializer(object sender, UpdateTickedEventArgs e) + private void EnforceSerializer(object sender, EventArgs e) { SaveGame.serializer = this.MainSerializer; SaveGame.farmerSerializer = this.FarmerSerializer; diff --git a/Framework/EntoaroxFrameworkAPI.cs b/Framework/EntoaroxFrameworkAPI.cs index 3edccf8..9fc7046 100644 --- a/Framework/EntoaroxFrameworkAPI.cs +++ b/Framework/EntoaroxFrameworkAPI.cs @@ -15,6 +15,7 @@ public class EntoaroxFrameworkAPI /********* ** Fields *********/ + internal static EntoaroxFrameworkAPI Singleton; private static PlayerModifier Modifier = new PlayerModifier(); private static readonly Dictionary RunBoosts = new Dictionary(); private static readonly Dictionary WalkBoosts = new Dictionary(); @@ -44,16 +45,21 @@ private interface IJsonAssetsAPI public EntoaroxFrameworkAPI() { - this.RegisterIdResolver("sdv.object", id => new SObject(Convert.ToInt32(id), 1)); - this.RegisterIdResolver("sdv.craftable", id => new SObject(Vector2.Zero, Convert.ToInt32(id), 1)); - this.RegisterIdResolver("sdv.hat", id => new Hat(Convert.ToInt32(id))); - this.RegisterIdResolver("sdv.furniture", id => new Furniture(Convert.ToInt32(id), Vector2.Zero)); - this.RegisterIdResolver("sdv.ring", id => new Ring(Convert.ToInt32(id))); - this.RegisterIdResolver("sdv.flooring", id => new Wallpaper(Convert.ToInt32(id), true)); - this.RegisterIdResolver("sdv.wallpaper", id => new Wallpaper(Convert.ToInt32(id))); - this.RegisterIdResolver("sdv.sign", id => new Sign(Vector2.Zero, Convert.ToInt32(id))); - this.RegisterIdResolver("sdv.weapon", id => new MeleeWeapon(Convert.ToInt32(id))); - this.RegisterIdResolver("sdv.wine", id => + Singleton = this; + } + + internal static void Ready() + { + Singleton.RegisterIdResolver("sdv.object", id => new SObject(Convert.ToInt32(id), 1)); + Singleton.RegisterIdResolver("sdv.craftable", id => new SObject(Vector2.Zero, Convert.ToInt32(id), 1)); + Singleton.RegisterIdResolver("sdv.hat", id => new Hat(Convert.ToInt32(id))); + Singleton.RegisterIdResolver("sdv.furniture", id => new Furniture(Convert.ToInt32(id), Vector2.Zero)); + Singleton.RegisterIdResolver("sdv.ring", id => new Ring(Convert.ToInt32(id))); + Singleton.RegisterIdResolver("sdv.flooring", id => new Wallpaper(Convert.ToInt32(id), true)); + Singleton.RegisterIdResolver("sdv.wallpaper", id => new Wallpaper(Convert.ToInt32(id))); + Singleton.RegisterIdResolver("sdv.sign", id => new Sign(Vector2.Zero, Convert.ToInt32(id))); + Singleton.RegisterIdResolver("sdv.weapon", id => new MeleeWeapon(Convert.ToInt32(id))); + Singleton.RegisterIdResolver("sdv.wine", id => { SObject item = new SObject(Convert.ToInt32(id), 1); SObject wine = new SObject(348, 1) @@ -65,7 +71,7 @@ public EntoaroxFrameworkAPI() wine.preservedParentSheetIndex.Value = item.ParentSheetIndex; return wine; }); - this.RegisterIdResolver("sdv.jelly", id => + Singleton.RegisterIdResolver("sdv.jelly", id => { SObject item = new SObject(Convert.ToInt32(id), 1); SObject jelly = new SObject(344, 1) @@ -77,7 +83,7 @@ public EntoaroxFrameworkAPI() jelly.preservedParentSheetIndex.Value = item.ParentSheetIndex; return jelly; }); - this.RegisterIdResolver("sdv.juice", id => + Singleton.RegisterIdResolver("sdv.juice", id => { SObject item = new SObject(Convert.ToInt32(id), 1); SObject juice = new SObject(350, 1) @@ -89,7 +95,7 @@ public EntoaroxFrameworkAPI() juice.preservedParentSheetIndex.Value = item.ParentSheetIndex; return juice; }); - this.RegisterIdResolver("sdv.pickled", id => + Singleton.RegisterIdResolver("sdv.pickled", id => { SObject item = new SObject(Convert.ToInt32(id), 1); SObject pickled = new SObject(342, 1) @@ -101,7 +107,7 @@ public EntoaroxFrameworkAPI() pickled.preservedParentSheetIndex.Value = item.ParentSheetIndex; return pickled; }); - this.RegisterIdResolver("sdv.honey", id => + Singleton.RegisterIdResolver("sdv.honey", id => { SObject item = new SObject(Convert.ToInt32(id), 1); SObject honey = new SObject(Vector2.Zero, 340, "Honey", false, true, false, false) @@ -135,7 +141,7 @@ public EntoaroxFrameworkAPI() } return honey; }); - this.RegisterIdResolver("sdv.other", id => { + Singleton.RegisterIdResolver("sdv.other", id => { switch(id) { case "chest": @@ -154,11 +160,11 @@ public EntoaroxFrameworkAPI() if (registry.IsLoaded("spacechase0.JsonAssets")) { IJsonAssetsAPI jaApi = registry.GetApi("spacechase0.JsonAssets"); - this.RegisterIdResolver("ja.object", id => new SObject(jaApi.GetObjectId(id), 1)); - this.RegisterIdResolver("ja.craftable", id => new SObject(Vector2.Zero, jaApi.GetBigCraftableId(id), 1)); - this.RegisterIdResolver("ja.hat", id => new Hat(jaApi.GetHatId(id))); - this.RegisterIdResolver("ja.weapon", id => new MeleeWeapon(jaApi.GetWeaponId(id))); - this.RegisterIdResolver("ja.ring", id => new Ring(jaApi.GetObjectId(id))); + Singleton.RegisterIdResolver("ja.object", id => new SObject(jaApi.GetObjectId(id), 1)); + Singleton.RegisterIdResolver("ja.craftable", id => new SObject(Vector2.Zero, jaApi.GetBigCraftableId(id), 1)); + Singleton.RegisterIdResolver("ja.hat", id => new Hat(jaApi.GetHatId(id))); + Singleton.RegisterIdResolver("ja.weapon", id => new MeleeWeapon(jaApi.GetWeaponId(id))); + Singleton.RegisterIdResolver("ja.ring", id => new Ring(jaApi.GetObjectId(id))); } } diff --git a/Framework/RELEASE-NOTES.md b/Framework/RELEASE-NOTES.md index 4b0ce82..4d4d023 100644 --- a/Framework/RELEASE-NOTES.md +++ b/Framework/RELEASE-NOTES.md @@ -1,4 +1,11 @@ ## Release notes +## 2.4.4 +Released 04 July 2019 +* Fixed the ICustomItem `TerrainFeature` and `GameLocation` support +* Fixed SMAPI listeners being removed unintentionally +* Fixed a cross-mod API error due to trying to setup to early +* Fixed the serializer detection being inverted, disabling on the wrong platforms + ## 2.4.3 Released 04 July 2019 * Updated for the upcoming SMAPI 3.0 again. diff --git a/Framework/manifest.json b/Framework/manifest.json index fc2fb12..7573ac1 100644 --- a/Framework/manifest.json +++ b/Framework/manifest.json @@ -1,7 +1,7 @@ { "Name": "Entoarox Framework", "Author": "Entoarox", - "Version": "2.4.3", + "Version": "2.4.4", "Description": "A collection of APIs to make modding easier.", "UniqueID": "Entoarox.EntoaroxFramework", "EntryDll": "EntoaroxFramework.dll",