Skip to content

Commit 32da603

Browse files
committed
Backport most toolchain and loader changes from api-14 to api-12
Mainly: - Remove arch-loom - Add unit-tests
1 parent 945643f commit 32da603

File tree

183 files changed

+4261
-5213
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

183 files changed

+4261
-5213
lines changed

bootstrap/build.gradle.kts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
plugins {
2+
id("implementation-structure")
3+
}
4+
5+
val main by sourceSets.named("main")
6+
7+
val forge by sourceSets.register("forge") {
8+
spongeImpl.addDependencyToImplementation(main, this)
9+
}
10+
11+
val neoforge by sourceSets.register("neoforge") {
12+
spongeImpl.addDependencyToImplementation(main, this)
13+
}
14+
15+
dependencies {
16+
val forgeLib = forge.implementationConfigurationName
17+
forgeLib(libs.securemodules)
18+
19+
val neoforgeLib = neoforge.implementationConfigurationName
20+
neoforgeLib(libs.securejarhandler)
21+
}

src/test/invalid/common/test/TestGame.java renamed to bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/ForgeBootstrap.java

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,38 +22,29 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package org.spongepowered.common.test;
25+
package org.spongepowered.bootstrap.forge;
2626

27-
import com.google.inject.Inject;
28-
import com.google.inject.Singleton;
29-
import org.spongepowered.api.Server;
30-
import org.spongepowered.common.SpongeGame;
27+
import cpw.mods.jarhandling.SecureJar;
3128

32-
import java.nio.file.Path;
29+
public class ForgeBootstrap extends ForgeLikeBootstrap {
3330

34-
@Singleton
35-
public class TestGame extends SpongeGame {
36-
37-
private TestServer server;
38-
39-
@Inject
40-
public TestGame(TestServer server) {
41-
this.server = server;
31+
public ForgeBootstrap(final String[] args) {
32+
super(args);
4233
}
4334

4435
@Override
45-
public boolean isServerAvailable() {
46-
return false;
36+
public String name() {
37+
return "Forge";
4738
}
4839

4940
@Override
50-
public Server getServer() {
51-
return this.server;
41+
protected boolean filterApplicationModule(final SecureJar jar) {
42+
final SecureJar.ModuleDataProvider data = jar.moduleDataProvider();
43+
return data.findFile("META-INF/mods.toml").isPresent() || data.findFile("net/minecraft/client/main/Main.class").isPresent()
44+
|| data.getManifest().getMainAttributes().getValue("FMLModType") != null;
5245
}
5346

54-
@Override
55-
public Path getSavesDirectory() {
56-
throw new UnsupportedOperationException();
47+
public static void main(final String[] args) throws Exception {
48+
new ForgeBootstrap(args).devBoot(true);
5749
}
58-
5950
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.bootstrap.forge;
26+
27+
import cpw.mods.jarhandling.SecureJar;
28+
import net.minecraftforge.securemodules.SecureModuleClassLoader;
29+
import net.minecraftforge.securemodules.SecureModuleFinder;
30+
import org.spongepowered.bootstrap.Bootstrap;
31+
32+
import java.lang.module.Configuration;
33+
import java.lang.module.ModuleFinder;
34+
import java.nio.file.Path;
35+
import java.util.Collection;
36+
import java.util.List;
37+
38+
public abstract class ForgeLikeBootstrap extends Bootstrap<SecureJar> {
39+
private final String[] args;
40+
41+
public ForgeLikeBootstrap(final String[] args) {
42+
this.args = args;
43+
}
44+
45+
@Override
46+
protected SecureJar createJar(final Path[] paths) {
47+
return SecureJar.from(paths);
48+
}
49+
50+
@Override
51+
protected String getModuleName(final SecureJar jar) {
52+
return jar.name();
53+
}
54+
55+
@Override
56+
protected ModuleFinder createModuleFinder(final Collection<SecureJar> jars) {
57+
return SecureModuleFinder.of(jars);
58+
}
59+
60+
@Override
61+
protected ClassLoader createApplicationClassLoader(final Configuration config, final List<ModuleLayer> parentLayers, final ClassLoader parentLoader) {
62+
return new SecureModuleClassLoader("APP-BOOTSTRAP", null, config, parentLayers, List.of(parentLoader));
63+
}
64+
65+
@Override
66+
protected void runApplication(final ModuleLayer layer) throws Exception {
67+
final Class<?> appClass = layer.findModule("cpw.mods.modlauncher").get().getClassLoader().loadClass("cpw.mods.modlauncher.Launcher");
68+
appClass.getDeclaredMethod("main", String[].class).invoke(null, (Object) this.args);
69+
}
70+
}

src/test/java/org/spongepowered/common/test/stub/StubModule.java renamed to bootstrap/src/forge/java/org/spongepowered/bootstrap/forge/VanillaBootstrap.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,20 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
package org.spongepowered.common.test.stub;
25+
package org.spongepowered.bootstrap.forge;
2626

27-
import com.google.inject.AbstractModule;
28-
import org.spongepowered.api.Game;
29-
import org.spongepowered.api.Sponge;
27+
public class VanillaBootstrap extends ForgeLikeBootstrap {
3028

31-
public class StubModule extends AbstractModule {
29+
public VanillaBootstrap(final String[] args) {
30+
super(args);
31+
}
3232

3333
@Override
34-
protected void configure() {
35-
this.bind(Game.class).to(StubGame.class);
36-
37-
this.requestStaticInjection(Sponge.class);
34+
public String name() {
35+
return "Vanilla";
36+
}
3837

38+
public static void main(final String[] args) throws Exception {
39+
new VanillaBootstrap(args).devBoot(true);
3940
}
4041
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* This file is part of Sponge, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) SpongePowered <https://www.spongepowered.org>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package org.spongepowered.bootstrap;
26+
27+
import org.spongepowered.bootstrap.dev.DevClasspath;
28+
29+
import java.io.File;
30+
import java.lang.module.Configuration;
31+
import java.lang.module.ModuleFinder;
32+
import java.net.URL;
33+
import java.nio.file.Path;
34+
import java.util.ArrayList;
35+
import java.util.Arrays;
36+
import java.util.Collection;
37+
import java.util.HashSet;
38+
import java.util.List;
39+
import java.util.Set;
40+
import java.util.stream.Collectors;
41+
import java.util.stream.Stream;
42+
43+
public abstract class Bootstrap<Jar> {
44+
public static final boolean DEBUG = Boolean.getBoolean("sponge.bootstrap.debug");
45+
46+
public abstract String name();
47+
48+
protected abstract Jar createJar(Path[] paths);
49+
50+
protected abstract String getModuleName(Jar jar);
51+
52+
protected abstract ModuleFinder createModuleFinder(Collection<Jar> jars);
53+
54+
/**
55+
* When filtered, the jar content is still accessible via the application classloader but is not loaded as a module.
56+
*/
57+
protected boolean filterApplicationModule(final Jar jar) {
58+
return false;
59+
}
60+
61+
protected abstract ClassLoader createApplicationClassLoader(Configuration config, List<ModuleLayer> parentLayers, ClassLoader parentLoader);
62+
63+
protected abstract void runApplication(ModuleLayer layer) throws Exception;
64+
65+
public void devBoot(final boolean isolated) throws Exception {
66+
this.boot(DevClasspath.resolve(), isolated);
67+
}
68+
69+
public void boot(final List<Path[]> classpath, final boolean isolated) throws Exception {
70+
if (Bootstrap.DEBUG) {
71+
System.out.println("Bootstrapping " + this.name() + " from classloader: " + getClass().getClassLoader());
72+
System.out.println("Isolated: " + isolated);
73+
}
74+
75+
// Collect the jars
76+
final Set<String> moduleNames = new HashSet<>();
77+
final List<Path> resourcePaths = new ArrayList<>();
78+
final List<Jar> resourceJars = new ArrayList<>();
79+
final List<Jar> appJars = new ArrayList<>();
80+
81+
for (final Path[] paths : classpath) {
82+
final Jar jar = this.createJar(paths);
83+
final String name = this.getModuleName(jar);
84+
85+
// Ignore modules already present
86+
if (ModuleLayer.boot().findModule(name).isPresent()) {
87+
if (Bootstrap.DEBUG) {
88+
System.out.println("Boot: " + name + " " + Bootstrap.formatUnion(paths));
89+
}
90+
continue;
91+
}
92+
93+
if (this.filterApplicationModule(jar)) {
94+
if (Bootstrap.DEBUG) {
95+
System.out.println("Filtered: " + name + " " + Bootstrap.formatUnion(paths));
96+
}
97+
resourceJars.add(jar);
98+
resourcePaths.addAll(Arrays.asList(paths));
99+
continue;
100+
}
101+
102+
if (!moduleNames.add(name)) {
103+
if (Bootstrap.DEBUG) {
104+
System.out.println("Duplicate: " + name + " " + Bootstrap.formatUnion(paths));
105+
}
106+
resourceJars.add(jar);
107+
resourcePaths.addAll(Arrays.asList(paths));
108+
continue;
109+
}
110+
111+
if (Bootstrap.DEBUG) {
112+
System.out.println("App: " + name + " " + Bootstrap.formatUnion(paths));
113+
}
114+
appJars.add(jar);
115+
}
116+
117+
// Create the layer configuration
118+
final ModuleFinder finder = this.createModuleFinder(appJars);
119+
final Configuration config = ModuleLayer.boot().configuration().resolveAndBind(finder, ModuleFinder.ofSystem(), moduleNames);
120+
final List<ModuleLayer> parentLayers = List.of(ModuleLayer.boot());
121+
122+
// Isolation
123+
final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
124+
ClassLoader parentLoader = ClassLoader.getPlatformClassLoader();
125+
if (!isolated) {
126+
// Make sure we don't leak classes that are supposed to be
127+
// in the game layer from the boostrap layer.
128+
final String resources = System.getProperty("sponge.resources");
129+
if (resources != null) {
130+
final List<Jar> spongeJars = new ArrayList<>();
131+
for (final String entry : resources.split(File.pathSeparator)) {
132+
final Path[] paths = Stream.of(entry.split("&")).map(Path::of).toArray(Path[]::new);
133+
spongeJars.add(this.createJar(paths));
134+
}
135+
final ModuleFinder spongeFinder = this.createModuleFinder(spongeJars);
136+
final ModuleFinder resourceFinder = this.createModuleFinder(resourceJars);
137+
parentLoader = new FilteringPassthroughClassLoader(contextLoader,
138+
Stream.concat(spongeFinder.findAll().stream(), resourceFinder.findAll().stream()));
139+
}
140+
}
141+
142+
// Intermediate classloader to include resources but not modules
143+
if (!resourcePaths.isEmpty()) {
144+
final URL[] urls = new URL[resourcePaths.size()];
145+
for (int i = 0; i < urls.length; i++) {
146+
urls[i] = resourcePaths.get(i).toUri().toURL();
147+
}
148+
parentLoader = new ResourceClassLoader("BOOTSTRAP-RESOURCES", urls, parentLoader);
149+
}
150+
151+
// Create the application classloader
152+
final ClassLoader loader = this.createApplicationClassLoader(config, parentLayers, parentLoader);
153+
final ModuleLayer layer = ModuleLayer.boot().defineModules(config, moduleName -> loader);
154+
155+
// Run the application
156+
try {
157+
Thread.currentThread().setContextClassLoader(loader);
158+
if (Bootstrap.DEBUG) {
159+
System.out.println("Starting application ...");
160+
}
161+
this.runApplication(layer);
162+
} finally {
163+
Thread.currentThread().setContextClassLoader(contextLoader);
164+
}
165+
}
166+
167+
public static String formatUnion(final Path[] paths) {
168+
return Arrays.stream(paths).map(Path::toString).collect(Collectors.joining("&"));
169+
}
170+
}

0 commit comments

Comments
 (0)