parent
5302b8f008
commit
64a9037bcb
14
arclight-api/build.gradle
Normal file
14
arclight-api/build.gradle
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava {
|
||||||
|
options.compilerArgs << '-XDignore.symbol.file' << '-XDenableSunApiLintControl'
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package io.izzel.arclight.util;
|
package io.izzel.arclight.api;
|
||||||
|
|
||||||
import sun.reflect.CallerSensitive;
|
import sun.reflect.CallerSensitive;
|
||||||
|
|
11
arclight-common/build.gradle
Normal file
11
arclight-common/build.gradle
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile project(':arclight-api')
|
||||||
|
}
|
|
@ -16,6 +16,13 @@ apply plugin: 'org.spongepowered.mixin'
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
minecraftVersion = '1.14.4'
|
||||||
|
forgeVersion = '28.2.0'
|
||||||
|
installerInfoDir = file("$buildDir/installer-info")
|
||||||
|
installerInfo = file("$installerInfoDir/META-INF/installer.json")
|
||||||
|
}
|
||||||
|
|
||||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
|
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8'
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
@ -25,7 +32,7 @@ configurations {
|
||||||
}
|
}
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
mappings channel: 'stable', version: '58-1.14.4'
|
mappings channel: 'stable', version: "58-$minecraftVersion"
|
||||||
accessTransformer = project.file('src/main/resources/META-INF/accesstransformer.cfg')
|
accessTransformer = project.file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||||
runs {
|
runs {
|
||||||
server {
|
server {
|
||||||
|
@ -55,19 +62,21 @@ repositories {
|
||||||
|
|
||||||
def embedLibs = ['org.spongepowered:mixin:0.8', 'org.ow2.asm:asm-util:6.2',
|
def embedLibs = ['org.spongepowered:mixin:0.8', 'org.ow2.asm:asm-util:6.2',
|
||||||
'org.ow2.asm:asm-analysis:6.2', 'org.yaml:snakeyaml:1.23',
|
'org.ow2.asm:asm-analysis:6.2', 'org.yaml:snakeyaml:1.23',
|
||||||
'net.md-5:bungeecord-chat:1.13-SNAPSHOT', 'org.xerial:sqlite-jdbc:3.28.0',
|
'org.xerial:sqlite-jdbc:3.28.0', 'mysql:mysql-connector-java:5.1.47',
|
||||||
'mysql:mysql-connector-java:5.1.47', 'commons-lang:commons-lang:2.6',
|
'commons-lang:commons-lang:2.6', 'jline:jline:2.12.1',
|
||||||
'jline:jline:2.12.1', 'com.googlecode.json-simple:json-simple:1.1.1',
|
'com.googlecode.json-simple:json-simple:1.1.1', 'org.apache.logging.log4j:log4j-jul:2.11.2',
|
||||||
'org.apache.logging.log4j:log4j-jul:2.11.2', 'net.md-5:SpecialSource:1.8.6',
|
'net.md-5:SpecialSource:1.8.6', 'net.minecraftforge:eventbus:2.0.0-milestone.1:service']
|
||||||
'net.minecraftforge:eventbus:2.0.0-milestone.1:service']
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
minecraft 'net.minecraftforge:forge:1.14.4-28.2.0'
|
minecraft "net.minecraftforge:forge:$minecraftVersion-$forgeVersion"
|
||||||
compile group: 'org.jetbrains', name: 'annotations', version: '19.0.0'
|
compile group: 'org.jetbrains', name: 'annotations', version: '19.0.0'
|
||||||
|
embed project(':arclight-common')
|
||||||
|
embed project(':forge-installer')
|
||||||
for (def lib : embedLibs) {
|
for (def lib : embedLibs) {
|
||||||
embedJar "$lib@jar"
|
embedJar "$lib@jar"
|
||||||
}
|
}
|
||||||
embed 'org.spigotmc:spigot-api:1.14.4-R0.1-SNAPSHOT@jar'
|
embed 'net.md-5:bungeecord-chat:1.13-SNAPSHOT@jar'
|
||||||
|
embed "org.spigotmc:spigot-api:$minecraftVersion-R0.1-SNAPSHOT@jar"
|
||||||
embed files("$projectDir/libs/spigot-1.14.4-mapped-deobf.jar")
|
embed files("$projectDir/libs/spigot-1.14.4-mapped-deobf.jar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,26 +91,13 @@ def getGitHash = { ->
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
filesNotMatching("**/accesstransformer.cfg") {
|
filesNotMatching("**/accesstransformer.cfg") {
|
||||||
expand 'version': "1.14.4-${project.version}-${getGitHash()}"
|
expand 'version': "$minecraftVersion-${project.version}-${getGitHash()}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def classpath = {
|
|
||||||
"libraries/org/ow2/asm/asm/6.2/asm-6.2.jar libraries/org/ow2/asm/asm-commons/6.2/asm-commons-6.2.jar libraries/org/ow2/asm/asm-tree/6.2/asm-tree-6.2.jar libraries/cpw/mods/modlauncher/4.1.0/modlauncher-4.1.0.jar libraries/cpw/mods/grossjava9hacks/1.1.0/grossjava9hacks-1.1.0.jar libraries/net/minecraftforge/accesstransformers/1.0.1-milestone.0.1+94458e7-shadowed/accesstransformers-1.0.1-milestone.0.1+94458e7-shadowed.jar libraries/net/minecraftforge/forgespi/1.5.0/forgespi-1.5.0.jar libraries/net/minecraftforge/coremods/1.0.0/coremods-1.0.0.jar libraries/net/minecraftforge/unsafe/0.2.0/unsafe-0.2.0.jar libraries/com/electronwill/night-config/core/3.6.0/core-3.6.0.jar libraries/com/electronwill/night-config/toml/3.6.0/toml-3.6.0.jar libraries/org/jline/jline/3.12.1/jline-3.12.1.jar libraries/org/apache/maven/maven-artifact/3.6.0/maven-artifact-3.6.0.jar libraries/net/jodah/typetools/0.6.0/typetools-0.6.0.jar libraries/java3d/vecmath/1.5.2/vecmath-1.5.2.jar libraries/org/apache/logging/log4j/log4j-api/2.11.2/log4j-api-2.11.2.jar libraries/org/apache/logging/log4j/log4j-core/2.11.2/log4j-core-2.11.2.jar libraries/net/minecrell/terminalconsoleappender/1.2.0/terminalconsoleappender-1.2.0.jar libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar libraries/net/minecraft/server/1.14.4/server-1.14.4-extra-stable.jar " +
|
|
||||||
embedLibs.collect {
|
|
||||||
def arr = it.split(':')
|
|
||||||
if (arr.length == 3) {
|
|
||||||
return "libraries/${arr[0].replace('.', '/')}/${arr[1]}/${arr[2]}/${arr[1]}-${arr[2]}.jar"
|
|
||||||
} else if (arr.length == 4) {
|
|
||||||
return "libraries/${arr[0].replace('.', '/')}/${arr[1]}/${arr[2]}/${arr[1]}-${arr[2]}-${arr[3]}.jar"
|
|
||||||
} else return ""
|
|
||||||
}.join(' ') + " forge-1.14.4-28.2.0.jar"
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest.attributes 'MixinConnector': 'io.izzel.arclight.mod.ArclightConnector'
|
manifest.attributes 'MixinConnector': 'io.izzel.arclight.mod.ArclightConnector'
|
||||||
manifest.attributes 'Main-Class': 'io.izzel.arclight.server.Main'
|
manifest.attributes 'Main-Class': 'io.izzel.arclight.server.Main'
|
||||||
manifest.attributes 'Class-Path': classpath()
|
|
||||||
manifest.attributes 'Implementation-Title': 'Arclight'
|
manifest.attributes 'Implementation-Title': 'Arclight'
|
||||||
manifest.attributes 'Implementation-Version': "arclight-${project.version}-${getGitHash()}"
|
manifest.attributes 'Implementation-Version': "arclight-${project.version}-${getGitHash()}"
|
||||||
manifest.attributes 'Implementation-Vendor': 'Arclight Team'
|
manifest.attributes 'Implementation-Vendor': 'Arclight Team'
|
||||||
|
@ -112,15 +108,22 @@ jar {
|
||||||
exclude "META-INF/*.RSA"
|
exclude "META-INF/*.RSA"
|
||||||
exclude "LICENSE.txt"
|
exclude "LICENSE.txt"
|
||||||
}
|
}
|
||||||
into('libs') {
|
|
||||||
from(configurations.embedJar.collect())
|
|
||||||
}
|
|
||||||
into('META-INF') {
|
into('META-INF') {
|
||||||
from(files("${project(':scripts').projectDir}/bukkit_srg.srg"))
|
from(files("${project(':scripts').projectDir}/bukkit_srg.srg"))
|
||||||
from(files("${project(':scripts').projectDir}/resources/inheritanceMap.txt"))
|
from(files("${project(':scripts').projectDir}/resources/inheritanceMap.txt"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task generateInstallerInfo {
|
||||||
|
def output = [installer: [minecraft: minecraftVersion, forge: forgeVersion], libraries: embedLibs]
|
||||||
|
outputs.file(installerInfo)
|
||||||
|
doLast {
|
||||||
|
installerInfo.text = groovy.json.JsonOutput.toJson(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets.main.output.dir installerInfoDir, builtBy: generateInstallerInfo
|
||||||
|
|
||||||
mixin {
|
mixin {
|
||||||
add sourceSets.main, 'mixins.arclight.refmap.json'
|
add sourceSets.main, 'mixins.arclight.refmap.json'
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
package io.izzel.arclight.mixin.core.state;
|
||||||
|
|
||||||
|
import net.minecraft.state.EnumProperty;
|
||||||
|
import net.minecraft.util.IStringSerializable;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@Mixin(EnumProperty.class)
|
||||||
|
public abstract class EnumPropertyMixin {
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public static <T extends Enum<T> & IStringSerializable> EnumProperty<T> create(String name, Class<T> clazz, Collection<T> values) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author IzzelAliz
|
||||||
|
* @reason
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public static <T extends Enum<T> & IStringSerializable> EnumProperty<T> create(String name, Class<T> clazz, Predicate<T> filter) {
|
||||||
|
try {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
|
for (T enumConstant : clazz.getEnumConstants()) {
|
||||||
|
if (filter.test(enumConstant)) list.add(enumConstant);
|
||||||
|
}
|
||||||
|
return create(name, clazz, list);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
System.out.println(name);
|
||||||
|
System.out.println(clazz);
|
||||||
|
System.out.println(filter);
|
||||||
|
for (T constant : clazz.getEnumConstants()) {
|
||||||
|
System.out.println(constant);
|
||||||
|
}
|
||||||
|
t.printStackTrace();
|
||||||
|
throw t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import io.izzel.arclight.util.Unsafe;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
|
@ -19,7 +19,7 @@ import io.izzel.arclight.bridge.bukkit.MaterialBridge;
|
||||||
import io.izzel.arclight.mod.ArclightMod;
|
import io.izzel.arclight.mod.ArclightMod;
|
||||||
import io.izzel.arclight.mod.util.potion.ArclightPotionEffect;
|
import io.izzel.arclight.mod.util.potion.ArclightPotionEffect;
|
||||||
import io.izzel.arclight.util.EnumHelper;
|
import io.izzel.arclight.util.EnumHelper;
|
||||||
import io.izzel.arclight.util.Unsafe;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
|
@ -2,7 +2,7 @@ package io.izzel.arclight.mod.util.remapper;
|
||||||
|
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import io.izzel.arclight.util.Unsafe;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
import net.md_5.specialsource.InheritanceMap;
|
import net.md_5.specialsource.InheritanceMap;
|
||||||
import net.md_5.specialsource.JarMapping;
|
import net.md_5.specialsource.JarMapping;
|
||||||
import net.md_5.specialsource.provider.ClassLoaderProvider;
|
import net.md_5.specialsource.provider.ClassLoaderProvider;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import io.izzel.arclight.mod.util.remapper.generated.ArclightReflectionHandler;
|
import io.izzel.arclight.mod.util.remapper.generated.ArclightReflectionHandler;
|
||||||
import io.izzel.arclight.util.Unsafe;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
import net.md_5.specialsource.JarMapping;
|
import net.md_5.specialsource.JarMapping;
|
||||||
import net.md_5.specialsource.JarRemapper;
|
import net.md_5.specialsource.JarRemapper;
|
||||||
import net.md_5.specialsource.RemappingClassAdapter;
|
import net.md_5.specialsource.RemappingClassAdapter;
|
||||||
|
|
|
@ -1,32 +1,25 @@
|
||||||
package io.izzel.arclight.server;
|
package io.izzel.arclight.server;
|
||||||
|
|
||||||
import com.google.common.io.ByteStreams;
|
import io.izzel.arclight.api.Unsafe;
|
||||||
|
import io.izzel.arclight.forgeinstaller.ForgeInstaller;
|
||||||
import io.izzel.arclight.mod.util.BukkitOptionParser;
|
import io.izzel.arclight.mod.util.BukkitOptionParser;
|
||||||
import io.izzel.arclight.mod.util.remapper.ArclightRemapper;
|
import io.izzel.arclight.mod.util.remapper.ArclightRemapper;
|
||||||
import io.izzel.arclight.util.EnumHelper;
|
import io.izzel.arclight.util.EnumHelper;
|
||||||
import io.izzel.arclight.util.Unsafe;
|
|
||||||
import joptsimple.OptionSet;
|
import joptsimple.OptionSet;
|
||||||
import net.minecraftforge.server.ServerMain;
|
import net.minecraftforge.server.ServerMain;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.fusesource.jansi.AnsiConsole;
|
import org.fusesource.jansi.AnsiConsole;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.net.URL;
|
||||||
import java.io.InputStream;
|
import java.net.URLClassLoader;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.file.FileVisitResult;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.nio.file.SimpleFileVisitor;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
if (Files.notExists(Paths.get("forge-1.14.4-28.2.0.jar"))) {
|
ForgeInstaller.install();
|
||||||
System.err.println("Install forge 1.14.4-28.2.0 before launching Arclight.");
|
for (URL url : ((URLClassLoader) Main.class.getClassLoader()).getURLs()) {
|
||||||
return;
|
System.out.println(url);
|
||||||
}
|
}
|
||||||
try { // Java 9 & Java 兼容性
|
try { // Java 9 & Java 兼容性
|
||||||
int javaVersion = (int) Float.parseFloat(System.getProperty("java.class.version"));
|
int javaVersion = (int) Float.parseFloat(System.getProperty("java.class.version"));
|
||||||
|
@ -40,114 +33,34 @@ public class Main {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (Files.notExists(Paths.get("./libraries/net/minecraftforge/eventbus/2.0.0-milestone.1/eventbus-2.0.0-milestone.1-service.jar"))) {
|
OptionSet options = new BukkitOptionParser().parse(args);
|
||||||
Path folder = Paths.get("./libraries/net/minecraftforge/eventbus");
|
String jline_UnsupportedTerminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
|
||||||
Files.walkFileTree(folder, new SimpleFileVisitor<Path>() {
|
String jline_terminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 't', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
|
||||||
@Override
|
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
|
||||||
Files.delete(file);
|
|
||||||
return FileVisitResult.CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
boolean useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal));
|
||||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
|
||||||
Files.delete(dir);
|
if (options.has("nojline")) {
|
||||||
return FileVisitResult.CONTINUE;
|
System.setProperty("user.language", "en");
|
||||||
}
|
useJline = false;
|
||||||
});
|
|
||||||
throw new Exception();
|
|
||||||
}
|
}
|
||||||
Class.forName("org.spongepowered.asm.mixin.Mixins", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.objectweb.asm.util.CheckClassAdapter", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.objectweb.asm.tree.analysis.AnalyzerException", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("net.md_5.bungee.api.ChatColor", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.yaml.snakeyaml.Yaml", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.sqlite.JDBC", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("com.mysql.jdbc.Driver", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.apache.commons.lang.Validate", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("jline.Terminal", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.json.simple.JSONObject", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("org.apache.logging.log4j.jul.LogManager", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("net.minecraftforge.eventbus.EventBus", false, ClassLoader.getSystemClassLoader());
|
|
||||||
Class.forName("net.md_5.specialsource.JarRemapper", false, ClassLoader.getSystemClassLoader());
|
|
||||||
try {
|
|
||||||
OptionSet options = new BukkitOptionParser().parse(args);
|
|
||||||
String jline_UnsupportedTerminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
|
|
||||||
String jline_terminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 't', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
|
|
||||||
|
|
||||||
boolean useJline = !(jline_UnsupportedTerminal).equals(System.getProperty(jline_terminal));
|
if (useJline) {
|
||||||
|
AnsiConsole.systemInstall();
|
||||||
if (options.has("nojline")) {
|
} else {
|
||||||
System.setProperty("user.language", "en");
|
System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName());
|
||||||
useJline = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useJline) {
|
|
||||||
AnsiConsole.systemInstall();
|
|
||||||
} else {
|
|
||||||
System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
|
|
||||||
System.setProperty("log4j.jul.LoggerAdapter", "io.izzel.arclight.mod.util.ArclightLoggerAdapter");
|
|
||||||
LogManager.getLogger("Arclight").info("Loading mappings ...");
|
|
||||||
Objects.requireNonNull(ArclightRemapper.INSTANCE);
|
|
||||||
ServerMain.main(args);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.err.println("Fail to launch Arclight.");
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("FATAL ERROR: The libraries required to launch Arclight are missing, extracting...");
|
e.printStackTrace();
|
||||||
extract("org.spongepowered:mixin:0.8");
|
|
||||||
extract("org.ow2.asm:asm-util:6.2");
|
|
||||||
extract("org.ow2.asm:asm-analysis:6.2");
|
|
||||||
extract("org.yaml:snakeyaml:1.23");
|
|
||||||
extract("net.md-5:bungeecord-chat:1.13-SNAPSHOT");
|
|
||||||
extract("org.xerial:sqlite-jdbc:3.28.0");
|
|
||||||
extract("mysql:mysql-connector-java:5.1.47");
|
|
||||||
extract("commons-lang:commons-lang:2.6");
|
|
||||||
extract("jline:jline:2.12.1");
|
|
||||||
extract("com.googlecode.json-simple:json-simple:1.1.1");
|
|
||||||
extract("org.apache.logging.log4j:log4j-jul:2.11.2");
|
|
||||||
extract("net.md-5:SpecialSource:1.8.6");
|
|
||||||
extract("net.minecraftforge:eventbus:2.0.0-milestone.1:service");
|
|
||||||
System.out.println("Please RESTART the server.");
|
|
||||||
}
|
}
|
||||||
}
|
try {
|
||||||
|
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
|
||||||
private static void extract(String artifact) throws Throwable {
|
System.setProperty("log4j.jul.LoggerAdapter", "io.izzel.arclight.mod.util.ArclightLoggerAdapter");
|
||||||
String[] split = artifact.split(":");
|
LogManager.getLogger("Arclight").info("Loading mappings ...");
|
||||||
if (split.length == 3) {
|
Objects.requireNonNull(ArclightRemapper.INSTANCE);
|
||||||
String jar = String.format("/%s-%s.jar", split[1], split[2]);
|
ServerMain.main(args);
|
||||||
String path = split[0].replace('.', '/') + "/" +
|
} catch (Exception e) {
|
||||||
split[1] + "/" + split[2] + jar;
|
e.printStackTrace();
|
||||||
extract("libs" + jar, "./libraries/" + path);
|
System.err.println("Fail to launch Arclight.");
|
||||||
System.out.println("Extracted " + artifact);
|
|
||||||
} else if (split.length == 4) {
|
|
||||||
String jar = String.format("/%s-%s-%s.jar", split[1], split[2], split[3]);
|
|
||||||
String path = split[0].replace('.', '/') + "/" +
|
|
||||||
split[1] + "/" + split[2] + jar;
|
|
||||||
extract("libs" + jar, "./libraries/" + path);
|
|
||||||
System.out.println("Extracted " + artifact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void extract(String name, String target) throws Throwable {
|
|
||||||
Path path = Paths.get(target);
|
|
||||||
if (Files.notExists(path)) {
|
|
||||||
Files.createDirectories(path.getParent());
|
|
||||||
Files.createFile(path);
|
|
||||||
InputStream stream = Main.class.getResourceAsStream("/" + name);
|
|
||||||
if (stream != null) {
|
|
||||||
OutputStream outputStream = Files.newOutputStream(Paths.get(target));
|
|
||||||
ByteStreams.copy(stream, outputStream);
|
|
||||||
stream.close();
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package io.izzel.arclight.util;
|
package io.izzel.arclight.util;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import io.izzel.arclight.api.Unsafe;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
|
|
|
@ -321,6 +321,7 @@
|
||||||
"server.management.PlayerInteractionManagerMixin",
|
"server.management.PlayerInteractionManagerMixin",
|
||||||
"server.management.PlayerListMixin",
|
"server.management.PlayerListMixin",
|
||||||
"server.management.UserListMixin",
|
"server.management.UserListMixin",
|
||||||
|
"state.EnumPropertyMixin",
|
||||||
"state.IntegerPropertyMixin",
|
"state.IntegerPropertyMixin",
|
||||||
"stats.StatisticsManagerMixin",
|
"stats.StatisticsManagerMixin",
|
||||||
"tags.NetworkTagCollectionMixin",
|
"tags.NetworkTagCollectionMixin",
|
||||||
|
|
12
forge-installer/build.gradle
Normal file
12
forge-installer/build.gradle
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile 'com.google.code.gson:gson:2.8.0'
|
||||||
|
compile project(':arclight-api')
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package io.izzel.arclight.forgeinstaller;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import io.izzel.arclight.api.Unsafe;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
|
public class ForgeInstaller {
|
||||||
|
|
||||||
|
private static final String INFO = "Download mirror service by BMCLAPI: https://bmclapidoc.bangbang93.com\n" +
|
||||||
|
"Support MinecraftForge project at https://www.patreon.com/LexManos/";
|
||||||
|
private static final String INSTALLER_URL = "https://bmclapi2.bangbang93.com/maven/net/minecraftforge/forge/%s-%s/forge-%s-%s-installer.jar";
|
||||||
|
private static final String BMCL_API = "https://bmclapi2.bangbang93.com/maven/";
|
||||||
|
private static final String SERVER_URL = "https://bmclapi2.bangbang93.com/version/%s/server";
|
||||||
|
|
||||||
|
public static void install() throws Throwable {
|
||||||
|
InputStream stream = ForgeInstaller.class.getResourceAsStream("/META-INF/installer.json");
|
||||||
|
InstallInfo installInfo = new Gson().fromJson(new InputStreamReader(stream), InstallInfo.class);
|
||||||
|
Path path = Paths.get(String.format("forge-%s-%s.jar", installInfo.installer.minecraft, installInfo.installer.forge));
|
||||||
|
if (!Files.exists(path)) {
|
||||||
|
System.out.println(INFO);
|
||||||
|
Thread.sleep(5000);
|
||||||
|
download(installInfo);
|
||||||
|
ProcessBuilder builder = new ProcessBuilder();
|
||||||
|
builder.command("java", "-jar", String.format("forge-%s-%s-installer.jar", installInfo.installer.minecraft, installInfo.installer.forge), "--installServer", ".");
|
||||||
|
builder.inheritIO();
|
||||||
|
Process process = builder.start();
|
||||||
|
process.waitFor();
|
||||||
|
}
|
||||||
|
classpath(path, installInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void classpath(Path path, InstallInfo installInfo) throws Throwable {
|
||||||
|
JarFile jarFile = new JarFile(path.toFile());
|
||||||
|
Manifest manifest = jarFile.getManifest();
|
||||||
|
String[] split = manifest.getMainAttributes().getValue("Class-Path").split(" ");
|
||||||
|
for (String s : split) {
|
||||||
|
if (s.contains("eventbus-1.0.0-service")) continue;
|
||||||
|
addToPath(Paths.get(s));
|
||||||
|
}
|
||||||
|
for (String library : installInfo.libraries) {
|
||||||
|
addToPath(Paths.get("libraries", mavenToPath(library)));
|
||||||
|
}
|
||||||
|
addToPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addToPath(Path path) throws Throwable {
|
||||||
|
ClassLoader loader = ForgeInstaller.class.getClassLoader();
|
||||||
|
Field ucpField = loader.getClass().getDeclaredField("ucp");
|
||||||
|
long offset = Unsafe.objectFieldOffset(ucpField);
|
||||||
|
Object ucp = Unsafe.getObject(loader, offset);
|
||||||
|
Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class);
|
||||||
|
Unsafe.lookup().unreflect(method).invoke(ucp, path.toUri().toURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void download(InstallInfo info) throws Exception {
|
||||||
|
SimpleDownloader downloader = new SimpleDownloader();
|
||||||
|
String format = String.format(INSTALLER_URL, info.installer.minecraft, info.installer.forge, info.installer.minecraft, info.installer.forge);
|
||||||
|
String dist = String.format("forge-%s-%s-installer.jar", info.installer.minecraft, info.installer.forge);
|
||||||
|
downloader.download(format, dist, null,
|
||||||
|
path -> processInstaller(path, downloader));
|
||||||
|
for (String library : info.libraries) {
|
||||||
|
String path = mavenToPath(library);
|
||||||
|
downloader.downloadMaven(path);
|
||||||
|
}
|
||||||
|
downloader.download(String.format(SERVER_URL, info.installer.minecraft), String.format("minecraft_server.%s.jar", info.installer.minecraft), null);
|
||||||
|
if (!downloader.awaitTermination()) {
|
||||||
|
Files.deleteIfExists(Paths.get(dist));
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processInstaller(Path path, SimpleDownloader downloader) {
|
||||||
|
try {
|
||||||
|
FileSystem system = FileSystems.newFileSystem(path, null);
|
||||||
|
Set<String> set = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||||
|
Path profile = system.getPath("install_profile.json");
|
||||||
|
downloadLibraries(downloader, profile, set);
|
||||||
|
Path version = system.getPath("version.json");
|
||||||
|
downloadLibraries(downloader, version, set);
|
||||||
|
system.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void downloadLibraries(SimpleDownloader downloader, Path path, Set<String> set) throws IOException {
|
||||||
|
JsonArray array = new JsonParser().parse(Files.newBufferedReader(path)).getAsJsonObject().getAsJsonArray("libraries");
|
||||||
|
for (JsonElement element : array) {
|
||||||
|
String name = element.getAsJsonObject().get("name").getAsString();
|
||||||
|
if (!set.add(name)) continue;
|
||||||
|
String libPath = mavenToPath(name);
|
||||||
|
JsonObject artifact = element.getAsJsonObject().getAsJsonObject("downloads").getAsJsonObject("artifact");
|
||||||
|
String url = artifact.get("url").getAsString();
|
||||||
|
if (url == null || url.trim().isEmpty()) continue;
|
||||||
|
String hash = artifact.get("sha1").getAsString();
|
||||||
|
downloader.download(BMCL_API + libPath, "libraries/" + libPath, hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String mavenToPath(String maven) {
|
||||||
|
String type;
|
||||||
|
if (maven.matches(".*@\\w+$")) {
|
||||||
|
int i = maven.lastIndexOf('@');
|
||||||
|
type = maven.substring(i + 1);
|
||||||
|
maven = maven.substring(0, i);
|
||||||
|
} else {
|
||||||
|
type = "jar";
|
||||||
|
}
|
||||||
|
String[] arr = maven.split(":");
|
||||||
|
if (arr.length == 3) {
|
||||||
|
String pkg = arr[0].replace('.', '/');
|
||||||
|
return String.format("%s/%s/%s/%s-%s.%s", pkg, arr[1], arr[2], arr[1], arr[2], type);
|
||||||
|
} else if (arr.length == 4) {
|
||||||
|
String pkg = arr[0].replace('.', '/');
|
||||||
|
return String.format("%s/%s/%s/%s-%s-%s.%s", pkg, arr[1], arr[2], arr[1], arr[2], arr[3], type);
|
||||||
|
} else throw new RuntimeException("Wrong maven coordinate " + maven);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package io.izzel.arclight.forgeinstaller;
|
||||||
|
|
||||||
|
public class InstallInfo {
|
||||||
|
|
||||||
|
public Installer installer;
|
||||||
|
public String[] libraries;
|
||||||
|
|
||||||
|
public static class Installer {
|
||||||
|
|
||||||
|
public String minecraft;
|
||||||
|
public String forge;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package io.izzel.arclight.forgeinstaller;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SimpleDownloader {
|
||||||
|
|
||||||
|
private static final String[] MAVEN_REPO = {
|
||||||
|
"https://repo.spongepowered.org/maven",
|
||||||
|
"https://oss.sonatype.org/content/repositories/snapshots/",
|
||||||
|
"https://hub.spigotmc.org/nexus/content/repositories/snapshots/",
|
||||||
|
"https://bmclapi2.bangbang93.com/maven/",
|
||||||
|
"https://maven.aliyun.com/repository/public/"
|
||||||
|
};
|
||||||
|
|
||||||
|
private final CompletableFuture<Void> future = new CompletableFuture<>();
|
||||||
|
private final AtomicInteger integer = new AtomicInteger(0);
|
||||||
|
private final ExecutorService service = Executors.newFixedThreadPool(8);
|
||||||
|
private final AtomicBoolean error = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public void run(Callable<Boolean> callable) {
|
||||||
|
integer.incrementAndGet();
|
||||||
|
CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
return callable.call();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
System.err.println(e.toString());
|
||||||
|
error.compareAndSet(false, true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, service).thenAccept(b -> {
|
||||||
|
int remain = integer.decrementAndGet();
|
||||||
|
if (remain == 0) {
|
||||||
|
if (error.get())
|
||||||
|
future.completeExceptionally(new Exception());
|
||||||
|
else
|
||||||
|
future.complete(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void download(String url, String dist, String hash) {
|
||||||
|
download(url, dist, hash, path -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void download(String url, String dist, String hash, Consumer<Path> onComplete) {
|
||||||
|
run(() -> downloadFile(url, dist, hash, 5, onComplete));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void downloadMaven(String path) {
|
||||||
|
run(() -> {
|
||||||
|
for (String s : MAVEN_REPO) {
|
||||||
|
if (downloadFile(s + path, "libraries/" + path, null, 3, p -> {})) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean awaitTermination() {
|
||||||
|
try {
|
||||||
|
future.join();
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
service.shutdownNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean downloadFile(String url, String dist, String hash, int retry, Consumer<Path> onComplete) throws Exception {
|
||||||
|
if (retry <= 0) return false;
|
||||||
|
try {
|
||||||
|
Path path = Paths.get(dist);
|
||||||
|
if (Files.exists(path) && hash != null) {
|
||||||
|
String hash1 = hash(path);
|
||||||
|
if (hash.equals(hash1)) {
|
||||||
|
onComplete.accept(path);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Files.delete(path);
|
||||||
|
throw new Exception("Checksum failed: expect " + hash + " but found " + hash1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!Files.exists(path) && path.getParent() != null) {
|
||||||
|
Files.createDirectories(path.getParent());
|
||||||
|
}
|
||||||
|
System.out.println("Downloading " + url);
|
||||||
|
InputStream stream = redirect(new URL(url));
|
||||||
|
Files.copy(stream, path, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
stream.close();
|
||||||
|
if (hash != null) {
|
||||||
|
String hash1 = hash(path);
|
||||||
|
if (!hash.equals(hash1)) {
|
||||||
|
Files.delete(path);
|
||||||
|
throw new Exception("Checksum failed: expect " + hash + " but found " + hash1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onComplete.accept(path);
|
||||||
|
return true;
|
||||||
|
} catch (FileNotFoundException | SocketTimeoutException e) {
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
run(() -> downloadFile(url, dist, hash, retry - 1, onComplete));
|
||||||
|
System.err.println("Failed to download file " + dist);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static InputStream redirect(URL url) throws Exception {
|
||||||
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
|
connection.setInstanceFollowRedirects(false);
|
||||||
|
connection.setReadTimeout(15000);
|
||||||
|
connection.setConnectTimeout(15000);
|
||||||
|
switch (connection.getResponseCode()) {
|
||||||
|
case HttpURLConnection.HTTP_MOVED_PERM:
|
||||||
|
case HttpURLConnection.HTTP_MOVED_TEMP:
|
||||||
|
String location = URLDecoder.decode(connection.getHeaderField("Location"), "UTF-8");
|
||||||
|
return redirect(new URL(url, location));
|
||||||
|
case HttpURLConnection.HTTP_FORBIDDEN:
|
||||||
|
case HttpURLConnection.HTTP_NOT_FOUND:
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
return connection.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String SHA_PAD = String.format("%040d", 0);
|
||||||
|
|
||||||
|
private static String hash(Path path) throws Exception {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||||
|
String hash = new BigInteger(1, digest.digest(Files.readAllBytes(path))).toString(16);
|
||||||
|
return (SHA_PAD + hash).substring(hash.length());
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,4 +2,7 @@ rootProject.name = 'arclight'
|
||||||
include 'arclight-coremod'
|
include 'arclight-coremod'
|
||||||
include 'scripts'
|
include 'scripts'
|
||||||
include 'arclight-testplugin'
|
include 'arclight-testplugin'
|
||||||
|
include 'arclight-common'
|
||||||
|
include 'forge-installer'
|
||||||
|
include 'arclight-api'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user