Skip to content

Commit

Permalink
Support plugin provided guice module
Browse files Browse the repository at this point in the history
  • Loading branch information
aromaa committed Sep 27, 2024
1 parent 02f45e8 commit fe720e4
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
package org.spongepowered.forge.launch.plugin;

import com.google.common.collect.MapMaker;
import com.google.inject.Injector;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.common.inject.SpongePluginInjectorProvider;
import org.spongepowered.common.launch.Launch;
import org.spongepowered.forge.launch.ForgeLaunch;
import org.spongepowered.plugin.PluginContainer;
Expand All @@ -41,16 +43,21 @@
import java.util.Objects;
import java.util.Optional;

public class ForgePluginContainer implements PluginContainer {
public class ForgePluginContainer implements PluginContainer, SpongePluginInjectorProvider {
private final ModContainer modContainer;

private Injector injector;
private Logger logger;
private PluginMetadata pluginMetadata;

ForgePluginContainer(final ModContainer modContainer) {
this.modContainer = modContainer;
}

public void initializeInstance(Injector injector) {
this.injector = injector;
}

public ModContainer getModContainer() {
return this.modContainer;
}
Expand Down Expand Up @@ -92,6 +99,11 @@ public Object instance() {
return this.modContainer.getMod();
}

@Override
public Injector injector() {
return this.injector;
}

private static final Map<ModContainer, ForgePluginContainer> containers = new MapMaker().weakKeys().makeMap();

public static ForgePluginContainer of(final ModContainer modContainer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import org.spongepowered.common.inject.plugin.PluginModule;
import org.spongepowered.common.launch.Launch;
import org.spongepowered.forge.launch.event.ForgeEventManager;
import org.spongepowered.plugin.PluginContainer;

import java.util.Optional;

Expand Down Expand Up @@ -84,9 +83,10 @@ private void constructPlugin() {
try {
LOGGER.trace(Logging.LOADING, "Loading plugin instance {} of type {}", getModId(), this.modClass.getName());

final PluginContainer pluginContainer = ForgePluginContainer.of(this);
final Injector childInjector = Launch.instance().lifecycle().platformInjector().createChildInjector(new PluginModule(pluginContainer, this.modClass));
this.modInstance = childInjector.getInstance(this.modClass);
final ForgePluginContainer pluginContainer = ForgePluginContainer.of(this);
final Injector pluginInjector = PluginModule.create(pluginContainer, this.modClass, Launch.instance().lifecycle().platformInjector());
this.modInstance = pluginInjector.getInstance(this.modClass);
pluginContainer.initializeInstance(pluginInjector);
((ForgeEventManager) MinecraftForge.EVENT_BUS).registerListeners(pluginContainer, this.modInstance);

LOGGER.trace(Logging.LOADING, "Loaded plugin instance {} of type {}", getModId(), this.modClass.getName());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.common.inject;

import com.google.inject.Injector;

public interface SpongePluginInjectorProvider {

Injector injector();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,44 @@
*/
package org.spongepowered.common.inject.plugin;

import com.google.inject.AbstractModule;
import com.google.inject.Binding;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.PrivateModule;
import com.google.inject.Scopes;
import com.google.inject.spi.Element;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.ExposedBinding;
import com.google.inject.spi.PrivateElements;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.api.Sponge;
import org.spongepowered.common.inject.InjectionPointProvider;
import org.spongepowered.common.inject.SpongePluginInjectorProvider;
import org.spongepowered.common.inject.provider.PluginConfigurationModule;
import org.spongepowered.plugin.PluginContainer;
import org.spongepowered.plugin.metadata.model.PluginDependency;

import java.util.ArrayList;

/**
* A module installed for each plugin.
*/
public final class PluginModule extends AbstractModule {
public final class PluginModule extends PrivateModule {

private final PluginContainer container;
private final Class<?> pluginClass;

public PluginModule(final PluginContainer container, final Class<?> pluginClass) {
private PluginModule(final PluginContainer container, final Class<?> pluginClass) {
this.container = container;
this.pluginClass = pluginClass;
}

@Override
protected void configure() {
this.bind(this.pluginClass).in(Scopes.SINGLETON);
this.expose(this.pluginClass);

this.install(new InjectionPointProvider());

Expand All @@ -55,6 +70,60 @@ protected void configure() {
this.bind(System.Logger.class).toProvider(() -> System.getLogger(this.container.logger().getName())).in(Scopes.SINGLETON);

this.install(new PluginConfigurationModule());

for (final PluginDependency dependency : this.container.metadata().dependencies()) {
if (dependency.loadOrder() != PluginDependency.LoadOrder.AFTER) {
continue;
}

Sponge.pluginManager().plugin(dependency.id()).ifPresent(dependencyContainer -> {
if (!(dependencyContainer instanceof final SpongePluginInjectorProvider injectorProvider)) {
return;
}

for (final Binding binding : injectorProvider.injector().getBindings().values()) {
if (!(binding.getSource() instanceof ElementSource)) {
continue;
}

if (binding instanceof final ExposedBinding<?> exposedBinding) {
final PrivateElements privateElements = exposedBinding.getPrivateElements();
for (final Element privateElement : privateElements.getElements()) {
if (!(privateElement instanceof final Binding privateBinding) || !privateElements.getExposedKeys().contains(privateBinding.getKey())) {
continue;
}

this.bind(privateBinding.getKey()).toProvider(() -> injectorProvider.injector().getInstance(privateBinding.getKey()));
}

continue;
}

this.bind(binding.getKey()).toProvider(() -> injectorProvider.injector().getInstance(binding.getKey()));
}
});
}
}

public static Injector create(final PluginContainer container, final Class<?> pluginClass, final @Nullable Injector platformInjector) {
final ArrayList<Module> modules = new ArrayList<>(2);
modules.add(new PluginModule(container, pluginClass));

final @Nullable Object customModule = container.metadata().property("module").orElse(null);
if (customModule != null) {
try {
final Class<?> moduleClass = Class.forName(customModule.toString(), true, pluginClass.getClassLoader());
final com.google.inject.Module moduleInstance = (com.google.inject.Module) moduleClass.getConstructor().newInstance();
modules.add(moduleInstance);
} catch (final Exception ex) {
throw new RuntimeException("Failed to instantiate the custom module!", ex);
}
}

if (platformInjector != null) {
return platformInjector.createChildInjector(modules);
} else {
return Guice.createInjector(modules);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.test.serviceloader;

import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import org.spongepowered.plugin.builtin.jvm.Plugin;

@Plugin("childmoduletestplugin")
public final class ChildModuleTestPlugin implements ChildModuleTestPluginService {

private final ChildModuleTestPluginService.External external;

@Inject
public ChildModuleTestPlugin(final ChildModuleTestPluginService.External external) {
this.external = external;
}

@Override
public External external() {
return this.external;
}

private static final class ExternalImpl implements ChildModuleTestPluginService.External {
}

public static final class Module extends AbstractModule {

@Override
protected void configure() {
this.bind(ChildModuleTestPluginService.class).to(ChildModuleTestPlugin.class);
this.bind(ChildModuleTestPluginService.External.class).toInstance(new ExternalImpl());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.test.serviceloader;

public interface ChildModuleTestPluginService {

External external();

interface External {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.test.serviceloader;

import com.google.inject.Inject;
import org.apache.logging.log4j.Logger;
import org.spongepowered.api.Sponge;
import org.spongepowered.plugin.builtin.jvm.Plugin;

@Plugin("parentmoduletestplugin")
public final class ParentModuleTestPlugin {

@Inject
public ParentModuleTestPlugin(final Logger logger, final ChildModuleTestPlugin childModulePlugin, final ChildModuleTestPluginService service, final ChildModuleTestPluginService.External external) {
if (Sponge.pluginManager().plugin("childmoduletestplugin").get().instance() != childModulePlugin) {
logger.error("Mismatched instance of the plugin childmoduletestplugin");
}

if (childModulePlugin != service) {
logger.error("Mismatched instance of service component from plugin childmoduletestplugin");
}

if (childModulePlugin.external() != external) {
logger.error("Mismatched instance of the external component from plugin childmoduletestplugin");
}
}
}
22 changes: 22 additions & 0 deletions testplugins/src/main/resources/META-INF/sponge_plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,28 @@
"name": "Resource Pack Tests",
"entrypoint": "org.spongepowered.test.resourcepack.ResourcePackTest",
"description": "Resource Pack Tests"
},
{
"id": "childmoduletestplugin",
"name": "Child Module Tests",
"entrypoint": "org.spongepowered.test.serviceloader.ChildModuleTestPlugin",
"description": "Child Module Tests",
"properties": {
"module": "org.spongepowered.test.serviceloader.ChildModuleTestPlugin$Module"
}
},
{
"id": "parentmoduletestplugin",
"name": "Parent Module Tests",
"entrypoint": "org.spongepowered.test.serviceloader.ParentModuleTestPlugin",
"description": "Parent Module Tests",
"dependencies": [
{
"id": "childmoduletestplugin",
"version": "11.0.0",
"load-order": "AFTER"
}
]
}
]
}
Loading

0 comments on commit fe720e4

Please sign in to comment.