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);
        }
Exemple #2
0
        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);
                    }
                }
            }
        }