From e4dc2bdd7d421b26b9752b4d2afea8533093a0f7 Mon Sep 17 00:00:00 2001 From: skorpnok <6632336+skorpnok@users.noreply.github.com> Date: Tue, 2 Jun 2026 20:07:54 +0200 Subject: [PATCH] Split ClientConfig off of ServerConfig Synchronize runtime ServerConfig with Server when joining fix some behaviours getting added twice --- .../CastingTweaks/ToolMoldUnitsPatch.cs | 16 ----- .../ClientTweaks/AnvilRecipeSelectorPatch.cs | 2 +- SmithingPlus/Config/ClientConfig.cs | 13 ++++ SmithingPlus/Config/ConfigLoader.cs | 36 +++++++++-- SmithingPlus/Config/ServerConfig.cs | 10 +-- SmithingPlus/Core.cs | 61 +++++++++++++------ .../CollectibleBehaviorBrokenToolHead.cs | 2 +- .../CollectibleBehaviorRepairableTool.cs | 4 +- SmithingPlus/Util/TreeAttributeSerialize.cs | 48 +++++++++++++++ 9 files changed, 141 insertions(+), 51 deletions(-) create mode 100644 SmithingPlus/Config/ClientConfig.cs create mode 100644 SmithingPlus/Util/TreeAttributeSerialize.cs diff --git a/SmithingPlus/CastingTweaks/ToolMoldUnitsPatch.cs b/SmithingPlus/CastingTweaks/ToolMoldUnitsPatch.cs index 6d3c6ef..84ff379 100644 --- a/SmithingPlus/CastingTweaks/ToolMoldUnitsPatch.cs +++ b/SmithingPlus/CastingTweaks/ToolMoldUnitsPatch.cs @@ -37,22 +37,6 @@ public static void Initialize_Postfix(BlockEntityToolMold __instance, ref int __ ___requiredUnits = requiredUnitsRounded; } - [HarmonyPostfix] - [HarmonyPatch(nameof(BlockEntityToolMold.ToTreeAttributes))] - public static void ToTreeAttributes_Postfix(BlockEntityToolMold __instance, int ___requiredUnits, - ITreeAttribute tree) - { - tree.SetInt("requiredUnits", ___requiredUnits); - } - - [HarmonyPostfix] - [HarmonyPatch(nameof(BlockEntityToolMold.FromTreeAttributes))] - public static void FromTreeAttributes_Postfix(ITreeAttribute tree, ref int ___requiredUnits, - IWorldAccessor worldForResolve) - { - ___requiredUnits = tree.GetInt("requiredUnits"); - } - public static int GetPatchedRequiredUnits(ICoreAPI api, Block toolMold, ItemStack fromMetal) { var dropStacks = GetMoldedStacksStatic(api, toolMold, fromMetal); diff --git a/SmithingPlus/ClientTweaks/AnvilRecipeSelectorPatch.cs b/SmithingPlus/ClientTweaks/AnvilRecipeSelectorPatch.cs index 19e68fb..4b67051 100644 --- a/SmithingPlus/ClientTweaks/AnvilRecipeSelectorPatch.cs +++ b/SmithingPlus/ClientTweaks/AnvilRecipeSelectorPatch.cs @@ -83,7 +83,7 @@ int ___prevSlotOver { _defaultSkillItemCache = ___skillItems; var cellCount = Math.Max(1, ___skillItems.Count); - var columns = Math.Min(cellCount, Core.Config?.AnvilRecipeSelectionColumns ?? 8); + var columns = Math.Min(cellCount, Core.CConfig?.AnvilRecipeSelectionColumns ?? 8); var rows = (int)Math.Ceiling(cellCount / (double)columns); var slotSize = GuiElementPassiveItemSlot.unscaledSlotSize + GuiElementItemSlotGridBase.unscaledSlotPadding; var fixedWidth = Math.Max(300.0, columns * slotSize); diff --git a/SmithingPlus/Config/ClientConfig.cs b/SmithingPlus/Config/ClientConfig.cs new file mode 100644 index 0000000..717310f --- /dev/null +++ b/SmithingPlus/Config/ClientConfig.cs @@ -0,0 +1,13 @@ +namespace SmithingPlus.Config; + +public class ClientConfig +{ + public bool ShowRepairedCount { get; set; } = true; + public bool ShowBrokenCount { get; set; } = true; + public bool ShowRepairSmithName { get; set; } = false; + public bool AnvilShowRecipeVoxels { get; set; } = true; + public bool RememberHammerToolMode { get; set; } = true; + public bool ShowWorkableTemperature { get; set; } = true; + public bool HandbookExtraInfo { get; set; } = true; + public int AnvilRecipeSelectionColumns { get; set; } = 8; +} diff --git a/SmithingPlus/Config/ConfigLoader.cs b/SmithingPlus/Config/ConfigLoader.cs index 58bf4b4..a5cd07f 100644 --- a/SmithingPlus/Config/ConfigLoader.cs +++ b/SmithingPlus/Config/ConfigLoader.cs @@ -7,8 +7,10 @@ namespace SmithingPlus.Config; [UsedImplicitly] public class ConfigLoader : ModSystem { - private const string ConfigName = "SmithingPlus.json"; + private const string ServerConfigName = "SmithingPlus.json"; + private const string ClientConfigName = "SmithingPlusClient.json"; public static ServerConfig Config { get; private set; } + public static ClientConfig CConfig { get; private set; } public override double ExecuteOrder() { @@ -19,14 +21,39 @@ public override void StartPre(ICoreAPI api) { try { - Config = api.LoadModConfig(ConfigName); + CConfig = api.LoadModConfig(ClientConfigName); + if (CConfig == null) + { + // Try to load settings from old mixed file + CConfig = api.LoadModConfig(ServerConfigName); + if (CConfig == null) + { + CConfig = new ClientConfig(); + Mod.Logger.VerboseDebug("Client Config file not found, creating a new one..."); + } else + { + Mod.Logger.VerboseDebug("Client Config file not found, creating from old combined config"); + } + } + + api.StoreModConfig(CConfig, ClientConfigName); + } + catch (Exception e) + { + Mod.Logger.Error("Failed to load client config, you probably made a typo: {0}", e); + CConfig = new ClientConfig(); + } + + try + { + Config = api.LoadModConfig(ServerConfigName); if (Config == null) { Config = new ServerConfig(); Mod.Logger.VerboseDebug("Config file not found, creating a new one..."); } - api.StoreModConfig(Config, ConfigName); + api.StoreModConfig(Config, ServerConfigName); } catch (Exception e) { @@ -55,6 +82,7 @@ public override void Start(ICoreAPI api) public override void Dispose() { Config = null; + CConfig = null; base.Dispose(); } -} \ No newline at end of file +} diff --git a/SmithingPlus/Config/ServerConfig.cs b/SmithingPlus/Config/ServerConfig.cs index 835b4ad..5bc0146 100644 --- a/SmithingPlus/Config/ServerConfig.cs +++ b/SmithingPlus/Config/ServerConfig.cs @@ -25,9 +25,6 @@ public class ServerConfig public string WorkItemSelector { get; set; } = "@(.*):workitem-(.*)"; public bool DontRepairBrokenToolHeads { get; set; } = false; public bool CanRepairForlornHopeEstoc { get; set; } = true; - public bool ShowRepairedCount { get; set; } = true; - public bool ShowBrokenCount { get; set; } = true; - public bool ShowRepairSmithName { get; set; } = false; public float HelveHammerSmithingQualityModifier { get; set; } = 1; public bool ArrowsDropBits { get; set; } = true; public string ArrowSelector { get; set; } = "@(.*):arrow-(.*)"; @@ -36,11 +33,6 @@ public class ServerConfig public bool DynamicMoldUnits { get; set; } = false; public bool HammerTweaks { get; set; } = true; public bool RotationRequiresTongs { get; set; } = false; - public bool AnvilShowRecipeVoxels { get; set; } = true; - public bool RememberHammerToolMode { get; set; } = true; - public bool ShowWorkableTemperature { get; set; } = true; - public bool HandbookExtraInfo { get; set; } = true; - public int AnvilRecipeSelectionColumns { get; set; } = 8; // public bool StoneSmithing { get; set; } = false; [JsonIgnore] @@ -50,4 +42,4 @@ public class ServerConfig .Append(ModStackAttributes.RepairedToolStack) .Append(ModStackAttributes.CastTool) .ToArray(); -} \ No newline at end of file +} diff --git a/SmithingPlus/Core.cs b/SmithingPlus/Core.cs index 0e4b3eb..8d74a7f 100644 --- a/SmithingPlus/Core.cs +++ b/SmithingPlus/Core.cs @@ -26,7 +26,10 @@ public partial class Core : ModSystem public static ILogger Logger { get; private set; } public static ICoreAPI Api { get; private set; } public static Harmony HarmonyInstance { get; private set; } - public static ServerConfig Config => ConfigLoader.Config; + public static ServerConfig LocalConfig => ConfigLoader.Config; + public static ClientConfig CConfig => ConfigLoader.CConfig; + public static ServerConfig Config { get; private set; } + public static bool OnlyEnableClientside { get; private set; } = false; public override void StartPre(ICoreAPI api) { @@ -55,6 +58,20 @@ public override void Start(ICoreAPI api) api.RegisterItemClass($"{ModId}:ItemStoneHammer", typeof(ItemStoneHammer)); api.RegisterBlockEntityClass($"{ModId}:StoneAnvil", typeof(BlockEntityStoneAnvil)); + if (api.Side.IsServer()) + { + Config = LocalConfig; + TreeAttributeSerializer.ToTreeAttributes(LocalConfig,api.World.Config.GetOrAddTreeAttribute("SmithingPlus")); + } + else + { + Config = new(); + var tree = api.World.Config.GetTreeAttribute("SmithingPlus"); + if(tree is not null) + { + TreeAttributeSerializer.FromTreeAttributes(Config,tree); + } + } Patch(); } @@ -66,7 +83,7 @@ public override void StartServerSide(ICoreServerAPI api) private static void AddEntityBehaviors(Entity entity) { - if (!Config.ArrowsDropBits || entity is not EntityProjectile projectile) return; + if (!LocalConfig.ArrowsDropBits || entity is not EntityProjectile projectile) return; if (!RecyclableArrowBehavior.IsRecyclableArrow(projectile)) return; Logger.VerboseDebug("Adding RecyclableArrowBehavior to {0}", entity.Code); entity.AddBehavior(new RecyclableArrowBehavior(entity)); @@ -75,16 +92,22 @@ private static void AddEntityBehaviors(Entity entity) public override void AssetsFinalize(ICoreAPI api) { base.AssetsFinalize(api); - foreach (var collObj in api.World.Collectibles.Where(c => c?.Code != null)) + foreach (CollectibleObject collObj in api.World.Collectibles.Where(c => c?.Code != null)) { - collObj.AddBehaviorIf( - api.Side == EnumAppSide.Client && - Config.ShowWorkableTemperature && - collObj.GetCollectibleInterface() is not null); - collObj.AddBehaviorIf( - api.Side == EnumAppSide.Client && - Config.ShowWorkableTemperature && - collObj.HasBehavior()); + if (api.Side.IsClient()) + { + collObj.AddBehaviorIf( + CConfig.ShowWorkableTemperature && + collObj.GetCollectibleInterface() is not null); + + collObj.AddBehaviorIf( + CConfig.ShowWorkableTemperature && + collObj.HasBehavior()); + + continue; // These only apply to the client + } + // The rest apply to the server (which automatically adds them for the client as well) + collObj.AddBehaviorIf(Config.RecoverBitsOnSplit && collObj is ItemChisel); collObj.AddBehaviorIf(Config.RecoverBitsOnSplit && @@ -113,7 +136,7 @@ public override void AssetsFinalize(ICoreAPI api) // { "workableRecipe": true } // A better solution would be // to define the recipe with code instead of cloning an ingot recipe defined in the assets - if (api.Side.IsClient()) continue; + var ingotCode = new AssetLocation("game:ingot-copper"); var ingotRecipe = api.ModLoader.GetModSystem().SmithingRecipes .FirstOrDefault(r => @@ -154,23 +177,25 @@ public override void AssetsFinalize(ICoreAPI api) private static void Patch() { if (HarmonyInstance != null) return; + HarmonyInstance = new Harmony(ModId); Logger.VerboseDebug("Patching..."); AlwaysPatchCategory.PatchIfEnabled(true); ToolRecoveryCategory.PatchIfEnabled(Config.EnableToolRecovery); SmithingRecipeAttributesPatch.PatchIfEnabled( Config.SmithWithBits || Config.BitsTopUp || Config.EnableToolRecovery, HarmonyInstance); - ClientTweaksCategories.RememberHammerToolMode.PatchIfEnabled(Config.RememberHammerToolMode); - ClientTweaksCategories.AnvilShowRecipeVoxels.PatchIfEnabled(Config.AnvilShowRecipeVoxels); - ClientTweaksCategories.ShowWorkablePatches.PatchIfEnabled(Config.ShowWorkableTemperature); - ClientTweaksCategories.HandbookExtraInfo.PatchIfEnabled(Config.HandbookExtraInfo); + + ClientTweaksCategories.RememberHammerToolMode.PatchIfEnabled(CConfig.RememberHammerToolMode); + ClientTweaksCategories.AnvilShowRecipeVoxels.PatchIfEnabled(CConfig.AnvilShowRecipeVoxels); + ClientTweaksCategories.ShowWorkablePatches.PatchIfEnabled(CConfig.ShowWorkableTemperature); + ClientTweaksCategories.HandbookExtraInfo.PatchIfEnabled(CConfig.HandbookExtraInfo); + BitsRecoveryCategory.PatchIfEnabled(Config.RecoverBitsOnSplit); HelveHammerBitsRecoveryCategory.PatchIfEnabled(Config.HelveHammerBitsRecovery); CastingTweaksCategory.PatchIfEnabled(Config.MetalCastingTweaks); DynamicMoldsCategory.PatchIfEnabled(Config.DynamicMoldUnits); BitSmithingCategory.PatchIfEnabled(Config.SmithWithBits || Config.BitsTopUp); HammerTweaksCategory.PatchIfEnabled(Config.HammerTweaks); - //StoneSmithingCategory.PatchIfEnabled(true); } private static void Unpatch() @@ -187,4 +212,4 @@ public override void Dispose() Api = null; base.Dispose(); } -} \ No newline at end of file +} diff --git a/SmithingPlus/ToolRecovery/CollectibleBehaviorBrokenToolHead.cs b/SmithingPlus/ToolRecovery/CollectibleBehaviorBrokenToolHead.cs index 433eadf..639f53a 100644 --- a/SmithingPlus/ToolRecovery/CollectibleBehaviorBrokenToolHead.cs +++ b/SmithingPlus/ToolRecovery/CollectibleBehaviorBrokenToolHead.cs @@ -48,7 +48,7 @@ public override void GetHeldItemInfo(ItemSlot inSlot, StringBuilder dsc, IWorldA if (world.Api is not ICoreClientAPI) return; var brokenCount = inSlot.Itemstack.GetBrokenCount(); if (brokenCount <= 0) return; - if (Core.Config.ShowBrokenCount) dsc.AppendLine(Lang.Get($"{LangKey} {{0}} times", brokenCount)); + if (Core.CConfig.ShowBrokenCount) dsc.AppendLine(Lang.Get($"{LangKey} {{0}} times", brokenCount)); if (Core.Config.DontRepairBrokenToolHeads) dsc.AppendLine(Lang.Get($"{Core.ModId}:itemdesc-needschiseling")); } diff --git a/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs b/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs index 6bb4ee7..bfdc2e9 100644 --- a/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs +++ b/SmithingPlus/ToolRecovery/CollectibleBehaviorRepairableTool.cs @@ -34,8 +34,8 @@ public override void GetHeldItemInfo(ItemSlot? inSlot, StringBuilder dsc, IWorld return; var brokenCount = inSlot.Itemstack.GetBrokenCount(); if (brokenCount <= 0) return; - if (Core.Config.ShowRepairedCount) dsc.AppendLine(Lang.Get($"{LangKey} {{0}} times", brokenCount)); - if (Core.Config.ShowRepairSmithName && inSlot.Itemstack.GetRepairSmith() is { } repairSmith) + if (Core.CConfig.ShowRepairedCount) dsc.AppendLine(Lang.Get($"{LangKey} {{0}} times", brokenCount)); + if (Core.CConfig.ShowRepairSmithName && inSlot.Itemstack.GetRepairSmith() is { } repairSmith) dsc.AppendLine(Lang.Get("Last repaired by {0}", repairSmith)); } } \ No newline at end of file diff --git a/SmithingPlus/Util/TreeAttributeSerialize.cs b/SmithingPlus/Util/TreeAttributeSerialize.cs new file mode 100644 index 0000000..ea5d266 --- /dev/null +++ b/SmithingPlus/Util/TreeAttributeSerialize.cs @@ -0,0 +1,48 @@ + + +using System.Reflection; +using Vintagestory.API.Datastructures; + +namespace SmithingPlus.Util; + +public class TreeAttributeSerializer +{ + public static void ToTreeAttributes(T obj, ITreeAttribute tree) + { + foreach (PropertyInfo fi in typeof(T).GetProperties()) + { + var type = fi.PropertyType; + var name = fi.Name; + if (type == typeof(bool)) { + tree.SetBool(name,(bool)fi.GetValue(obj)); + } else if (type==typeof(int)) { + tree.SetInt(name,(int)fi.GetValue(obj)); + } else if (type==typeof(float)) { + tree.SetFloat(name,(float)fi.GetValue(obj)); + } else if (type==typeof(string)) { + tree.SetString(name,(string)fi.GetValue(obj)); + } + } + } + + public static void FromTreeAttributes(T obj, ITreeAttribute tree) + { + foreach (PropertyInfo fi in typeof(T).GetProperties()) + { + var type = fi.PropertyType; + var name = fi.Name; + if (type == typeof(bool)) { + bool? v = tree.TryGetBool(name); + if (v.HasValue) fi.SetValue(obj,v.Value); + } else if (type==typeof(int)) { + int? v = tree.TryGetInt(name); + if (v.HasValue) fi.SetValue(obj,v.Value); + } else if (type==typeof(float)) { + float? v = tree.TryGetFloat(name); + if (v.HasValue) fi.SetValue(obj,v.Value); + } else if (type==typeof(string)) { + if(tree.HasAttribute(name)) fi.SetValue(obj,tree.GetString(name)); + } + } + } +}