internal HashSet <string> LoadRequested(PluginScanner pluginScanner) { _moduleSet = new HashSet <string>(); void AddModuleToSet(IEnumerable <CustomAttributeArgument> arguments) { foreach (var arg in arguments) { foreach (var stringElement in (CustomAttributeArgument[])arg.Value) { _moduleSet.Add((string)stringElement.Value); } } } void CallWhenAssembliesAreScanned() { var moduleTypes = Assembly.GetExecutingAssembly().GetTypes().Where(APISubmoduleFilter).ToList(); foreach (var moduleType in moduleTypes) { R2API.Logger.LogInfo($"Enabling R2API Submodule: {moduleType.Name}"); } var faults = new Dictionary <Type, Exception>(); LoadedModules = new HashSet <string>(); moduleTypes .ForEachTry(t => InvokeStage(t, InitStage.SetHooks), faults); moduleTypes.Where(t => !faults.ContainsKey(t)) .ForEachTry(t => InvokeStage(t, InitStage.Load), faults); faults.Keys.ForEachTry(t => { _logger?.Log(LogLevel.Error, $"{t.Name} could not be initialized and has been disabled:\n\n{faults[t]}"); InvokeStage(t, InitStage.UnsetHooks); }); moduleTypes.Where(t => !faults.ContainsKey(t)) .ForEachTry(t => t.SetFieldValue("_loaded", true)); moduleTypes.Where(t => !faults.ContainsKey(t)) .ForEachTry(t => LoadedModules.Add(t.Name)); } var scanRequest = new PluginScanner.AttributeScanRequest(attributeTypeFullName: typeof(R2APISubmoduleDependency).FullName, attributeTargets: AttributeTargets.Assembly | AttributeTargets.Class, CallWhenAssembliesAreScanned, oneMatchPerAssembly: false, foundOnAssemblyAttributes: (assembly, arguments) => AddModuleToSet(arguments), foundOnAssemblyTypes: (type, arguments) => AddModuleToSet(arguments) ); pluginScanner.AddScanRequest(scanRequest); return(LoadedModules); }
internal void BuildModList(PluginScanner pluginScanner) { var modList = new HashSet <string>(); var scanForBepinExUnityPlugins = new PluginScanner.ClassScanRequest(typeof(BaseUnityPlugin).FullName, whenRequestIsDone: null, oneMatchPerAssembly: false, foundOnAssemblyTypes: (type, attributes) => { var haveNetworkCompatAttribute = attributes.FirstOrDefault(attribute => attribute.AttributeType.FullName == typeof(NetworkCompatibility).FullName) != null; var bepinPluginAttribute = attributes.FirstOrDefault(attribute => attribute.AttributeType.FullName == typeof(BepInPlugin).FullName); var(modGuid, modVersion) = PluginScanner.GetBepinPluginInfo(bepinPluginAttribute?.ConstructorArguments); var haveManualRegistrationAttribute = type.Module.Assembly.CustomAttributes?.FirstOrDefault(a => a.AttributeType.FullName == typeof(ManualNetworkRegistrationAttribute).FullName) != null; // By default, any plugins that don't have the NetworkCompatibility attribute and // don't have the ManualNetworkRegistration attribute are added to the networked mod list if (!haveNetworkCompatAttribute) { if (bepinPluginAttribute != null && !haveManualRegistrationAttribute) { modList.Add(modGuid + ModGuidAndModVersionSeparator + modVersion); } else { R2API.Logger.LogDebug($"Found {nameof(BaseUnityPlugin)} type but no {nameof(BepInPlugin)} attribute"); } } }); pluginScanner.AddScanRequest(scanForBepinExUnityPlugins); var scanRequestForNetworkCompatAttr = new PluginScanner.AttributeScanRequest(attributeTypeFullName: typeof(NetworkCompatibility).FullName, attributeTargets: AttributeTargets.Assembly | AttributeTargets.Class, CallWhenAssembliesAreScanned, oneMatchPerAssembly: true, foundOnAssemblyAttributes: (assembly, arguments) => { TryGetNetworkCompatibilityArguments(arguments, out var compatibilityLevel, out var versionStrictness); if (compatibilityLevel == CompatibilityLevel.EveryoneMustHaveMod) { modList.Add(versionStrictness == VersionStrictness.EveryoneNeedSameModVersion ? assembly.Name.FullName : assembly.Name.Name); } }, foundOnAssemblyTypes: (type, arguments) => { TryGetNetworkCompatibilityArguments(arguments, out var compatibilityLevel, out var versionStrictness); if (compatibilityLevel == CompatibilityLevel.EveryoneMustHaveMod) { var bepinPluginAttribute = type.CustomAttributes.FirstOrDefault(attr => attr.AttributeType.Resolve().IsSubtypeOf(typeof(BepInPlugin))); if (bepinPluginAttribute != null) { var(modGuid, modVersion) = PluginScanner.GetBepinPluginInfo(bepinPluginAttribute.ConstructorArguments); modList.Add(versionStrictness == VersionStrictness.EveryoneNeedSameModVersion ? modGuid + ModGuidAndModVersionSeparator + modVersion : modGuid); } else { throw new Exception($"Could not find corresponding {nameof(BepInPlugin)} Attribute of your plugin, " + $"make sure that the {nameof(NetworkCompatibility)} attribute is " + $"on the same class as the {nameof(BepInPlugin)} attribute. " + $"If you don't have a plugin that has a class heriting from {nameof(BaseUnityPlugin)}, " + $"put the {nameof(NetworkCompatibility)} attribute as an Assembly attribute instead"); } } }, attributeMustBeOnTypeFullName: typeof(BaseUnityPlugin).FullName); pluginScanner.AddScanRequest(scanRequestForNetworkCompatAttr); void CallWhenAssembliesAreScanned() { if (modList.Count != 0) { var sortedModList = modList.ToList(); sortedModList.Sort(); R2API.Logger.LogInfo("[NetworkCompatibility] Adding to the networkModList : "); foreach (var mod in sortedModList) { R2API.Logger.LogInfo(mod); NetworkModCompatibilityHelper.networkModList = NetworkModCompatibilityHelper.networkModList.Append(mod); } } } }