diff --git a/src/main/java/org/spigotmc/builder/Builder.java b/src/main/java/org/spigotmc/builder/Builder.java index 55d000c..1177b1e 100644 --- a/src/main/java/org/spigotmc/builder/Builder.java +++ b/src/main/java/org/spigotmc/builder/Builder.java @@ -33,7 +33,9 @@ import java.lang.management.ManagementFactory; import java.net.URI; import java.net.URL; import java.net.URLConnection; +import java.nio.file.FileSystem; import java.nio.file.FileSystemException; +import java.nio.file.FileSystems; import java.nio.file.Path; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -47,6 +49,7 @@ import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; +import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import javax.net.ssl.HostnameVerifier; @@ -396,7 +399,8 @@ public class Builder } File vanillaJar = new File( workDir, "minecraft_server." + versionInfo.getMinecraftVersion() + ".jar" ); - if ( !vanillaJar.exists() || !checkHash( vanillaJar, versionInfo ) ) + File embeddedVanillaJar = new File( workDir, "server-" + versionInfo.getMinecraftVersion() + ".jar" ); + if ( !checkHash( vanillaJar, versionInfo ) ) { if ( versionInfo.getServerUrl() != null ) { @@ -407,6 +411,33 @@ public class Builder } } + try ( JarFile jar = new JarFile( vanillaJar ) ) + { + ZipEntry entry = jar.getEntry( "META-INF/versions/" + versionInfo.getMinecraftVersion() + "/server-" + versionInfo.getMinecraftVersion() + ".jar" ); + if ( entry != null ) + { + if ( !checkHash( embeddedVanillaJar, HashFormat.SHA256, versionInfo.getMinecraftHash() ) ) + { + try ( InputStream is = jar.getInputStream( entry ) ) + { + byte[] embedded = ByteStreams.toByteArray( is ); + if ( embedded != null ) + { + Files.write( embedded, embeddedVanillaJar ); + } + } + + try ( FileSystem zipfs = FileSystems.newFileSystem( embeddedVanillaJar.toPath(), (ClassLoader) null ) ) + { + java.nio.file.Files.delete( zipfs.getPath( "/META-INF/MOJANGCS.RSA" ) ); + java.nio.file.Files.delete( zipfs.getPath( "/META-INF/MOJANGCS.SF" ) ); + } + } + + vanillaJar = embeddedVanillaJar; + } + } + if ( versionInfo.getServerUrl() == null ) { // Legacy versions can also specify a specific shell to build with which has to be bash-compatible @@ -446,23 +477,33 @@ public class Builder MapUtil mapUtil = new MapUtil(); mapUtil.loadBuk( classMappings ); - if ( !fieldMappings.exists() ) + if ( !memberMappings.exists() ) { - mapUtil.makeFieldMaps( mojangMappings, fieldMappings ); - } + memberMappings = new File( workDir, "bukkit-" + mappingsVersion + "-members.csrg" );; + mapUtil.makeFieldMaps( mojangMappings, memberMappings, true ); - File combinedMappings = new File( workDir, "bukkit-" + mappingsVersion + "-combined.csrg" ); - if ( !combinedMappings.exists() ) + runMaven( CWD, "install:install-file", "-Dfile=" + memberMappings, "-Dpackaging=csrg", "-DgroupId=org.spigotmc", + "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-spigot-members", "-DgeneratePom=false" ); + + runMaven( CWD, "install:install-file", "-Dfile=" + classMappings, "-Dpackaging=csrg", "-DgroupId=org.spigotmc", + "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-spigot", "-DgeneratePom=false" ); + } else if ( !fieldMappings.exists() ) { - mapUtil.makeCombinedMaps( combinedMappings, memberMappings ); + mapUtil.makeFieldMaps( mojangMappings, fieldMappings, false ); + + runMaven( CWD, "install:install-file", "-Dfile=" + fieldMappings, "-Dpackaging=csrg", "-DgroupId=org.spigotmc", + "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-spigot-fields", "-DgeneratePom=false" ); + + File combinedMappings = new File( workDir, "bukkit-" + mappingsVersion + "-combined.csrg" ); + if ( !combinedMappings.exists() ) + { + mapUtil.makeCombinedMaps( combinedMappings, memberMappings ); + } + + runMaven( CWD, "install:install-file", "-Dfile=" + combinedMappings, "-Dpackaging=csrg", "-DgroupId=org.spigotmc", + "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-spigot", "-DgeneratePom=false" ); } - runMaven( CWD, "install:install-file", "-Dfile=" + fieldMappings, "-Dpackaging=csrg", "-DgroupId=org.spigotmc", - "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-spigot-fields", "-DgeneratePom=false" ); - - runMaven( CWD, "install:install-file", "-Dfile=" + combinedMappings, "-Dpackaging=csrg", "-DgroupId=org.spigotmc", - "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-spigot", "-DgeneratePom=false" ); - runMaven( CWD, "install:install-file", "-Dfile=" + mojangMappings, "-Dpackaging=txt", "-DgroupId=org.spigotmc", "-DartifactId=minecraft-server", "-Dversion=" + versionInfo.getSpigotVersion(), "-Dclassifier=maps-mojang", "-DgeneratePom=false" ); } @@ -685,29 +726,36 @@ public class Builder private static boolean checkHash(File vanillaJar, VersionInfo versionInfo) throws IOException { + if ( versionInfo.getShaServerHash() != null ) + { + return checkHash( vanillaJar, HashFormat.SHA1, versionInfo.getShaServerHash() ); + } else if ( versionInfo.getMinecraftHash() != null ) + { + return checkHash( vanillaJar, HashFormat.MD5, versionInfo.getMinecraftHash() ); + } else + { + return true; + } + } + + private static boolean checkHash(File vanillaJar, HashFormat hashFormat, String goodHash) throws IOException + { + if ( !vanillaJar.isFile() ) + { + return false; + } + if ( dev ) { return true; } - String hash; - boolean result; - if ( versionInfo.getMinecraftHash() != null ) - { - hash = Files.asByteSource( vanillaJar ).hash( HashFormat.MD5.getHash() ).toString(); - result = hash.equals( versionInfo.getMinecraftHash() ); - } else if ( versionInfo.getShaServerHash() != null ) - { - hash = Files.asByteSource( vanillaJar ).hash( HashFormat.SHA1.getHash() ).toString(); - result = hash.equals( versionInfo.getShaServerHash() ); - } else - { - return true; - } + String hash = Files.asByteSource( vanillaJar ).hash( hashFormat.getHash() ).toString(); + boolean result = hash.equals( goodHash ); if ( !result ) { - System.err.println( "**** Warning, Minecraft jar hash of " + hash + " does not match stored hash of " + versionInfo.getMinecraftHash() ); + System.err.println( "**** Warning, Minecraft jar hash of " + hash + " does not match stored hash of " + goodHash ); return false; } else { diff --git a/src/main/java/org/spigotmc/mapper/MapUtil.java b/src/main/java/org/spigotmc/mapper/MapUtil.java index 0e6252e..864b750 100644 --- a/src/main/java/org/spigotmc/mapper/MapUtil.java +++ b/src/main/java/org/spigotmc/mapper/MapUtil.java @@ -19,6 +19,7 @@ public class MapUtil // private List header = new ArrayList<>(); private final BiMap obf2Buk = HashBiMap.create(); + private final BiMap moj2Obf = HashBiMap.create(); public void loadBuk(File bukClasses) throws IOException { @@ -38,8 +39,28 @@ public class MapUtil } } - public void makeFieldMaps(File mojIn, File fields) throws IOException + public void makeFieldMaps(File mojIn, File fields, boolean includeMethods) throws IOException { + if ( includeMethods ) + { + for ( String line : Files.readAllLines( mojIn.toPath() ) ) + { + if ( line.startsWith( "#" ) ) + { + continue; + } + + if ( line.endsWith( ":" ) ) + { + String[] parts = line.split( " -> " ); + String orig = parts[0].replace( '.', '/' ); + String obf = parts[1].substring( 0, parts[1].length() - 1 ).replace( '.', '/' ); + + moj2Obf.put( orig, obf ); + } + } + } + List outFields = new ArrayList<>( header ); String currentClass = null; @@ -76,16 +97,26 @@ public class MapUtil String nameDesc = matcher.group( 2 ); if ( !nameDesc.contains( "(" ) ) { - if ( nameDesc.contains( "$" ) ) + if ( nameDesc.equals( obf ) || nameDesc.contains( "$" ) ) { continue; } - if ( obf.equals( "if" ) || obf.equals( "do" ) ) + if ( !includeMethods && ( obf.equals( "if" ) || obf.equals( "do" ) ) ) { obf += "_"; } outFields.add( currentClass + " " + obf + " " + nameDesc ); + } else if ( includeMethods ) + { + String sig = csrgDesc( moj2Obf, obf2Buk, nameDesc.substring( nameDesc.indexOf( '(' ) ), matcher.group( 1 ) ); + String mojName = nameDesc.substring( 0, nameDesc.indexOf( '(' ) ); + + if ( obf.equals( mojName ) || mojName.contains( "$" ) || obf.equals( "" ) || obf.equals( "" ) ) + { + continue; + } + outFields.add( currentClass + " " + obf + " " + sig + " " + mojName ); } } } @@ -208,4 +239,56 @@ public class MapUtil } return desc.substring( size ); } + + private static String csrgDesc(Map first, Map second, String args, String ret) + { + String[] parts = args.substring( 1, args.length() - 1 ).split( "," ); + StringBuilder desc = new StringBuilder( "(" ); + for ( String part : parts ) + { + if ( part.isEmpty() ) + { + continue; + } + desc.append( toJVMType( first, second, part ) ); + } + desc.append( ")" ); + desc.append( toJVMType( first, second, ret ) ); + return desc.toString(); + } + + private static String toJVMType(Map first, Map second, String type) + { + switch ( type ) + { + case "byte": + return "B"; + case "char": + return "C"; + case "double": + return "D"; + case "float": + return "F"; + case "int": + return "I"; + case "long": + return "J"; + case "short": + return "S"; + case "boolean": + return "Z"; + case "void": + return "V"; + default: + if ( type.endsWith( "[]" ) ) + { + return "[" + toJVMType( first, second, type.substring( 0, type.length() - 2 ) ); + } + String clazzType = type.replace( '.', '/' ); + String obf = deobfClass( clazzType, first ); + String mappedType = deobfClass( ( obf != null ) ? obf : clazzType, second ); + + return "L" + ( ( mappedType != null ) ? mappedType : clazzType ) + ";"; + } + } }