Search in sources :

Example 1 with PluginResource

use of org.spongepowered.plugin.PluginResource in project SpongeCommon by SpongePowered.

the class VanillaPlatformService method runScan.

@Override
public List<Map.Entry<String, Path>> runScan(final IEnvironment environment) {
    VanillaPlatformService.pluginPlatform.locatePluginResources();
    VanillaPlatformService.pluginPlatform.createPluginCandidates();
    final AccessWidenerTransformationService accessWidener = environment.getProperty(AccessWidenerTransformationService.INSTANCE.get()).orElse(null);
    final SuperclassChanger superclassChanger = environment.getProperty(SuperclassChanger.INSTANCE.get()).orElse(null);
    final ILaunchPluginService mixin = environment.findLaunchPlugin(MixinLaunchPluginLegacy.NAME).orElse(null);
    final List<Map.Entry<String, Path>> launchResources = new ArrayList<>();
    for (final Map.Entry<String, Set<PluginResource>> resourcesEntry : VanillaPlatformService.pluginPlatform.getResources().entrySet()) {
        final Set<PluginResource> resources = resourcesEntry.getValue();
        for (final PluginResource resource : resources) {
            // Handle Access Transformers
            if ((accessWidener != null || mixin != null || superclassChanger != null) && resource instanceof JVMPluginResource) {
                if (mixin != null) {
                    // Offer jar to the Mixin service
                    mixin.offerResource(((JVMPluginResource) resource).path(), ((JVMPluginResource) resource).path().getFileName().toString());
                }
                // Offer jar to the AW service
                ((JVMPluginResource) resource).manifest().ifPresent(manifest -> {
                    if (accessWidener != null) {
                        final String atFiles = manifest.getMainAttributes().getValue(Constants.ManifestAttributes.ACCESS_WIDENER);
                        if (atFiles != null) {
                            for (final String atFile : atFiles.split(",")) {
                                if (!atFile.endsWith(AccessWidenerTransformationService.ACCESS_WIDENER_EXTENSION)) {
                                    continue;
                                }
                                try {
                                    accessWidener.offerResource(((JVMPluginResource) resource).fileSystem().getPath(atFile).toUri().toURL(), atFile);
                                } catch (final MalformedURLException ex) {
                                    VanillaPlatformService.pluginPlatform.logger().warn("Failed to read declared access widener {}, from {}:", atFile, resource.locator());
                                }
                            }
                        }
                    }
                    if (mixin != null && manifest.getMainAttributes().getValue(org.spongepowered.asm.util.Constants.ManifestAttributes.MIXINCONFIGS) != null) {
                        if (!VanillaPlatformService.isSponge((JVMPluginResource) resource)) {
                            VanillaPlatformService.pluginPlatform.logger().warn("Plugin from {} uses Mixins to modify the Minecraft Server. If something breaks, remove it before reporting the " + "problem to Sponge!", ((JVMPluginResource) resource).path());
                        }
                    }
                    if (superclassChanger != null) {
                        final String superclassChangeFiles = manifest.getMainAttributes().getValue(Constants.ManifestAttributes.SUPERCLASS_CHANGE);
                        if (superclassChangeFiles != null) {
                            for (final String superclassChangeFile : superclassChangeFiles.split(",")) {
                                if (!superclassChangeFile.endsWith(SuperclassChanger.SUPER_CLASS_EXTENSION)) {
                                    continue;
                                }
                                try {
                                    superclassChanger.offerResource(((JVMPluginResource) resource).fileSystem().getPath(superclassChangeFile).toUri().toURL(), superclassChangeFile);
                                } catch (final MalformedURLException ex) {
                                    VanillaPlatformService.pluginPlatform.logger().warn("Failed to read declared superclass changer {}, from {}:", superclassChangeFile, resource.locator());
                                }
                            }
                        }
                    }
                });
                final Map.Entry<String, Path> entry = Maps.immutableEntry(((JVMPluginResource) resource).path().getFileName().toString(), ((JVMPluginResource) resource).path());
                launchResources.add(entry);
            }
        }
    }
    return launchResources;
}
Also used : Path(java.nio.file.Path) MalformedURLException(java.net.MalformedURLException) Set(java.util.Set) SuperclassChanger(org.spongepowered.transformers.modlauncher.SuperclassChanger) ArrayList(java.util.ArrayList) ILaunchPluginService(cpw.mods.modlauncher.serviceapi.ILaunchPluginService) JVMPluginResource(org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource) JVMPluginResource(org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource) PluginResource(org.spongepowered.plugin.PluginResource) AccessWidenerTransformationService(org.spongepowered.transformers.modlauncher.AccessWidenerTransformationService) Map(java.util.Map)

Example 2 with PluginResource

use of org.spongepowered.plugin.PluginResource in project SpongeCommon by SpongePowered.

the class DependencyResolver method resolveAndSortCandidates.

public static <T extends PluginResource> ResolutionResult<T> resolveAndSortCandidates(final Collection<PluginCandidate<T>> candidates, final Logger logger) {
    final Map<String, Node<T>> nodes = new HashMap<>();
    final ResolutionResult<T> resolutionResult = new ResolutionResult<>();
    for (final PluginCandidate<T> candidate : candidates) {
        final String id = candidate.metadata().id();
        // If we already have an entry, this is now a duplicate ID situation.
        if (nodes.containsKey(id)) {
            resolutionResult.duplicateIds().add(id);
        } else {
            nodes.put(id, new Node<>(candidate));
        }
    }
    for (final Map.Entry<String, Node<T>> entry : nodes.entrySet()) {
        // Attach deps, invalid deps will appear at this point.
        final Node<T> node = entry.getValue();
        for (final PluginDependency pd : node.candidate.metadata().dependencies()) {
            final boolean isOptional = pd.optional();
            final Node<T> dep = nodes.get(pd.id());
            if (dep == null) {
                if (isOptional) {
                    // just move on to the next dep
                    continue;
                }
                node.invalid = true;
                final String failure;
                if (pd.version() != null) {
                    failure = String.format("%s version %s", pd.id(), pd.version());
                } else {
                    failure = pd.id();
                }
                resolutionResult.missingDependencies().computeIfAbsent(entry.getValue().candidate, k -> new ArrayList<>()).add(failure);
                // no need to process this further
                node.checked = true;
                continue;
            }
            if (!DependencyResolver.checkVersion(pd.version(), dep.candidate.metadata().version())) {
                if (isOptional) {
                    // just move on to the next dep
                    continue;
                }
                resolutionResult.versionMismatch().computeIfAbsent(entry.getValue().candidate, k -> new ArrayList<>()).add(Tuple.of(pd.version().toString(), dep.candidate));
                node.invalid = true;
                // no need to process this further.
                node.checked = true;
            }
            if (pd.loadOrder() == PluginDependency.LoadOrder.BEFORE) {
                if (!pd.optional()) {
                    // Because of what we're about to do, we need to just make sure that
                    // if the "before" dep fails within here, then we still throw it out.
                    // Note, we can only do this for sorting, once sorted, if this loads
                    // but the before dep doesn't, well, it's on the plugin to solve, not
                    // us.
                    node.beforeRequiredDependency.add(node);
                }
                // don't bomb out the dep if this doesn't load - so set it to be optional.
                // however, we otherwise treat it as an AFTER dep on the target dep
                DependencyResolver.setDependency(dep, node, true);
            } else {
                DependencyResolver.setDependency(node, dep, pd.optional());
            }
        }
    }
    // Check for invalid deps
    DependencyResolver.checkCyclic(nodes.values(), resolutionResult);
    for (final Node<T> node : nodes.values()) {
        DependencyResolver.calculateSecondaryFailures(node, resolutionResult);
    }
    // Now to sort them.
    final List<Node<T>> original = nodes.values().stream().filter(x -> !x.invalid).collect(Collectors.toCollection(ArrayList::new));
    final List<Node<T>> toLoad = new ArrayList<>(original);
    final LinkedHashSet<Node<T>> sorted = new LinkedHashSet<>();
    toLoad.stream().filter(x -> x.dependencies.isEmpty() && x.optionalDependencies.isEmpty()).forEach(sorted::add);
    toLoad.removeIf(sorted::contains);
    int size = toLoad.size();
    boolean excludeOptionals = false;
    while (!toLoad.isEmpty()) {
        boolean containsOptionalDeps = false;
        for (final Node<T> node : toLoad) {
            if (sorted.containsAll(node.dependencies) && DependencyResolver.checkOptionalDependencies(excludeOptionals, sorted, node)) {
                final boolean hasOptionalDeps = !node.optionalDependencies.isEmpty();
                containsOptionalDeps |= hasOptionalDeps;
                sorted.add(node);
                if (excludeOptionals && hasOptionalDeps) {
                    logger.warn("Plugin {} will be loaded before its optional dependencies: [ {} ]", node.candidate.metadata().id(), node.optionalDependencies.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", ")));
                }
            }
        }
        toLoad.removeIf(sorted::contains);
        if (toLoad.size() == size) {
            // without them.
            if (excludeOptionals || !containsOptionalDeps) {
                // We have a problem
                throw new IllegalStateException(String.format("Dependency resolver could not resolve order of all plugins.\n\n" + "Attempted to sort %d plugins: [ %s ]\n" + "Could not sort %d plugins: [ %s ]", original.size(), original.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", ")), toLoad.size(), toLoad.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", "))));
            }
            logger.warn("Failed to resolve plugin load order due to failed dependency resolution, attempting to resolve order ignoring optional" + " dependencies.");
            excludeOptionals = true;
        } else {
            size = toLoad.size();
            excludeOptionals = false;
        }
    }
    final Collection<PluginCandidate<T>> sortedSuccesses = resolutionResult.sortedSuccesses();
    for (final Node<T> x : sorted) {
        sortedSuccesses.add(x.candidate);
    }
    return resolutionResult;
}
Also used : PluginCandidate(org.spongepowered.plugin.PluginCandidate) Collection(java.util.Collection) ArtifactVersion(org.apache.maven.artifact.versioning.ArtifactVersion) Set(java.util.Set) HashMap(java.util.HashMap) Tuple(org.spongepowered.api.util.Tuple) Collectors(java.util.stream.Collectors) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Objects(java.util.Objects) List(java.util.List) Logger(org.apache.logging.log4j.Logger) PluginDependency(org.spongepowered.plugin.metadata.model.PluginDependency) Map(java.util.Map) VersionRange(org.apache.maven.artifact.versioning.VersionRange) LinkedList(java.util.LinkedList) PluginResource(org.spongepowered.plugin.PluginResource) Nullable(org.checkerframework.checker.nullness.qual.Nullable) LinkedHashSet(java.util.LinkedHashSet) LinkedHashSet(java.util.LinkedHashSet) PluginDependency(org.spongepowered.plugin.metadata.model.PluginDependency) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) PluginCandidate(org.spongepowered.plugin.PluginCandidate) HashMap(java.util.HashMap) Map(java.util.Map)

Example 3 with PluginResource

use of org.spongepowered.plugin.PluginResource in project SpongeCommon by SpongePowered.

the class VanillaPluginManager method loadPlugins.

@SuppressWarnings("unchecked")
public void loadPlugins(final VanillaPluginPlatform platform) {
    this.locatedResources.putAll(platform.getResources());
    final Map<PluginCandidate<PluginResource>, PluginLanguageService<PluginResource>> pluginLanguageLookup = new HashMap<>();
    final Map<PluginLanguageService<PluginResource>, PluginLoader<PluginResource, PluginContainer>> pluginLoaders = new HashMap<>();
    // Initialise the plugin language loaders.
    for (final Map.Entry<PluginLanguageService<PluginResource>, List<PluginCandidate<PluginResource>>> candidate : platform.getCandidates().entrySet()) {
        final PluginLanguageService<PluginResource> languageService = candidate.getKey();
        final String loaderClass = languageService.pluginLoader();
        try {
            pluginLoaders.put(languageService, (PluginLoader<PluginResource, PluginContainer>) Class.forName(loaderClass).getConstructor().newInstance());
        } catch (final InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        candidate.getValue().forEach(x -> pluginLanguageLookup.put(x, languageService));
    }
    // Priority to platform plugins that will already exist here -- meaning the resolver will act upon them first
    // and if someone decides to give a plugin an ID that is the same as a platform plugin, the resolver will effectively
    // reject it.
    final Set<PluginCandidate<PluginResource>> resources = new LinkedHashSet<>();
    pluginLanguageLookup.keySet().stream().filter(x -> this.plugins.containsKey(x.metadata().id())).forEach(resources::add);
    resources.addAll(pluginLanguageLookup.keySet());
    final ResolutionResult<PluginResource> resolutionResult = DependencyResolver.resolveAndSortCandidates(resources, platform.logger());
    final Map<PluginCandidate<PluginResource>, String> failedInstances = new HashMap<>();
    final Map<PluginCandidate<PluginResource>, String> consequentialFailedInstances = new HashMap<>();
    final ClassLoader launchClassloader = VanillaLaunch.instance().getClass().getClassLoader();
    for (final PluginCandidate<PluginResource> candidate : resolutionResult.sortedSuccesses()) {
        final PluginContainer plugin = this.plugins.get(candidate.metadata().id());
        if (plugin != null) {
            if (plugin instanceof VanillaDummyPluginContainer) {
                continue;
            }
            // If we get here, we screwed up - duplicate IDs should have been detected earlier.
            // Place it in the resolution result... it'll then get picked up in the big error message
            resolutionResult.duplicateIds().add(candidate.metadata().id());
            // but this is our screw up, let's also make a big point of it
            final PrettyPrinter prettyPrinter = new PrettyPrinter(120).add("ATTEMPTED TO CREATE PLUGIN WITH DUPLICATE PLUGIN ID").centre().hr().addWrapped("Sponge attempted to create a second plugin with ID '%s'. This is not allowed - all plugins must have a unique " + "ID. Usually, Sponge will catch this earlier -- but in this case Sponge has validated two plugins with " + "the same ID. Please report this error to Sponge.", candidate.metadata().id()).add().add("Technical Details:").add("Plugins to load:", 4);
            resolutionResult.sortedSuccesses().forEach(x -> prettyPrinter.add("*" + x.metadata().id(), 4));
            prettyPrinter.add().add("Detected Duplicate IDs:", 4);
            resolutionResult.duplicateIds().forEach(x -> prettyPrinter.add("*" + x, 4));
            prettyPrinter.log(platform.logger(), Level.ERROR);
            continue;
        }
        // This should work fine, we're sorted so all deps should be in place at this stage.
        if (this.stillValid(candidate, consequentialFailedInstances)) {
            final PluginLanguageService<PluginResource> languageService = pluginLanguageLookup.get(candidate);
            final PluginLoader<PluginResource, PluginContainer> pluginLoader = pluginLoaders.get(languageService);
            try {
                final PluginContainer container = pluginLoader.loadPlugin(platform.getStandardEnvironment(), candidate, launchClassloader);
                this.addPlugin(container);
                this.containerToResource.put(container, candidate.resource());
            } catch (final InvalidPluginException e) {
                failedInstances.put(candidate, "Failed to construct: see stacktrace(s) above this message for details.");
                e.printStackTrace();
            }
        }
    }
    resolutionResult.printErrorsIfAny(failedInstances, consequentialFailedInstances, platform.logger());
    platform.logger().info("Loaded plugin(s): {}", this.sortedPlugins.stream().map(p -> p.metadata().id()).collect(Collectors.toList()));
}
Also used : LinkedHashSet(java.util.LinkedHashSet) SpongePluginManager(org.spongepowered.common.launch.plugin.SpongePluginManager) PluginLanguageService(org.spongepowered.plugin.PluginLanguageService) PluginCandidate(org.spongepowered.plugin.PluginCandidate) Level(org.apache.logging.log4j.Level) PrettyPrinter(org.spongepowered.common.util.PrettyPrinter) HashMap(java.util.HashMap) DependencyResolver(org.spongepowered.vanilla.launch.plugin.resolver.DependencyResolver) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) Object2ObjectOpenHashMap(it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) PluginLoader(org.spongepowered.plugin.PluginLoader) Map(java.util.Map) LinkedHashSet(java.util.LinkedHashSet) IdentityHashMap(java.util.IdentityHashMap) ResolutionResult(org.spongepowered.vanilla.launch.plugin.resolver.ResolutionResult) Collection(java.util.Collection) Set(java.util.Set) InvalidPluginException(org.spongepowered.plugin.InvalidPluginException) Collectors(java.util.stream.Collectors) InvocationTargetException(java.lang.reflect.InvocationTargetException) Objects(java.util.Objects) Nullable(org.jetbrains.annotations.Nullable) List(java.util.List) PluginContainer(org.spongepowered.plugin.PluginContainer) PluginDependency(org.spongepowered.plugin.metadata.model.PluginDependency) VanillaPluginPlatform(org.spongepowered.vanilla.applaunch.plugin.VanillaPluginPlatform) Optional(java.util.Optional) PluginResource(org.spongepowered.plugin.PluginResource) Collections(java.util.Collections) Singleton(com.google.inject.Singleton) VanillaLaunch(org.spongepowered.vanilla.launch.VanillaLaunch) HashMap(java.util.HashMap) Object2ObjectOpenHashMap(it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) IdentityHashMap(java.util.IdentityHashMap) PluginLanguageService(org.spongepowered.plugin.PluginLanguageService) PrettyPrinter(org.spongepowered.common.util.PrettyPrinter) InvalidPluginException(org.spongepowered.plugin.InvalidPluginException) PluginResource(org.spongepowered.plugin.PluginResource) ArrayList(java.util.ArrayList) List(java.util.List) PluginLoader(org.spongepowered.plugin.PluginLoader) PluginContainer(org.spongepowered.plugin.PluginContainer) PluginCandidate(org.spongepowered.plugin.PluginCandidate) InvocationTargetException(java.lang.reflect.InvocationTargetException) HashMap(java.util.HashMap) Object2ObjectOpenHashMap(it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) Map(java.util.Map) IdentityHashMap(java.util.IdentityHashMap)

Example 4 with PluginResource

use of org.spongepowered.plugin.PluginResource in project SpongeCommon by SpongePowered.

the class PluginRepositorySource method loadPacks.

@Override
public void loadPacks(final Consumer<Pack> callback, final Pack.PackConstructor constructor) {
    final VanillaPluginManager pluginManager = (VanillaPluginManager) Launch.instance().pluginManager();
    // For each plugin, we create a pack. That pack might be empty.
    for (final PluginContainer pluginContainer : pluginManager.plugins()) {
        // The pack ID is prepended with "plugin-", as this will be the namespace we have to use a valid
        // character as a separator
        final String id = "plugin-" + pluginContainer.metadata().id();
        final PluginResource resource = pluginManager.resource(pluginContainer);
        // TODO: provide hook in the resource to return the file system for all resource types?
        // Also -- can we fake a FileSystem for a non-Jar (needs thinking about)....
        @Nullable Supplier<FileSystem> fileSystemSupplier = null;
        if (resource instanceof JVMPluginResource) {
            final String extension = FilenameUtils.getExtension(resource.path().getFileName().toString());
            if ("jar".equals(extension)) {
                fileSystemSupplier = ((JVMPluginResource) resource)::fileSystem;
            }
        }
        final PluginPackResources packResources = new PluginPackResources(id, pluginContainer, fileSystemSupplier);
        final Pack pack = Pack.create(id, true, () -> packResources, constructor, Pack.Position.BOTTOM, PackSource.DEFAULT);
        ((PackRepositoryBridge_Vanilla) this.repository).bridge$registerResourcePack(pluginContainer, pack);
        callback.accept(pack);
    }
}
Also used : PluginContainer(org.spongepowered.plugin.PluginContainer) JVMPluginResource(org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource) JVMPluginResource(org.spongepowered.plugin.builtin.jvm.locator.JVMPluginResource) PluginResource(org.spongepowered.plugin.PluginResource) PackRepositoryBridge_Vanilla(org.spongepowered.vanilla.bridge.server.packs.repository.PackRepositoryBridge_Vanilla) FileSystem(java.nio.file.FileSystem) VanillaPluginManager(org.spongepowered.vanilla.launch.plugin.VanillaPluginManager) Pack(net.minecraft.server.packs.repository.Pack) Nullable(org.checkerframework.checker.nullness.qual.Nullable)

Example 5 with PluginResource

use of org.spongepowered.plugin.PluginResource in project SpongeCommon by SpongePowered.

the class VanillaPluginPlatform method createPluginCandidates.

public void createPluginCandidates() {
    for (final Map.Entry<String, PluginLanguageService<PluginResource>> languageEntry : this.languageServices.entrySet()) {
        final PluginLanguageService<PluginResource> languageService = languageEntry.getValue();
        for (final Map.Entry<String, Set<PluginResource>> resourcesEntry : this.locatorResources.entrySet()) {
            for (final PluginResource pluginResource : resourcesEntry.getValue()) {
                try {
                    final List<PluginCandidate<PluginResource>> candidates = languageService.createPluginCandidates(this.standardEnvironment, pluginResource);
                    if (candidates.isEmpty()) {
                        continue;
                    }
                    this.pluginCandidates.computeIfAbsent(languageService, k -> new LinkedList<>()).addAll(candidates);
                } catch (final Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}
Also used : JVMKeys(org.spongepowered.plugin.builtin.jvm.JVMKeys) PluginLanguageService(org.spongepowered.plugin.PluginLanguageService) IdentityHashMap(java.util.IdentityHashMap) Iterator(java.util.Iterator) PluginCandidate(org.spongepowered.plugin.PluginCandidate) PluginPlatform(org.spongepowered.common.applaunch.plugin.PluginPlatform) Set(java.util.Set) HashMap(java.util.HashMap) ServiceLoader(java.util.ServiceLoader) Key(org.spongepowered.plugin.blackboard.Key) HashSet(java.util.HashSet) List(java.util.List) Logger(org.apache.logging.log4j.Logger) PluginResourceLocatorService(org.spongepowered.plugin.PluginResourceLocatorService) StandardEnvironment(org.spongepowered.plugin.builtin.StandardEnvironment) Keys(org.spongepowered.plugin.blackboard.Keys) Map(java.util.Map) ServiceConfigurationError(java.util.ServiceConfigurationError) LinkedList(java.util.LinkedList) PluginResource(org.spongepowered.plugin.PluginResource) Path(java.nio.file.Path) Collections(java.util.Collections) Set(java.util.Set) HashSet(java.util.HashSet) PluginCandidate(org.spongepowered.plugin.PluginCandidate) LinkedList(java.util.LinkedList) PluginLanguageService(org.spongepowered.plugin.PluginLanguageService) PluginResource(org.spongepowered.plugin.PluginResource) IdentityHashMap(java.util.IdentityHashMap) HashMap(java.util.HashMap) Map(java.util.Map)

Aggregations

PluginResource (org.spongepowered.plugin.PluginResource)5 Map (java.util.Map)4 Set (java.util.Set)4 ArrayList (java.util.ArrayList)3 HashMap (java.util.HashMap)3 HashSet (java.util.HashSet)3 List (java.util.List)3 PluginCandidate (org.spongepowered.plugin.PluginCandidate)3 Path (java.nio.file.Path)2 Collection (java.util.Collection)2 Collections (java.util.Collections)2 IdentityHashMap (java.util.IdentityHashMap)2 LinkedHashSet (java.util.LinkedHashSet)2 LinkedList (java.util.LinkedList)2 Objects (java.util.Objects)2 Collectors (java.util.stream.Collectors)2 Logger (org.apache.logging.log4j.Logger)2 Nullable (org.checkerframework.checker.nullness.qual.Nullable)2 PluginContainer (org.spongepowered.plugin.PluginContainer)2 PluginLanguageService (org.spongepowered.plugin.PluginLanguageService)2