internal void ServerAICSyncOneToAll(AutoConfigBinding targetConfig, object newValue) { foreach (var user in NetworkUser.readOnlyInstancesList) { if (user.hasAuthority || (user.connectionToClient != null && Util.ConnectionIsLocal(user.connectionToClient))) { continue; } TargetAICSyncOneToAll(user.connectionToClient, targetConfig.modName, targetConfig.configEntry.Definition.Section, targetConfig.configEntry.Definition.Key, TomlTypeConverter.ConvertToString(newValue, targetConfig.propType)); } }
internal static void Evt_USMSceneLoaded(Scene scene, LoadSceneMode mode) { AutoConfigBinding.CleanupDirty(false); }
/// <summary>Binds a property to a BepInEx config file, using reflection and attributes to automatically generate much of the necessary information.</summary> public void Bind(PropertyInfo prop, ConfigFile cfl, string modName, string categoryName, AutoConfigAttribute attrib, AutoConfigUpdateActionsAttribute eiattr = null, BindSubDictInfo?subDict = null) { string errorStr = "AutoItemCfg.Bind on property " + prop.Name + " in category " + categoryName + " failed: "; if (!subDict.HasValue) { if (this.bindings.Exists(x => x.boundProperty == prop)) { TILER2Plugin._logger.LogError(errorStr + "this property has already been bound."); return; } if ((attrib.flags & AutoConfigFlags.BindDict) == AutoConfigFlags.BindDict) { if (!prop.PropertyType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary <,>))) { TILER2Plugin._logger.LogError(errorStr + "BindDict flag cannot be used on property types which don't implement IDictionary."); return; } var kTyp = prop.PropertyType.GetGenericArguments()[1]; if (attrib.avb != null && attrib.avbType != kTyp) { TILER2Plugin._logger.LogError(errorStr + "dict value and AcceptableValue types must match (received " + kTyp.Name + " and " + attrib.avbType.Name + ")."); return; } if (!TomlTypeConverter.CanConvert(kTyp)) { TILER2Plugin._logger.LogError(errorStr + "dict value type cannot be converted by BepInEx.Configuration.TomlTypeConverter (received " + kTyp.Name + ")."); return; } var idict = (System.Collections.IDictionary)prop.GetValue(this, null); int ind = 0; var dkeys = (from object k in idict.Keys select k).ToList(); foreach (object o in dkeys) { Bind(prop, cfl, modName, categoryName, attrib, eiattr, new BindSubDictInfo { key = o, val = idict[o], keyType = kTyp, index = ind }); ind++; } return; } } if (!subDict.HasValue) { if (attrib.avb != null && attrib.avbType != prop.PropertyType) { TILER2Plugin._logger.LogError(errorStr + "property and AcceptableValue types must match (received " + prop.PropertyType.Name + " and " + attrib.avbType.Name + ")."); return; } if (!TomlTypeConverter.CanConvert(prop.PropertyType)) { TILER2Plugin._logger.LogError(errorStr + "property type cannot be converted by BepInEx.Configuration.TomlTypeConverter (received " + prop.PropertyType.Name + ")."); return; } } object propObj = subDict.HasValue ? prop.GetValue(this) : this; var dict = subDict.HasValue ? (System.Collections.IDictionary)propObj : null; var propGetter = subDict.HasValue ? dict.GetType().GetProperty("Item").GetGetMethod(true) : (prop.GetGetMethod(true) ?? prop.DeclaringType.GetProperty(prop.Name)?.GetGetMethod(true)); var propSetter = subDict.HasValue ? dict.GetType().GetProperty("Item").GetSetMethod(true) : (prop.GetSetMethod(true) ?? prop.DeclaringType.GetProperty(prop.Name)?.GetSetMethod(true)); var propType = subDict.HasValue ? subDict.Value.keyType : prop.PropertyType; if (propGetter == null || propSetter == null) { TILER2Plugin._logger.LogError(errorStr + "property (or IDictionary Item property, if using BindDict flag) must have both a getter and a setter."); return; } string cfgName = attrib.name; if (cfgName != null) { cfgName = ReplaceTags(cfgName, prop, categoryName, subDict); } else { cfgName = char.ToUpperInvariant(prop.Name[0]) + prop.Name.Substring(1) + (subDict.HasValue ? ":" + subDict.Value.index : ""); } string cfgDesc = attrib.desc; if (cfgDesc != null) { cfgDesc = ReplaceTags(cfgDesc, prop, categoryName, subDict); } else { cfgDesc = "Automatically generated from a C# " + (subDict.HasValue ? "dictionary " : "") + "property."; } //Matches ConfigFile.Bind<T>(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription) var genm = typeof(ConfigFile).GetMethods().First( x => x.Name == nameof(ConfigFile.Bind) && x.GetParameters().Length == 3 && x.GetParameters()[0].ParameterType == typeof(ConfigDefinition) && x.GetParameters()[2].ParameterType == typeof(ConfigDescription) ).MakeGenericMethod(propType); var propValue = subDict.HasValue ? subDict.Value.val : prop.GetValue(this); bool allowMismatch = (attrib.flags & AutoConfigFlags.PreventNetMismatch) != AutoConfigFlags.PreventNetMismatch; bool deferForever = (attrib.flags & AutoConfigFlags.DeferForever) == AutoConfigFlags.DeferForever; bool deferRun = (attrib.flags & AutoConfigFlags.DeferUntilEndGame) == AutoConfigFlags.DeferUntilEndGame; bool deferStage = (attrib.flags & AutoConfigFlags.DeferUntilNextStage) == AutoConfigFlags.DeferUntilNextStage; bool allowCon = (attrib.flags & AutoConfigFlags.PreventConCmd) != AutoConfigFlags.PreventConCmd; if (deferForever && !allowMismatch) { cfgDesc += "\nWARNING: THIS SETTING CANNOT BE CHANGED WHILE THE GAME IS RUNNING, AND MUST BE SYNCED MANUALLY FOR MULTIPLAYER!"; } var cfe = (ConfigEntryBase)genm.Invoke(cfl, new[] { new ConfigDefinition(categoryName, cfgName), propValue, new ConfigDescription(cfgDesc, attrib.avb) }); observedFiles[cfl] = System.IO.File.GetLastWriteTime(cfl.ConfigFilePath); var newAIC = new AutoConfigBinding { boundProperty = prop, allowConCmd = allowCon && !deferForever && !deferRun, allowNetMismatch = allowMismatch, netMismatchCritical = !allowMismatch && deferForever, deferType = deferForever ? AutoConfigBinding.DeferType.NeverAutoUpdate : (deferRun ? AutoConfigBinding.DeferType.WaitForRunEnd : (deferStage ? AutoConfigBinding.DeferType.WaitForNextStage : AutoConfigBinding.DeferType.UpdateImmediately)), configEntry = cfe, modName = modName, owner = this, propGetter = propGetter, propSetter = propSetter, propType = propType, onDict = subDict.HasValue, boundKey = subDict.HasValue ? subDict.Value.key : null, updateEventAttribute = eiattr, cachedValue = propValue, target = propObj }; this.bindings.Add(newAIC); if (!deferForever) { var gtyp = typeof(ConfigEntry <>).MakeGenericType(propType); var evh = gtyp.GetEvent("SettingChanged"); evh.ReflAddEventHandler(cfe, (object obj, EventArgs evtArgs) => { newAIC.UpdateProperty(cfe.BoxedValue); }); } if ((attrib.flags & AutoConfigFlags.NoInitialRead) != AutoConfigFlags.NoInitialRead) { propSetter.Invoke(propObj, subDict.HasValue ? new[] { subDict.Value.key, cfe.BoxedValue } : new[] { cfe.BoxedValue }); newAIC.cachedValue = cfe.BoxedValue; } }
internal static void On_GNMDisconnect(On.RoR2.Networking.GameNetworkManager.orig_Disconnect orig, GameNetworkManager self) { orig(self); AutoConfigBinding.CleanupDirty(true); }