diff --git a/build.gradle b/build.gradle index f9b880c..0646fd9 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,10 @@ repositories { name = 'Forge' //For ModLauncher url = 'http://files.minecraftforge.net/maven/' } + maven { + name = 'SpongePowered' + url = 'http://repo.spongepowered.org/maven' + } } configurations { @@ -59,7 +63,15 @@ dependencies { shade 'net.sf.jopt-simple:jopt-simple:5.0.4' shade 'org.apache.logging.log4j:log4j-api:2.11.0' shade 'org.apache.logging.log4j:log4j-core:2.11.0' - shade 'cpw.mods:modlauncher:0.1.0-rc.3' + + shade ('net.minecraft:launchwrapper:1.12') { + transitive = false + } + + shade('org.spongepowered:mixin:0.7.11-SNAPSHOT') { + exclude module: 'launchwrapper' + exclude module: 'guava' + } shade 'org.apache.commons:commons-lang3:3.5' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 91ca28c..f6b961f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a95009c..1f47535 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon May 14 11:21:37 BST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.7-all.zip diff --git a/src/main/java/net/fabricmc/loom/AbstractPlugin.java b/src/main/java/net/fabricmc/loom/AbstractPlugin.java index 21a39ce..1b6426e 100644 --- a/src/main/java/net/fabricmc/loom/AbstractPlugin.java +++ b/src/main/java/net/fabricmc/loom/AbstractPlugin.java @@ -87,6 +87,36 @@ public class AbstractPlugin implements Plugin { configureIDEs(); configureCompile(); + Map> taskMap = project.getAllTasks(true); + for (Map.Entry> entry : taskMap.entrySet()) { + Project project = entry.getKey(); + Set taskSet = entry.getValue(); + for (Task task : taskSet) { + if (task instanceof JavaCompile + && !(task.getName().contains("Test")) && !(task.getName().contains("test"))) { + JavaCompile javaCompileTask = (JavaCompile) task; + javaCompileTask.doFirst(task1 -> { + project.getLogger().lifecycle(":setting java compiler args"); + try { + javaCompileTask.getClasspath().add(target.files(this.getClass().getProtectionDomain().getCodeSource().getLocation())); + + javaCompileTask.getOptions().getCompilerArgs().add("-AinMapFilePomfMojang=" + Constants.MAPPINGS_TINY.get(extension).getCanonicalPath()); + javaCompileTask.getOptions().getCompilerArgs().add("-AoutMapFilePomfMojang=" + Constants.MAPPINGS_MIXIN_EXPORT.get(extension).getCanonicalPath()); + if(extension.refmapName == null || extension.refmapName.isEmpty()){ + project.getLogger().error("Could not find refmap definition, will be using default name: " + project.getName() + "-refmap.json"); + extension.refmapName = project.getName() + "-refmap.json"; + } + javaCompileTask.getOptions().getCompilerArgs().add("-AoutRefMapFile=" + new File(javaCompileTask.getDestinationDir(), extension.refmapName).getCanonicalPath()); + javaCompileTask.getOptions().getCompilerArgs().add("-AdefaultObfuscationEnv=pomf:mojang"); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + } + + } + } /** @@ -187,6 +217,11 @@ public class AbstractPlugin implements Plugin { mavenArtifactRepository.setUrl("https://maven.modmuss50.me/"); }); + project1.getRepositories().maven(mavenArtifactRepository -> { + mavenArtifactRepository.setName("SpongePowered"); + mavenArtifactRepository.setUrl("http://repo.spongepowered.org/maven"); + }); + project1.getRepositories().maven(mavenArtifactRepository -> { mavenArtifactRepository.setName("Mojang"); mavenArtifactRepository.setUrl("https://libraries.minecraft.net/"); diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index c4250da..2b91e66 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -33,6 +33,7 @@ public class LoomGradleExtension { public String runDir = "run"; public String omlVersion; public String pomfVersion; + public String refmapName; public boolean skipPrebake = false; public boolean localMappings = false; diff --git a/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java b/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java new file mode 100644 index 0000000..e63db8e --- /dev/null +++ b/src/main/java/net/fabricmc/loom/mixin/MixinMappingProviderTiny.java @@ -0,0 +1,144 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016 FabricMC + * + * 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 net.fabricmc.loom.mixin; + +import net.fabricmc.tinyremapper.TinyUtils; +import org.spongepowered.asm.obfuscation.mapping.common.MappingField; +import org.spongepowered.asm.obfuscation.mapping.common.MappingMethod; +import org.spongepowered.tools.obfuscation.mapping.common.MappingProvider; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public class MixinMappingProviderTiny extends MappingProvider { + private final String from, to; + + public MixinMappingProviderTiny(Messager messager, Filer filer, String from, String to) { + super(messager, filer); + this.from = from; + this.to = to; + } + + private static final String[] removeFirst(String[] src, int count) { + if (count >= src.length) { + return new String[0]; + } else { + String[] out = new String[src.length - count]; + System.arraycopy(src, count, out, 0, out.length); + return out; + } + } + + @Override + public MappingMethod getMethodMapping(MappingMethod method) { + System.out.println("processing " + method.getName() + method.getDesc()); + + MappingMethod mapped = this.methodMap.get(method); + if (mapped != null) + return mapped; + + try { + Class c = this.getClass().getClassLoader().loadClass(method.getOwner().replace('/', '.')); + if (c == null || c == Object.class) { + return null; + } + + for (Class cc : c.getInterfaces()) { + mapped = getMethodMapping(method.move(cc.getName().replace('.', '/'))); + if (mapped != null) + return mapped; + } + + if (c.getSuperclass() != null) { + mapped = getMethodMapping(method.move(c.getSuperclass().getName().replace('.', '/'))); + if (mapped != null) + return mapped; + } + + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public MappingField getFieldMapping(MappingField field) { + System.out.println("processing " + field.getOwner() + "/" + field.getName() + field.getDesc()); + + MappingField mapped = this.fieldMap.get(field); + if (mapped != null) + return mapped; + + try { + Class c = this.getClass().getClassLoader().loadClass(field.getOwner().replace('/', '.')); + if (c == null || c == Object.class) { + return null; + } + + for (Class cc : c.getInterfaces()) { + mapped = getFieldMapping(field.move(cc.getName().replace('.', '/'))); + if (mapped != null) + return mapped; + } + + if (c.getSuperclass() != null) { + mapped = getFieldMapping(field.move(c.getSuperclass().getName().replace('.', '/'))); + if (mapped != null) + return mapped; + } + + return null; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + // TODO: Unify with tiny-remapper + + @Override + public void read(File input) throws IOException { + BufferedReader reader = Files.newBufferedReader(input.toPath()); + + TinyUtils.read(reader, from, to, (classFrom, classTo) -> { + classMap.put(classFrom, classTo); + }, (fieldFrom, fieldTo) -> { + fieldMap.put( + new MappingField(fieldFrom.owner, fieldFrom.name, fieldFrom.desc), + new MappingField(fieldTo.owner, fieldTo.name, fieldTo.desc) + ); + }, (methodFrom, methodTo) -> { + methodMap.put( + new MappingMethod(methodFrom.owner, methodFrom.name, methodFrom.desc), + new MappingMethod(methodTo.owner, methodTo.name, methodTo.desc) + ); + }); + } +} diff --git a/src/main/java/net/fabricmc/loom/mixin/MixinMappingWriterTiny.java b/src/main/java/net/fabricmc/loom/mixin/MixinMappingWriterTiny.java new file mode 100644 index 0000000..0ea4a47 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/mixin/MixinMappingWriterTiny.java @@ -0,0 +1,75 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016 FabricMC + * + * 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 net.fabricmc.loom.mixin; + +import org.spongepowered.asm.obfuscation.mapping.common.MappingField; +import org.spongepowered.asm.obfuscation.mapping.common.MappingMethod; +import org.spongepowered.tools.obfuscation.ObfuscationType; +import org.spongepowered.tools.obfuscation.mapping.IMappingConsumer; +import org.spongepowered.tools.obfuscation.mapping.common.MappingWriter; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * Created by asie on 10/9/16. + */ +public class MixinMappingWriterTiny extends MappingWriter { + public MixinMappingWriterTiny(Messager messager, Filer filer) { + super(messager, filer); + } + + @Override + public void write(String output, ObfuscationType type, IMappingConsumer.MappingSet fields, IMappingConsumer.MappingSet methods) { + if (output != null) { + PrintWriter writer = null; + + try { + String from = type.getKey().split(":")[0]; + String to = type.getKey().split(":")[1]; + + writer = this.openFileWriter(output, type + " output TinyMappings"); + writer.println(String.format("v1\t%s\t%s", from, to)); + for (IMappingConsumer.MappingSet.Pair pair : fields) { + writer.println(String.format("FIELD\t%s\t%s\t%s\t%s", pair.from.getOwner(), pair.from.getDesc(), pair.from.getSimpleName(), pair.to.getSimpleName())); + } + for (IMappingConsumer.MappingSet.Pair pair : methods) { + writer.println(String.format("METHOD\t%s\t%s\t%s\t%s", pair.from.getOwner(), pair.from.getDesc(), pair.from.getSimpleName(), pair.to.getSimpleName())); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (Exception e) { + } + } + } + } + } +} diff --git a/src/main/java/net/fabricmc/loom/mixin/MixinServiceGradle.java b/src/main/java/net/fabricmc/loom/mixin/MixinServiceGradle.java new file mode 100644 index 0000000..d38c8a5 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/mixin/MixinServiceGradle.java @@ -0,0 +1,77 @@ +package net.fabricmc.loom.mixin; + +import com.google.common.io.ByteStreams; +import net.fabricmc.loom.task.ProcessModsTask; +import net.fabricmc.loom.util.proccessing.MixinPrebaker; +import org.spongepowered.asm.service.IClassBytecodeProvider; +import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +public class MixinServiceGradle extends MixinServiceLaunchWrapper implements IClassBytecodeProvider { + + private static List jars = new ArrayList<>(); + + + @Override + public String getName() { + return "OpenModLoaderGradle"; + } + + @Override + public InputStream getResourceAsStream(String name) { + if(MixinPrebaker.jarFileCache.containsKey(name)){ + return MixinPrebaker.jarFileCache.get(name); + } + for(JarFile file : jars){ + ZipEntry entry = file.getEntry(name); + if(entry != null){ + try { + InputStream stream = file.getInputStream(entry); + return stream; + } catch (IOException e) { + throw new RuntimeException("Failed to read mod file", e); + } + } + } + return super.getResourceAsStream(name); + } + + public static void setupModFiles(Set mods, File minecraft) throws IOException { + jars.clear(); + for(File mod : mods){ + JarFile jarFile = new JarFile(mod); + jars.add(jarFile); + } + jars.add(new JarFile(minecraft)); + } + + public static void addMCDeps(Set deps, Object object) throws IOException { + for(File mod : deps){ + JarFile jarFile = new JarFile(mod); + jars.add(jarFile); + ProcessModsTask.addFile(mod, object); + } + } + + @Override + public IClassBytecodeProvider getBytecodeProvider() { + return this; + } + + public byte[] getClassBytes(String name, String transformedName) throws IOException { + InputStream inputStream = getResourceAsStream(name.replace(".", "/") + ".class"); + byte[] classBytes = ByteStreams.toByteArray(inputStream); + if(classBytes == null){ + return super.getClassBytes(name, transformedName); + } + return classBytes; + } +} diff --git a/src/main/java/net/fabricmc/loom/mixin/ObfuscationEnvironmentFabric.java b/src/main/java/net/fabricmc/loom/mixin/ObfuscationEnvironmentFabric.java new file mode 100644 index 0000000..70318e4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/mixin/ObfuscationEnvironmentFabric.java @@ -0,0 +1,51 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016 FabricMC + * + * 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 net.fabricmc.loom.mixin; + +import org.spongepowered.tools.obfuscation.ObfuscationEnvironment; +import org.spongepowered.tools.obfuscation.ObfuscationType; +import org.spongepowered.tools.obfuscation.mapping.IMappingProvider; +import org.spongepowered.tools.obfuscation.mapping.IMappingWriter; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; + +public class ObfuscationEnvironmentFabric extends ObfuscationEnvironment { + protected ObfuscationEnvironmentFabric(ObfuscationType type) { + super(type); + } + + @Override + protected IMappingProvider getMappingProvider(Messager messager, Filer filer) { + String from = type.getKey().split(":")[0]; + String to = type.getKey().split(":")[1]; + return new MixinMappingProviderTiny(messager, filer, from, to); + } + + @Override + protected IMappingWriter getMappingWriter(Messager messager, Filer filer) { + return new MixinMappingWriterTiny(messager, filer); + } +} diff --git a/src/main/java/net/fabricmc/loom/mixin/ObfuscationServiceFabric.java b/src/main/java/net/fabricmc/loom/mixin/ObfuscationServiceFabric.java new file mode 100644 index 0000000..b95f719 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/mixin/ObfuscationServiceFabric.java @@ -0,0 +1,75 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016 FabricMC + * + * 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 net.fabricmc.loom.mixin; + +import com.google.common.collect.ImmutableSet; +import org.apache.commons.lang3.StringUtils; +import org.spongepowered.tools.obfuscation.service.IObfuscationService; +import org.spongepowered.tools.obfuscation.service.ObfuscationTypeDescriptor; + +import java.util.Collection; +import java.util.Set; + +public class ObfuscationServiceFabric implements IObfuscationService { + public static final String IN_MAP_FILE = "inMapFile"; + public static final String IN_MAP_EXTRA_FILES = "inMapExtraFiles"; + public static final String OUT_MAP_FILE = "outMapFile"; + + private String asSuffixed(String arg, String from, String to) { + return arg + StringUtils.capitalize(from) + StringUtils.capitalize(to); + } + + private ObfuscationTypeDescriptor createObfuscationType(String from, String to) { + return new ObfuscationTypeDescriptor( + from + ":" + to, + asSuffixed(ObfuscationServiceFabric.IN_MAP_FILE, from, to), + asSuffixed(ObfuscationServiceFabric.IN_MAP_EXTRA_FILES, from, to), + asSuffixed(ObfuscationServiceFabric.OUT_MAP_FILE, from, to), + ObfuscationEnvironmentFabric.class + ); + } + + private void addSupportedOptions(ImmutableSet.Builder builder, String from, String to) { + builder.add(asSuffixed(ObfuscationServiceFabric.IN_MAP_FILE, from, to)); + builder.add(asSuffixed(ObfuscationServiceFabric.IN_MAP_EXTRA_FILES, from, to)); + builder.add(asSuffixed(ObfuscationServiceFabric.OUT_MAP_FILE, from, to)); + } + + @Override + public Set getSupportedOptions() { + ImmutableSet.Builder builder = new ImmutableSet.Builder(); + addSupportedOptions(builder, "mojang", "pomf"); + addSupportedOptions(builder, "pomf", "mojang"); + return builder.build(); + } + + @Override + public Collection getObfuscationTypes() { + return ImmutableSet.of( + createObfuscationType("mojang", "pomf"), + createObfuscationType("pomf", "mojang") + ); + } +} diff --git a/src/main/java/net/fabricmc/loom/task/GenIdeaProjectTask.java b/src/main/java/net/fabricmc/loom/task/GenIdeaProjectTask.java index ee13c7c..eb47cd5 100644 --- a/src/main/java/net/fabricmc/loom/task/GenIdeaProjectTask.java +++ b/src/main/java/net/fabricmc/loom/task/GenIdeaProjectTask.java @@ -135,22 +135,22 @@ public class GenIdeaProjectTask extends DefaultTask { } IdeaRunConfig ideaClient = new IdeaRunConfig(); - ideaClient.mainClass = "cpw.mods.modlauncher.Launcher"; + ideaClient.mainClass = "net.minecraft.launchwrapper.Launch"; ideaClient.projectName = project.getName(); ideaClient.configName = "Minecraft Client"; ideaClient.runDir = "file://$PROJECT_DIR$/" + extension.runDir; ideaClient.vmArgs = "-Djava.library.path=" + Constants.MINECRAFT_NATIVES.get(extension).getAbsolutePath() + " -Doml.development=true"; - ideaClient.programArgs = "--launchTarget oml --accessToken not_a_real_token --version " + extension.version + " --assetIndex " + version.assetIndex.id + " --assetsDir " + new File(extension.getUserCache(), "assets-" + extension.version).getAbsolutePath(); + ideaClient.programArgs = "--tweakClass com.openmodloader.loader.launch.OpenClientTweaker --assetIndex " + version.assetIndex.id + " --assetsDir " + new File(extension.getUserCache(), "assets-" + extension.version).getAbsolutePath(); runManager.appendChild(ideaClient.genRuns(runManager)); IdeaRunConfig ideaServer = new IdeaRunConfig(); - ideaServer.mainClass = "com.openmodloader.loader.launch.ServerLauncher"; + ideaServer.mainClass = "net.minecraft.launchwrapper.Launch"; ideaServer.projectName = project.getName(); ideaServer.configName = "Minecraft Server"; ideaServer.runDir = "file://$PROJECT_DIR$/" + extension.runDir; ideaServer.vmArgs = "-Doml.development=true"; - ideaServer.programArgs = ""; + ideaServer.programArgs = "--tweakClass com.openmodloader.loader.launch.OpenServerTweaker"; runManager.appendChild(ideaServer.genRuns(runManager)); diff --git a/src/main/java/net/fabricmc/loom/task/MapJarsTask.java b/src/main/java/net/fabricmc/loom/task/MapJarsTask.java index c2451a8..28ed3c3 100644 --- a/src/main/java/net/fabricmc/loom/task/MapJarsTask.java +++ b/src/main/java/net/fabricmc/loom/task/MapJarsTask.java @@ -49,7 +49,7 @@ public class MapJarsTask extends DefaultTask { @TaskAction public void mapJars() throws IOException, MappingParseException { LoomGradleExtension extension = this.getProject().getExtensions().getByType(LoomGradleExtension.class); - if (!Constants.MINECRAFT_MAPPED_JAR.get(extension).exists() || extension.localMappings) { + if (!Constants.MINECRAFT_MAPPED_JAR.get(extension).exists() || extension.localMappings || true) { if(extension.localMappings && Constants.MINECRAFT_MAPPED_JAR.get(extension).exists()){ //Always remap the jar when using local mappings. Constants.MINECRAFT_MAPPED_JAR.get(extension).delete(); diff --git a/src/main/java/net/fabricmc/loom/task/ProcessModsTask.java b/src/main/java/net/fabricmc/loom/task/ProcessModsTask.java index b58e2eb..697662f 100644 --- a/src/main/java/net/fabricmc/loom/task/ProcessModsTask.java +++ b/src/main/java/net/fabricmc/loom/task/ProcessModsTask.java @@ -26,6 +26,7 @@ package net.fabricmc.loom.task; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.util.Constants; +import net.fabricmc.loom.util.proccessing.PreBakeMixins; import org.apache.commons.io.FileUtils; import org.gradle.api.DefaultTask; import org.gradle.api.artifacts.Configuration; @@ -56,10 +57,11 @@ public class ProcessModsTask extends DefaultTask { Constants.MINECRAFT_FINAL_JAR.get(extension).delete(); } if (mods.size() == 0 || extension.skipPrebake) { + getProject().getLogger().lifecycle(":skipping mixin prebake"); FileUtils.copyFile(Constants.MINECRAFT_MERGED_JAR.get(extension), Constants.MINECRAFT_MIXED_JAR.get(extension)); } else { -// downloadRequiredDeps(extension); - throw new UnsupportedOperationException("Mixin prebake isnt done yet"); + downloadRequiredDeps(extension); + new PreBakeMixins().proccess(getProject(), extension, mods); } } diff --git a/src/main/java/net/fabricmc/loom/task/RunClientTask.java b/src/main/java/net/fabricmc/loom/task/RunClientTask.java index 31b4bf5..2b4b31a 100644 --- a/src/main/java/net/fabricmc/loom/task/RunClientTask.java +++ b/src/main/java/net/fabricmc/loom/task/RunClientTask.java @@ -73,7 +73,7 @@ public class RunClientTask extends JavaExec { classpath(libs); - args("--launchTarget", "oml", "--accessToken", "NOT_A_TOKEN", "--version", extension.version, "--assetIndex", version.assetIndex.id, "--assetsDir", new File(extension.getUserCache(), "assets-" + extension.version).getAbsolutePath()); + args("--tweakClass", "com.openmodloader.loader.launch.OpenClientTweaker", "--assetIndex", version.assetIndex.id, "--assetsDir", new File(extension.getUserCache(), "assets-" + extension.version).getAbsolutePath()); setWorkingDir(new File(getProject().getRootDir(), "run")); diff --git a/src/main/java/net/fabricmc/loom/task/RunServerTask.java b/src/main/java/net/fabricmc/loom/task/RunServerTask.java index a9a734d..115a5f4 100644 --- a/src/main/java/net/fabricmc/loom/task/RunServerTask.java +++ b/src/main/java/net/fabricmc/loom/task/RunServerTask.java @@ -65,6 +65,8 @@ public class RunServerTask extends JavaExec { libs.add(Constants.MINECRAFT_FINAL_JAR.get(extension).getAbsolutePath()); classpath(libs); + args("--tweakClass", "com.openmodloader.loader.launch.OpenServerTweaker"); + setWorkingDir(new File(getProject().getRootDir(), "run")); super.exec(); @@ -72,7 +74,7 @@ public class RunServerTask extends JavaExec { @Override public String getMain() { - return "com.openmodloader.loader.launch.ServerLauncher"; + return "net.minecraft.launchwrapper.Launch"; } @Override diff --git a/src/main/java/net/fabricmc/loom/util/Constants.java b/src/main/java/net/fabricmc/loom/util/Constants.java index 63e2cdd..e9a150f 100644 --- a/src/main/java/net/fabricmc/loom/util/Constants.java +++ b/src/main/java/net/fabricmc/loom/util/Constants.java @@ -57,6 +57,7 @@ public class Constants { public static final IDelayed MINECRAFT_LIBS = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-libs")); public static final IDelayed MINECRAFT_NATIVES = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-natives")); public static final IDelayed MINECRAFT_JSON = new DelayedFile(extension -> new File(extension.getUserCache(), extension.version + "-info.json")); + public static final IDelayed REF_MAP = new DelayedFile(extension -> new File(CACHE_FILES, "mixin-refmap.json")); public static final IDelayed VERSION_MANIFEST = new DelayedFile(extension -> new File(extension.getUserCache(), "version_manifest.json")); diff --git a/src/main/java/net/fabricmc/loom/util/proccessing/MixinDevRemapper.java b/src/main/java/net/fabricmc/loom/util/proccessing/MixinDevRemapper.java new file mode 100644 index 0000000..4cde444 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/proccessing/MixinDevRemapper.java @@ -0,0 +1,75 @@ +package net.fabricmc.loom.util.proccessing; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import net.fabricmc.tinyremapper.TinyUtils; +import org.objectweb.asm.commons.Remapper; +import org.spongepowered.asm.mixin.extensibility.IRemapper; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class MixinDevRemapper implements IRemapper { + private final BiMap classMap = HashBiMap.create(); + private final Map fieldMap = new HashMap<>(); + private final Map methodMap = new HashMap<>(); + + private final SimpleClassMapper classMapper = new SimpleClassMapper(classMap); + private final SimpleClassMapper classUnmapper = new SimpleClassMapper(classMap.inverse()); + + private static class SimpleClassMapper extends Remapper { + final Map classMap; + + public SimpleClassMapper(Map map) { + this.classMap = map; + } + + public String map(String typeName) { + return this.classMap.getOrDefault(typeName, typeName); + } + } + + public void readMapping(BufferedReader reader, String fromM, String toM) throws IOException { + TinyUtils.read(reader, fromM, toM, classMap::put, fieldMap::put, methodMap::put); + } + + @Override + public String mapMethodName(String owner, String name, String desc) { + TinyUtils.Mapping mapping = methodMap.get(new TinyUtils.Mapping(owner, name, desc)); + return mapping != null ? mapping.name : name; + } + + @Override + public String mapFieldName(String owner, String name, String desc) { + TinyUtils.Mapping mapping = fieldMap.get(new TinyUtils.Mapping(owner, name, desc)); + if(mapping == null){ + //We try again using obfed names + owner = unmap(owner); + desc = unmapDesc(desc); + mapping = fieldMap.get(new TinyUtils.Mapping(owner, name, desc)); + } + return mapping != null ? mapping.name : name; + } + + @Override + public String map(String typeName) { + return classMap.getOrDefault(typeName, typeName); + } + + @Override + public String unmap(String typeName) { + return classMap.inverse().getOrDefault(typeName, typeName); + } + + @Override + public String mapDesc(String desc) { + return classMapper.mapDesc(desc); + } + + @Override + public String unmapDesc(String desc) { + return classUnmapper.mapDesc(desc); + } +} \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/util/proccessing/MixinPrebaker.java b/src/main/java/net/fabricmc/loom/util/proccessing/MixinPrebaker.java new file mode 100644 index 0000000..f08bbd4 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/proccessing/MixinPrebaker.java @@ -0,0 +1,317 @@ +/* + * Copyright 2016 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.fabricmc.loom.util.proccessing; + +import com.google.common.base.Charsets; +import com.google.common.base.Predicate; +import com.google.common.io.ByteStreams; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.fabricmc.loom.mixin.MixinServiceGradle; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.gradle.api.Project; +import org.objectweb.asm.*; +import org.spongepowered.asm.launch.GlobalProperties; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.mixin.EnvironmentStateTweaker; +import org.spongepowered.asm.mixin.MixinEnvironment; +import org.spongepowered.asm.mixin.Mixins; +import org.spongepowered.asm.mixin.transformer.MixinTransformer; +import org.spongepowered.asm.service.MixinService; +import org.spongepowered.asm.service.mojang.MixinServiceLaunchWrapper; + +import javax.annotation.Nonnull; +import java.io.*; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.attribute.FileTime; +import java.time.Instant; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; + +/** + * The purpose of this class is to provide an utility for baking mixins from + * mods into a JAR file at compile time to make accessing APIs provided by them + * more intuitive in development environment. + */ +public class MixinPrebaker { + private static class DesprinklingFieldVisitor extends FieldVisitor { + public DesprinklingFieldVisitor(int api, FieldVisitor fv) { + super(api, fv); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (isSprinkledAnnotation(desc)) { + return null; + } + return super.visitAnnotation(desc, visible); + } + } + + private static class DesprinklingMethodVisitor extends MethodVisitor { + public DesprinklingMethodVisitor(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (isSprinkledAnnotation(desc)) { + return null; + } + return super.visitAnnotation(desc, visible); + } + } + + private static class DesprinklingClassVisitor extends ClassVisitor { + public DesprinklingClassVisitor(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + return new DesprinklingFieldVisitor(Opcodes.ASM5, super.visitField(access, name, desc, signature, value)); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + return new DesprinklingMethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions)); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (isSprinkledAnnotation(desc)) { + return null; + } + return super.visitAnnotation(desc, visible); + } + } + + private static boolean isSprinkledAnnotation(String desc) { + //System.out.println(desc); + return desc.startsWith("Lorg/spongepowered/asm/mixin/transformer/meta"); + } + + // Term proposed by Mumfrey, don't blame me + public static byte[] desprinkle(byte[] cls) { + ClassReader reader = new ClassReader(cls); + ClassWriter writer = new ClassWriter(0); + + reader.accept(new DesprinklingClassVisitor(Opcodes.ASM5, writer), 0); + return writer.toByteArray(); + } + + public static final String APPLIED_MIXIN_CONFIGS_FILENAME = ".oml-applied-mixin-configs"; + public static final String MAPPINGS_FILENAME = ".oml-dev-mappings.tiny"; + + public static Map jarFileCache = new HashMap<>(); + + public static void main(String[] args, Project project) throws IOException { + boolean hasMappingsFile = false; + + if (args.length < 3) { + System.out.println("usage: MixinPrebaker [-m mapping-file] "); + return; + } + + File mappingsFile = null; + int argOffset; + for (argOffset = 0; argOffset < args.length; argOffset++) { + if ("-m".equals(args[argOffset])) { + hasMappingsFile = true; + mappingsFile = new File(args[++argOffset]); + //TODO this is prob what was handling the mixin remmapping, this may need to be added back + //FabricMixinBootstrap.setMappingFile(); + } else { + break; + } + } + + + + Set modFiles = new HashSet<>(); + for (int i = argOffset + 2; i < args.length; i++) { + modFiles.add(new File(args[i])); + } + + URLClassLoader ucl = (URLClassLoader) MixinPrebaker.class.getClassLoader(); + Launch.classLoader = new LaunchClassLoader(ucl.getURLs()); + Launch.blackboard = new HashMap<>(); + Launch.blackboard.put(MixinServiceLaunchWrapper.BLACKBOARD_KEY_TWEAKS, Collections.emptyList()); + + List mods = findModInfo(modFiles); + System.out.println("Found " + mods.size() + " mods"); + List mixins = new ArrayList<>(); + for(JsonObject modObject : mods){ + mixins.addAll(findMixins(modObject.getAsJsonArray("mixins"))); + mixins.addAll(findMixins(modObject.getAsJsonArray("clientMixins"))); + mixins.addAll(findMixins(modObject.getAsJsonArray("serverMixins"))); + } + System.out.println("Found " + mixins.size() + " mixins to pre bake"); + + List tweakers = new ArrayList<>(); + tweakers.add("com.openmodloader.loader.launch.OpenTweaker"); + GlobalProperties.put("TweakClasses", tweakers); + + MixinBootstrap.init(); + mixins.forEach(Mixins::addConfiguration); + + MixinServiceGradle.setupModFiles(modFiles, new File(args[argOffset + 0])); + + + EnvironmentStateTweaker tweaker = new EnvironmentStateTweaker(); + tweaker.getLaunchArguments(); + tweaker.injectIntoClassLoader(Launch.classLoader); + + //MixinServiceGradle.addMCDeps(project.getConfigurations().getByName(Constants.CONFIG_MC_DEPENDENCIES).getFiles(), tweaker); + + MixinEnvironment mixinEnvironment = MixinEnvironment.getDefaultEnvironment(); + + System.out.println("Loading mappings: " + mappingsFile); + InputStream mappingStream = new FileInputStream(mappingsFile); + MixinDevRemapper devRemapper = new MixinDevRemapper(); + devRemapper.readMapping(new BufferedReader(new InputStreamReader(mappingStream)), "pomf", "mojang"); + mappingStream.close(); + mixinEnvironment.getRemappers().add(devRemapper); + + mixinEnvironment.setSide(MixinEnvironment.Side.CLIENT); //TODO have an all side? + + + MixinTransformer mixinTransformer = GlobalProperties.get(GlobalProperties.Keys.TRANSFORMER); + if(mixinTransformer == null){ + MixinService.getService().beginPhase(); + mixinTransformer = GlobalProperties.get(GlobalProperties.Keys.TRANSFORMER); + } + mixinTransformer.audit(mixinEnvironment); + + + try { + JarInputStream input = new JarInputStream(new FileInputStream(new File(args[argOffset + 0]))); + JarOutputStream output = new JarOutputStream(new FileOutputStream(new File(args[argOffset + 1]))); + JarEntry entry; + while ((entry = input.getNextJarEntry()) != null) { + if (entry.getName().equals(APPLIED_MIXIN_CONFIGS_FILENAME)) { + continue; + } + + if (hasMappingsFile && entry.getName().equals(MAPPINGS_FILENAME)) { + continue; + } + + if (entry.getName().endsWith(".class")) { + byte[] classIn = ByteStreams.toByteArray(input); + String className = entry.getName().substring(0, entry.getName().length() - 6).replace('/', '.'); + byte[] classOut = mixinTransformer.transformClassBytes(className, className, classIn); + if (classIn != classOut) { + System.out.println("Transformed " + className); + classOut = desprinkle(classOut); + } + JarEntry newEntry = new JarEntry(entry.getName()); + newEntry.setComment(entry.getComment()); + newEntry.setSize(classOut.length); + newEntry.setLastModifiedTime(FileTime.from(Instant.now())); + output.putNextEntry(newEntry); + output.write(classOut); + } else { + output.putNextEntry(entry); + ByteStreams.copy(input, output); + } + } + + output.putNextEntry(new JarEntry(APPLIED_MIXIN_CONFIGS_FILENAME)); + output.write(String.join("\n", mixins).getBytes(Charsets.UTF_8)); + + if (hasMappingsFile) { + output.putNextEntry(new JarEntry(MAPPINGS_FILENAME)); + Files.copy(mappingsFile.toPath(), output); + } + + input.close(); + output.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private static List findMixins(JsonArray jsonArray){ + if(jsonArray == null || jsonArray.size() == 0){ + return Collections.emptyList(); + } + List mixinList = new ArrayList<>(); + for (int i = 0; i < jsonArray.size(); i++) { + mixinList.add(jsonArray.get(i).getAsString()); + } + return mixinList; + } + + private static List findModInfo(Set mods){ + List modFiles = mods.stream().map(file -> { + try { + JarFile jar = new JarFile(file); + return readModInfoFromJar(jar); + } catch (IOException e) { + throw new RuntimeException("Failed to mod " + file.getName(), e); + } + }).filter((Predicate) Objects::nonNull).collect(Collectors.toList()); + + List containedMods = new ArrayList<>(); + for(JsonArray modFile : modFiles){ + for (int i = 0; i < modFile.size(); i++) { + containedMods.add(modFile.get(i).getAsJsonObject()); + } + } + return containedMods; + } + + private static JsonArray readModInfoFromJar(@Nonnull JarFile file) throws IOException { + Gson gson = new Gson(); + ZipEntry entry = file.getEntry("mod.json"); + if (entry == null) + return null; + + InputStreamReader stream = new InputStreamReader(file.getInputStream(entry)); + JsonArray jsonArray = gson.fromJson(stream, JsonArray.class); + stream.close(); + + List mixins = new ArrayList<>(); + for (int i = 0; i < jsonArray.size(); i++) { + JsonObject modObject = jsonArray.get(i).getAsJsonObject(); + mixins.addAll(findMixins(modObject.getAsJsonArray("mixins"))); + mixins.addAll(findMixins(modObject.getAsJsonArray("clientMixins"))); + mixins.addAll(findMixins(modObject.getAsJsonArray("serverMixins"))); + } + + System.out.println("Found: " + mixins.size() + " mixins in " + file.getName()); + + mixins.forEach(s -> { + ZipEntry entry1 = file.getEntry(s); + try { + jarFileCache.put(s, file.getInputStream(entry1)); + } catch (IOException e) { + throw new RuntimeException("Failed to load jar", e); + } + }); + return jsonArray; + } + +} \ No newline at end of file diff --git a/src/main/java/net/fabricmc/loom/util/proccessing/PreBakeMixins.java b/src/main/java/net/fabricmc/loom/util/proccessing/PreBakeMixins.java new file mode 100644 index 0000000..accdc66 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/proccessing/PreBakeMixins.java @@ -0,0 +1,53 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016 FabricMC + * + * 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 net.fabricmc.loom.util.proccessing; + +import net.fabricmc.loom.LoomGradleExtension; +import net.fabricmc.loom.task.ProcessModsTask; +import net.fabricmc.loom.util.Constants; +import org.gradle.api.Project; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +public class PreBakeMixins { + + public void proccess(Project project, LoomGradleExtension extension, List mods) throws IOException { + project.getLogger().lifecycle(":Found " + mods.size() + " mods to prebake"); + String[] args = new String[mods.size() + 4]; + args[0] = "-m"; + args[1] = Constants.MAPPINGS_TINY.get(extension).getAbsolutePath(); + args[2] = Constants.MINECRAFT_MERGED_JAR.get(extension).getAbsolutePath(); + args[3] = Constants.MINECRAFT_MIXED_JAR.get(extension).getAbsolutePath(); + for (int i = 0; i < mods.size(); i++) { + args[i + 4] = mods.get(i).getAbsolutePath(); + } + project.getLogger().lifecycle(":preBaking mixins"); + ProcessModsTask.addFile(Constants.MINECRAFT_MIXED_JAR.get(extension), this); + MixinPrebaker.main(args, project); + } + +} diff --git a/src/main/resources/META-INF/services/org.spongepowered.asm.service.IMixinService b/src/main/resources/META-INF/services/org.spongepowered.asm.service.IMixinService new file mode 100644 index 0000000..75ed11a --- /dev/null +++ b/src/main/resources/META-INF/services/org.spongepowered.asm.service.IMixinService @@ -0,0 +1 @@ +net.fabricmc.loom.mixin.MixinServiceGradle \ No newline at end of file diff --git a/src/main/resources/META-INF/services/org.spongepowered.tools.obfuscation.service.IObfuscationService b/src/main/resources/META-INF/services/org.spongepowered.tools.obfuscation.service.IObfuscationService new file mode 100644 index 0000000..b3949cc --- /dev/null +++ b/src/main/resources/META-INF/services/org.spongepowered.tools.obfuscation.service.IObfuscationService @@ -0,0 +1 @@ +net.fabricmc.loom.mixin.ObfuscationServiceFabric \ No newline at end of file