Skip to content

Commit 1104fd1

Browse files
committed
Add bootstrapper
1 parent aff8eb4 commit 1104fd1

File tree

3 files changed

+293
-0
lines changed

3 files changed

+293
-0
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.gradle/
2+
build/
3+
4+
bin/
5+
.settings/
6+
.project
7+
.classpath
8+
9+
10+

build.gradle

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
apply plugin: 'java'
2+
apply plugin: 'eclipse'
3+
4+
sourceCompatibility = '1.6'
5+
targetCompatibility = '1.6'
6+
7+
version = '0.0.1'
8+
9+
repositories {
10+
mavenCentral()
11+
maven {
12+
name = 'minecraft'
13+
url = 'https://libraries.minecraft.net/'
14+
}
15+
}
16+
17+
configurations {
18+
buildOnly
19+
}
20+
21+
dependencies {
22+
compile 'net.minecraft:launchwrapper:1.12'
23+
compile 'org.apache.logging.log4j:log4j-core:2.0-beta9'
24+
// TODO Fix this
25+
buildOnly files('C:/Users/Simon/.gradle/caches/minecraft/net/minecraftforge/forge/1.8.9-11.15.1.1808/stable/22/forgeBin-1.8.9-11.15.1.1808.jar')
26+
compile files('C:/Users/Simon/.gradle/caches/minecraft/net/minecraftforge/forge/1.8.9-11.15.1.1808/start')
27+
}
28+
29+
sourceSets.main.compileClasspath += [ configurations.buildOnly ]
30+
31+
jar {
32+
manifest {
33+
attributes(
34+
"Main-Class": "com.simon816.sponge.bootstrap.Bootstrap",
35+
"Class-Path": "libraries/net/minecraft/launchwrapper/1.12/launchwrapper-1.12.jar"
36+
+ " libraries/net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar"
37+
+ " minecraft_server.1.8.9.jar"
38+
)
39+
}
40+
}
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
package com.simon816.sponge.bootstrap;
2+
3+
import net.minecraft.launchwrapper.ITweaker;
4+
import net.minecraft.launchwrapper.Launch;
5+
import net.minecraft.launchwrapper.LaunchClassLoader;
6+
import net.minecraftforge.fml.relauncher.CoreModManager;
7+
import net.minecraftforge.gradle.GradleStartCommon;
8+
import org.apache.logging.log4j.LogManager;
9+
import org.apache.logging.log4j.Logger;
10+
11+
import java.io.File;
12+
import java.io.FileFilter;
13+
import java.lang.reflect.Field;
14+
import java.lang.reflect.Method;
15+
import java.net.URISyntaxException;
16+
import java.net.URL;
17+
import java.net.URLClassLoader;
18+
import java.util.ArrayList;
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.Scanner;
23+
24+
public class Bootstrap {
25+
26+
private static final String COREMOD = "org.spongepowered.mod.SpongeCoremod";
27+
private static final String PRE_TWEAKER = "com.simon816.sponge.bootstrap.Bootstrap$PreFMLTweaker";
28+
private static final String POST_TWEAKER = "com.simon816.sponge.bootstrap.Bootstrap$PostFMLTweaker";
29+
public static final String FML_TWEAKER = "net.minecraftforge.fml.common.launcher.FMLServerTweaker";
30+
31+
private static final Logger logger = LogManager.getLogger("SpongeBootstrap");
32+
33+
public static void main(String[] args) {
34+
logger.info("Detecting environment...");
35+
try {
36+
Class.forName("GradleStartServer");
37+
logger.info("Detected gradle development environment, continuing");
38+
loadGradle(args);
39+
} catch (ClassNotFoundException e) {
40+
try {
41+
Class.forName("net.minecraft.launchwrapper.Launch");
42+
logger.info("Found launch wrapper, continuing");
43+
findAndLoadJars();
44+
load(args);
45+
} catch (ClassNotFoundException e1) {
46+
System.err.println("Failed to load Launch class");
47+
System.exit(1);
48+
} catch (NoClassDefFoundError e2) {
49+
System.err.println("Failed to load Launch class");
50+
System.exit(1);
51+
}
52+
}
53+
}
54+
55+
private static void removeFromSysArgs() {
56+
// Remove SpongeCoremod from args as we load it ourselves
57+
List<String> coreModsArgs = new ArrayList<String>(Arrays.asList(System.getProperty("fml.coreMods.load", "").split(",")));
58+
while (coreModsArgs.contains(COREMOD)) {
59+
coreModsArgs.remove(COREMOD);
60+
}
61+
StringBuilder coreMods = new StringBuilder();
62+
for (String cm : coreModsArgs) {
63+
coreMods.append(cm).append(',');
64+
}
65+
System.setProperty("fml.coreMods.load", coreMods.toString());
66+
}
67+
68+
private static void findAndLoadJars() {
69+
File rootDir;
70+
try {
71+
rootDir = new File(Bootstrap.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParentFile();
72+
} catch (URISyntaxException e) {
73+
System.err.println("Could not get jar directory");
74+
e.printStackTrace();
75+
System.exit(1);
76+
return;
77+
}
78+
findJar(rootDir, "forge", new FileFilter() {
79+
80+
@Override
81+
public boolean accept(File pathname) {
82+
String fn = pathname.getName().toLowerCase();
83+
return fn.endsWith(".jar") && fn.contains("forge") && fn.contains("-universal") && fn.contains("1.8.9");
84+
}
85+
});
86+
findJar(new File(rootDir, "mods"), "sponge", new FileFilter() {
87+
88+
@Override
89+
public boolean accept(File pathname) {
90+
String fn = pathname.getName().toLowerCase();
91+
return fn.endsWith(".jar") && fn.contains("sponge") && fn.contains("1.8.9");
92+
}
93+
});
94+
}
95+
96+
private static void findJar(File directory, String jarName, FileFilter filter) {
97+
File[] files = directory.listFiles(filter);
98+
if (files == null) {
99+
System.err.println("An error occured when listing directory contents");
100+
System.exit(1);
101+
return;
102+
}
103+
if (files.length == 0) {
104+
System.err.println("Could not find " + jarName + " jar. Please make sure a" + jarName + " jar exists.");
105+
System.exit(1);
106+
}
107+
int idx = 0;
108+
if (files.length > 1) {
109+
System.out.println("Multiple " + jarName + " jars have been detected, please choose");
110+
for (int i = 0; i < files.length; i++) {
111+
System.out.println(String.format("%d: %s", i, files[i]));
112+
}
113+
@SuppressWarnings("resource")
114+
Scanner scanner = new Scanner(System.in);
115+
do {
116+
idx = scanner.nextInt();
117+
} while (idx < 0 || idx > files.length - 1);
118+
// scanner.close(); Don't close - this kills command handling
119+
}
120+
File jarFile = files[idx];
121+
URLClassLoader classLoader = (URLClassLoader) Bootstrap.class.getClassLoader();
122+
try {
123+
Method addUrl = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
124+
addUrl.setAccessible(true);
125+
addUrl.invoke(classLoader, jarFile.toURI().toURL());
126+
} catch (Exception e) {
127+
System.err.println("Failed to add " + jarName + " jar to classpath");
128+
e.printStackTrace();
129+
System.exit(1);
130+
}
131+
}
132+
133+
private static void load(String[] args) {
134+
removeFromSysArgs();
135+
String[] newArgs = new String[args.length + 6];
136+
System.arraycopy(args, 0, newArgs, 6, args.length);
137+
newArgs[0] = "--tweakClass";
138+
newArgs[1] = PRE_TWEAKER;
139+
newArgs[2] = "--tweakClass";
140+
newArgs[3] = FML_TWEAKER;
141+
newArgs[4] = "--tweakClass";
142+
newArgs[5] = POST_TWEAKER;
143+
Launch.main(newArgs);
144+
}
145+
146+
private static void loadGradle(String[] args) {
147+
removeFromSysArgs();
148+
try {
149+
GradleHackServer.main(args);
150+
} catch (Throwable e) {
151+
e.printStackTrace();
152+
}
153+
154+
}
155+
156+
public static class PreFMLTweaker extends SimpleTweaker {
157+
158+
@Override
159+
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
160+
// Adds SpongeCoremod to FML's 'root plugins' so it always loads
161+
// before other coremods
162+
// Add to end of array so FML plugins are first
163+
try {
164+
logger.info("Performing SpongeCoremod injection");
165+
Field rootPluginsField = CoreModManager.class.getDeclaredField("rootPlugins");
166+
rootPluginsField.setAccessible(true);
167+
String[] rootPlugins = (String[]) rootPluginsField.get(null);
168+
String[] rootPlugins2 = new String[rootPlugins.length + 1];
169+
System.arraycopy(rootPlugins, 0, rootPlugins2, 0, rootPlugins.length);
170+
rootPlugins2[rootPlugins.length] = COREMOD;
171+
rootPluginsField.set(null, rootPlugins2);
172+
logger.info("SpongeCoremod successfully injected into FML");
173+
} catch (Exception e) {
174+
e.printStackTrace();
175+
}
176+
}
177+
}
178+
179+
public static class PostFMLTweaker extends SimpleTweaker {
180+
181+
@Override
182+
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
183+
// Mixin system already loaded early so don't load twice
184+
List<?> tweakClasses = (List<?>) Launch.blackboard.get("TweakClasses");
185+
while (tweakClasses.remove("org.spongepowered.asm.launch.MixinTweaker")) {
186+
}
187+
System.out.println(Launch.blackboard);
188+
}
189+
}
190+
191+
private static class SimpleTweaker implements ITweaker {
192+
193+
@Override
194+
public void acceptOptions(List<String> args, File gameDir, File assetsDir, String profile) {
195+
}
196+
197+
@Override
198+
public void injectIntoClassLoader(LaunchClassLoader classLoader) {
199+
}
200+
201+
@Override
202+
public String getLaunchTarget() {
203+
return "net.minecraft.server.MinecraftServer";
204+
}
205+
206+
@Override
207+
public String[] getLaunchArguments() {
208+
return new String[0];
209+
}
210+
211+
}
212+
213+
public static class GradleHackServer extends GradleStartCommon {
214+
215+
public static void main(String[] args) throws Throwable {
216+
(new GradleHackServer()).launch(args);
217+
}
218+
219+
@Override
220+
protected String getTweakClass() {
221+
return PRE_TWEAKER;
222+
}
223+
224+
@Override
225+
protected String getBounceClass() {
226+
return "net.minecraft.launchwrapper.Launch";
227+
}
228+
229+
@Override
230+
protected void preLaunch(Map<String, String> argMap, List<String> extras) {
231+
// Add the tweak class from GradleStartServer AFTER our tweaker
232+
extras.add("--tweakClass");
233+
extras.add(FML_TWEAKER);
234+
extras.add("--tweakClass");
235+
extras.add(POST_TWEAKER);
236+
}
237+
238+
@Override
239+
protected void setDefaultArguments(Map<String, String> argMap) {
240+
}
241+
}
242+
243+
}

0 commit comments

Comments
 (0)