From 600012727a20c9c30ac46d067fca875e7be5da4f Mon Sep 17 00:00:00 2001 From: JackTheFragger Date: Mon, 27 Mar 2023 22:43:07 +0200 Subject: [PATCH 1/3] - local Hotkey settings will be used, if serverSyncHotkeys is disabled - serverSyncHotkeys will only read from local config - refactoring for better readibility - addex xml comments for syncsServerConfig and serverSyncHotkeys for better understanding in code --- ValheimPlus/Configurations/BaseConfig.cs | 165 ++++++++++++++---- ValheimPlus/Configurations/Configuration.cs | 1 - .../Configurations/ConfigurationExtra.cs | 39 +++-- .../Sections/ServerConfiguration.cs | 16 +- ValheimPlus/RPC/VPlusConfigSync.cs | 58 +++--- ValheimPlus/UI/VPlusSettings.cs | 6 +- 6 files changed, 205 insertions(+), 80 deletions(-) diff --git a/ValheimPlus/Configurations/BaseConfig.cs b/ValheimPlus/Configurations/BaseConfig.cs index 490dd21d..20cf2aab 100644 --- a/ValheimPlus/Configurations/BaseConfig.cs +++ b/ValheimPlus/Configurations/BaseConfig.cs @@ -1,15 +1,22 @@ using IniParser.Model; +using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using UnityEngine; +using ValheimPlus.GameClasses; +using ValheimPlus.RPC; +using YamlDotNet.Core.Tokens; +using static CharacterDrop; namespace ValheimPlus.Configurations { public interface IConfig { - void LoadIniData(KeyDataCollection data); + void LoadIniData(KeyDataCollection data, string section); } - public abstract class BaseConfig : IConfig where T : IConfig, new() + public abstract class BaseConfig : IConfig where T : class, IConfig, new() { public string ServerSerializeSection() @@ -24,9 +31,10 @@ public string ServerSerializeSection() } return r; } - - public bool IsEnabled = false; - public virtual bool NeedsServerSync { get; set;} = false; + [LoadingOption(LoadingMode.Never)] + public bool IsEnabled { get; private set; } = false; + [LoadingOption(LoadingMode.Never)] + public virtual bool NeedsServerSync { get; set; } = false; public static IniData iniUpdated = null; @@ -34,72 +42,155 @@ public static T LoadIni(IniData data, string section) { var n = new T(); - + Debug.Log($"Loading config section {section}"); if (data[section] == null || data[section]["enabled"] == null || !data[section].GetBool("enabled")) { Debug.Log(" Section not enabled"); return n; } - - n.LoadIniData(data[section]); + var keyData = data[section]; + n.LoadIniData(keyData, section); return n; } + private static Dictionary _getValues = new Dictionary() + { + {typeof(float), GetFloatValue }, + {typeof(int), GetIntValue }, + {typeof(KeyCode), GetKeyCodeValue}, + {typeof(bool), GetBoolValue } + }; - public void LoadIniData(KeyDataCollection data) + public void LoadIniData(KeyDataCollection data, string section) { IsEnabled = true; - - foreach (var prop in typeof(T).GetProperties()) + var thisConfiguration = GetCurrentConfiguration(section); + if (thisConfiguration == null) { - var keyName = prop.Name; - if (new[] { "NeedsServerSync", "IsEnabled" }.Contains(keyName)) continue; - // Set first char of keyName to lowercase - if (keyName != string.Empty && char.IsUpper(keyName[0])) - { - keyName = char.ToLower(keyName[0]) + keyName.Substring(1); - } + Debug.Log("Configuration not set."); + thisConfiguration = this as T; + if (thisConfiguration == null) Debug.Log("Error on setting Configuration"); + } - if (!data.ContainsKey(keyName)) { - Debug.Log($" Key {keyName} not defined, using default value"); - continue; - } + foreach (var property in typeof(T).GetProperties()) + { - Debug.Log($" Loading key {keyName}"); - var existingValue = prop.GetValue(this, null); - if (prop.PropertyType == typeof(float)) + if (IgnoreLoading(property)) { - prop.SetValue(this, data.GetFloat(keyName, (float)existingValue), null); continue; } - - if (prop.PropertyType == typeof(int)) + var currentValue = property.GetValue(thisConfiguration); + if (IgnoreKeyCode(property)) { - prop.SetValue(this, data.GetInt(keyName, (int)existingValue), null); + property.SetValue(this, currentValue, null); continue; } - if (prop.PropertyType == typeof(bool)) + var keyName = GetKeyNameFromProperty(property); + + if (!data.ContainsKey(keyName)) { - prop.SetValue(this, data.GetBool(keyName), null); + Debug.Log($" Key {keyName} not defined, using default value"); continue; } - if (prop.PropertyType == typeof(KeyCode) && !RPC.VPlusConfigSync.isConnecting) + Debug.Log($"{property.Name} [{keyName}] = {currentValue} ({property.PropertyType})"); + + if (_getValues.ContainsKey(property.PropertyType)) { - prop.SetValue(this, data.GetKeyCode(keyName, (KeyCode)existingValue), null); - continue; + var getValue = _getValues[property.PropertyType]; + var value = getValue(data, currentValue, keyName); + Debug.Log($"{keyName} = {currentValue} => {value}"); + property.SetValue(this, value, null); } + else Debug.LogWarning($" Could not load data of type {property.PropertyType} for key {keyName}"); + } + } - Debug.LogWarning($" Could not load data of type {prop.PropertyType} for key {keyName}"); + delegate object DGetDataValue(KeyDataCollection data, object currentValue, string keyName); + + private static object GetFloatValue(KeyDataCollection data, object currentValue, string keyName) + { + return data.GetFloat(keyName, (float)currentValue); + } + private static object GetBoolValue(KeyDataCollection data, object currentValue, string keyName) + { + return data.GetBool(keyName); + } + private static object GetIntValue(KeyDataCollection data, object currentValue, string keyName) + { + return data.GetInt(keyName, (int)currentValue); + } + private static object GetKeyCodeValue(KeyDataCollection data, object currentValue, string keyName) + { + return data.GetKeyCode(keyName, (KeyCode)currentValue); + } + + private string GetKeyNameFromProperty(PropertyInfo property) + { + var keyName = property.Name; + + // Set first char of keyName to lowercase + if (keyName != string.Empty && char.IsUpper(keyName[0])) + { + keyName = char.ToLower(keyName[0]) + keyName.Substring(1); } + return keyName; + } + private bool IgnoreLoading(PropertyInfo property) + { + var loadingOption = property.GetCustomAttribute(); + var loadingMode = loadingOption?.LoadingMode ?? LoadingMode.Always; + + return (VPlusConfigSync.SyncRemote && loadingMode == LoadingMode.LocalOnly + || loadingMode == LoadingMode.Never); + } + private bool IgnoreKeyCode(PropertyInfo property) + { + return property.PropertyType == typeof(KeyCode) && VPlusConfigSync.SyncRemote && !ConfigurationExtra.SyncHotkeys; } + private static object GetCurrentConfiguration(string section) + { + if (Configuration.Current == null) return null; + Debug.Log($"Reading Config '{section}'"); + var properties = Configuration.Current.GetType().GetProperties(); + PropertyInfo property = properties.SingleOrDefault(p => p.Name.Equals(section, System.StringComparison.CurrentCultureIgnoreCase)); + if (property == null) + { + Debug.LogWarning($"Property '{section}' not found in Configuration"); + return null; + } + var thisConfiguration = property.GetValue(Configuration.Current) as T; + return thisConfiguration; + } + } + + public abstract class ServerSyncConfig : BaseConfig where T : class, IConfig, new() + { + [LoadingOption(LoadingMode.Never)] + public override bool NeedsServerSync { get; set; } = true; } - public abstract class ServerSyncConfig: BaseConfig where T : IConfig, new() { - public override bool NeedsServerSync { get; set;} = true; + public class LoadingOption : Attribute + { + public LoadingMode LoadingMode { get; } + public LoadingOption(LoadingMode loadingMode) + { + LoadingMode = loadingMode; + } + } + /// + /// Defines, when a property is loaded + /// + public enum LoadingMode + { + Always = 0, + RemoteOnly = 1, + LocalOnly = 2, + Never = 3 } } + diff --git a/ValheimPlus/Configurations/Configuration.cs b/ValheimPlus/Configurations/Configuration.cs index 115f6e94..6882f960 100644 --- a/ValheimPlus/Configurations/Configuration.cs +++ b/ValheimPlus/Configurations/Configuration.cs @@ -5,7 +5,6 @@ namespace ValheimPlus.Configurations public class Configuration { public static Configuration Current { get; set; } - public static Configuration Settings { get; set; } public AdvancedBuildingModeConfiguration AdvancedBuildingMode { get; set; } public AdvancedEditingModeConfiguration AdvancedEditingMode { get; set; } public BedConfiguration Bed { get; set; } diff --git a/ValheimPlus/Configurations/ConfigurationExtra.cs b/ValheimPlus/Configurations/ConfigurationExtra.cs index 4ae721c2..92d643d4 100644 --- a/ValheimPlus/Configurations/ConfigurationExtra.cs +++ b/ValheimPlus/Configurations/ConfigurationExtra.cs @@ -9,6 +9,8 @@ using System.Reflection; using UnityEngine; using ValheimPlus.Utility; +using ValheimPlus.Configurations.Sections; +using ValheimPlus.RPC; namespace ValheimPlus.Configurations { @@ -21,7 +23,7 @@ public static string GetServerHashFor(Configuration config) { var keyName = prop.Name; var method = prop.PropertyType.GetMethod("ServerSerializeSection", BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance); - + if (method != null) { var instance = prop.GetValue(config, null); @@ -50,7 +52,7 @@ public static bool LoadSettings() // get the current versions ini data compareIni = ValheimPlusPlugin.getCurrentWebIniFile(); } - catch (Exception e) { } + catch (Exception) { } if (compareIni != null) { @@ -75,7 +77,7 @@ public static bool LoadSettings() try { string defaultIni = ValheimPlusPlugin.getCurrentWebIniFile(); - if(defaultIni != null) + if (defaultIni != null) { System.IO.File.WriteAllText(ConfigIniPath, defaultIni); Debug.Log("Default Configuration downloaded. Loading downloaded default settings."); @@ -83,7 +85,7 @@ public static bool LoadSettings() status = true; } } - catch (Exception e) { } + catch (Exception) { } return status; } @@ -96,12 +98,13 @@ public static bool LoadSettings() return true; } + static public bool SyncHotkeys { get; private set; } = false; + //loading local configuration public static Configuration LoadFromIni(string filename) { FileIniDataParser parser = new FileIniDataParser(); IniData configdata = parser.ReadFile(filename); - Configuration conf = new Configuration(); foreach (var prop in typeof(Configuration).GetProperties()) { @@ -117,13 +120,26 @@ public static Configuration LoadFromIni(string filename) return conf; } - + /// + /// Loading remote configuration + /// + /// remote configuration stream + /// new configuration public static Configuration LoadFromIni(Stream iniStream) { using (StreamReader iniReader = new StreamReader(iniStream)) { FileIniDataParser parser = new FileIniDataParser(); IniData configdata = parser.ReadData(iniReader); + var serverSection = configdata[nameof(Configuration.Server)]; + var serverSyncsConfig = serverSection.GetBool(nameof(ServerConfiguration.serverSyncsConfig)); + Debug.Log($"ServerSyncsConfig = {serverSyncsConfig}"); + + if (!serverSyncsConfig) return Configuration.Current; + + var serverSyncsHotkeys = Configuration.Current.Server.serverSyncHotkeys; + Debug.Log($"ServerSyncsHotkeys = {serverSyncsConfig}"); + SyncHotkeys = serverSyncsHotkeys; Configuration conf = new Configuration(); foreach (var prop in typeof(Configuration).GetProperties()) @@ -134,7 +150,7 @@ public static Configuration LoadFromIni(Stream iniStream) if (method != null) { - object result = method.Invoke(null, new object[] {configdata, keyName}); + object result = method.Invoke(null, new object[] { configdata, keyName }); prop.SetValue(conf, result, null); } } @@ -147,7 +163,8 @@ public static class IniDataExtensions { public static float GetFloat(this KeyDataCollection data, string key, float defaultVal) { - if (float.TryParse(data[key], NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var result)) { + if (float.TryParse(data[key], NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var result)) + { return result; } @@ -163,7 +180,8 @@ public static bool GetBool(this KeyDataCollection data, string key) public static int GetInt(this KeyDataCollection data, string key, int defaultVal) { - if (int.TryParse(data[key], NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var result)) { + if (int.TryParse(data[key], NumberStyles.Any, CultureInfo.InvariantCulture.NumberFormat, out var result)) + { return result; } @@ -173,7 +191,8 @@ public static int GetInt(this KeyDataCollection data, string key, int defaultVal public static KeyCode GetKeyCode(this KeyDataCollection data, string key, KeyCode defaultVal) { - if (Enum.TryParse(data[key].Trim(), out var result)) { + if (Enum.TryParse(data[key].Trim(), out var result)) + { return result; } diff --git a/ValheimPlus/Configurations/Sections/ServerConfiguration.cs b/ValheimPlus/Configurations/Sections/ServerConfiguration.cs index e5ec7570..44e50a01 100644 --- a/ValheimPlus/Configurations/Sections/ServerConfiguration.cs +++ b/ValheimPlus/Configurations/Sections/ServerConfiguration.cs @@ -1,11 +1,25 @@ -namespace ValheimPlus.Configurations.Sections +using System.Security.Policy; +using UnityEngine; + +namespace ValheimPlus.Configurations.Sections { public class ServerConfiguration : BaseConfig { public int maxPlayers { get; internal set; } = 10; public bool disableServerPassword { get; internal set; } = false; public bool enforceMod { get; internal set; } = true; + /// + /// Changes whether the server will force it's config on clients that connect. Only affects servers. + /// WE HEAVILY RECOMMEND TO NEVER DISABLE THIS! + /// + [LoadingOption(LoadingMode.RemoteOnly)] public bool serverSyncsConfig { get; internal set; } = true; + /// + /// If false allows you to keep your own defined hotkeys instead of synchronising the ones declared for the server. + /// Sections need to be enabled in your local configuration to load hotkeys. + /// This is a client side setting and not affected by server settings. + /// + [LoadingOption(LoadingMode.LocalOnly)] public bool serverSyncHotkeys { get; internal set; } = true; } diff --git a/ValheimPlus/RPC/VPlusConfigSync.cs b/ValheimPlus/RPC/VPlusConfigSync.cs index 3e5a9da2..05dae2de 100644 --- a/ValheimPlus/RPC/VPlusConfigSync.cs +++ b/ValheimPlus/RPC/VPlusConfigSync.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using BepInEx; using ValheimPlus.Configurations; @@ -7,8 +8,7 @@ namespace ValheimPlus.RPC { public class VPlusConfigSync { - - static public bool isConnecting = false; + public static bool SyncRemote { get; private set; } = false; public static void RPC_VPlusConfigSync(long sender, ZPackage configPkg) { if (ZNet.m_isServer) //Server @@ -49,8 +49,8 @@ public static void RPC_VPlusConfigSync(long sender, ZPackage configPkg) } else //Client { - if (configPkg != null && - configPkg.Size() > 0 && + if (configPkg != null && + configPkg.Size() > 0 && sender == ZRoutedRpc.instance.GetServerPeerID()) //Validate the message is from the server and not another client. { int numLines = configPkg.ReadInt(); @@ -61,41 +61,41 @@ public static void RPC_VPlusConfigSync(long sender, ZPackage configPkg) return; } - using (MemoryStream memStream = new MemoryStream()) + try { - using (StreamWriter tmpWriter = new StreamWriter(memStream)) + SyncRemote = true; + using (MemoryStream memStream = new MemoryStream()) { - for (int i = 0; i < numLines; i++) + using (StreamWriter tmpWriter = new StreamWriter(memStream)) { - string line = configPkg.ReadString(); + for (int i = 0; i < numLines; i++) + { + var line = configPkg.ReadString(); + tmpWriter.WriteLine(line); + } - tmpWriter.WriteLine(line); - } + tmpWriter.Flush(); //Flush to memStream + memStream.Position = 0; //Rewind stream - tmpWriter.Flush(); //Flush to memStream - memStream.Position = 0; //Rewind stream + ValheimPlusPlugin.harmony.UnpatchSelf(); + Configuration.Current = ConfigurationExtra.LoadFromIni(memStream); - ValheimPlusPlugin.harmony.UnpatchSelf(); + ValheimPlusPlugin.harmony.PatchAll(); - // Sync HotKeys when connecting ? - if(Configuration.Current.Server.IsEnabled && !Configuration.Current.Server.serverSyncHotkeys) - { - isConnecting = true; - Configuration.Current = ConfigurationExtra.LoadFromIni(memStream); - isConnecting = false; + ZLog.Log("Successfully synced VPlus configuration from server."); } - else - { - Configuration.Current = ConfigurationExtra.LoadFromIni(memStream); - } - - ValheimPlusPlugin.harmony.PatchAll(); - - ZLog.Log("Successfully synced VPlus configuration from server."); } } + catch (Exception) + { + //ignore + } + finally + { + SyncRemote = false; + } } } } } -} \ No newline at end of file +} diff --git a/ValheimPlus/UI/VPlusSettings.cs b/ValheimPlus/UI/VPlusSettings.cs index 91a5aab3..df8a6954 100644 --- a/ValheimPlus/UI/VPlusSettings.cs +++ b/ValheimPlus/UI/VPlusSettings.cs @@ -11,6 +11,7 @@ using UnityEngine.SceneManagement; using UnityEngine.UI; using ValheimPlus.Configurations; +using ValheimPlus.RPC; using ValheimPlus.Utility; namespace ValheimPlus.UI @@ -113,6 +114,7 @@ public static void Apply() { if (File.Exists(ConfigurationExtra.ConfigIniPath)) { + Debug.Log("Applying Values"); FileIniDataParser parser = new FileIniDataParser(); IniData configdata = parser.ReadFile(ConfigurationExtra.ConfigIniPath); foreach (KeyValuePair> settingSection in settingFamillySettings) @@ -163,7 +165,7 @@ public static void Apply() continue; } - if (prop.PropertyType == typeof(KeyCode) && !RPC.VPlusConfigSync.isConnecting) + if (prop.PropertyType == typeof(KeyCode) && (!VPlusConfigSync.SyncRemote || Configuration.Current.Server.serverSyncHotkeys)) { prop.SetValue(settingFamilyProp, Enum.Parse(typeof(KeyCode), newVal), null); configdata[settingSection.Key][settingEntry.name.Replace("(Clone)", "")] = newVal; @@ -181,7 +183,7 @@ public static void Apply() string newVal = keycodeNames[inputDropdown.value]; if (newVal == ((KeyCode)prop.GetValue(settingFamilyProp, null)).ToString()) continue; - if (prop.PropertyType == typeof(KeyCode) && !RPC.VPlusConfigSync.isConnecting) + if (prop.PropertyType == typeof(KeyCode) && (!VPlusConfigSync.SyncRemote || Configuration.Current.Server.serverSyncHotkeys)) { prop.SetValue(settingFamilyProp, Enum.Parse(typeof(KeyCode), newVal), null); configdata[settingSection.Key][settingEntry.name.Replace("(Clone)", "")] = newVal; From 782a3fc463271001a98fee7e2779bb7f3b2b9d91 Mon Sep 17 00:00:00 2001 From: JackTheFragger Date: Tue, 28 Mar 2023 20:48:37 +0200 Subject: [PATCH 2/3] - try-catch-finally-block in RPC_VPlusConfigSync will be thrown - all properties with option "LoadMode.LocalOnly" will be using local values when syncing --- ValheimPlus/Configurations/BaseConfig.cs | 12 +++++++----- ValheimPlus/RPC/VPlusConfigSync.cs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ValheimPlus/Configurations/BaseConfig.cs b/ValheimPlus/Configurations/BaseConfig.cs index 20cf2aab..ad2018a3 100644 --- a/ValheimPlus/Configurations/BaseConfig.cs +++ b/ValheimPlus/Configurations/BaseConfig.cs @@ -82,7 +82,7 @@ public void LoadIniData(KeyDataCollection data, string section) continue; } var currentValue = property.GetValue(thisConfiguration); - if (IgnoreKeyCode(property)) + if (LoadLocalOnly(property)) { property.SetValue(this, currentValue, null); continue; @@ -144,12 +144,14 @@ private bool IgnoreLoading(PropertyInfo property) var loadingOption = property.GetCustomAttribute(); var loadingMode = loadingOption?.LoadingMode ?? LoadingMode.Always; - return (VPlusConfigSync.SyncRemote && loadingMode == LoadingMode.LocalOnly - || loadingMode == LoadingMode.Never); + return (loadingMode == LoadingMode.Never); } - private bool IgnoreKeyCode(PropertyInfo property) + private bool LoadLocalOnly(PropertyInfo property) { - return property.PropertyType == typeof(KeyCode) && VPlusConfigSync.SyncRemote && !ConfigurationExtra.SyncHotkeys; + var loadingOption = property.GetCustomAttribute(); + var loadingMode = loadingOption?.LoadingMode ?? LoadingMode.Always; + + return VPlusConfigSync.SyncRemote && (property.PropertyType == typeof(KeyCode) && !ConfigurationExtra.SyncHotkeys || loadingMode == LoadingMode.LocalOnly); } private static object GetCurrentConfiguration(string section) diff --git a/ValheimPlus/RPC/VPlusConfigSync.cs b/ValheimPlus/RPC/VPlusConfigSync.cs index 05dae2de..bb502146 100644 --- a/ValheimPlus/RPC/VPlusConfigSync.cs +++ b/ValheimPlus/RPC/VPlusConfigSync.cs @@ -88,7 +88,7 @@ public static void RPC_VPlusConfigSync(long sender, ZPackage configPkg) } catch (Exception) { - //ignore + throw; } finally { From 918c8d8d061a57a6f965f475115c5bf6607bd3d5 Mon Sep 17 00:00:00 2001 From: JackTheFragger <75476778+JackTheFragger@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:59:53 +0200 Subject: [PATCH 3/3] Update BaseConfig.cs Formatting in Line 61 corrected --- ValheimPlus/Configurations/BaseConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ValheimPlus/Configurations/BaseConfig.cs b/ValheimPlus/Configurations/BaseConfig.cs index ad2018a3..ac1f2a64 100644 --- a/ValheimPlus/Configurations/BaseConfig.cs +++ b/ValheimPlus/Configurations/BaseConfig.cs @@ -58,7 +58,7 @@ public static T LoadIni(IniData data, string section) { {typeof(float), GetFloatValue }, {typeof(int), GetIntValue }, - {typeof(KeyCode), GetKeyCodeValue}, + {typeof(KeyCode), GetKeyCodeValue }, {typeof(bool), GetBoolValue } };