diff --git a/build.gradle b/build.gradle index eff63f7..8087044 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import org.gradle.api.java.archives.internal.DefaultManifest + buildscript { repositories { maven { url = 'https://maven.minecraftforge.net' } @@ -7,111 +9,175 @@ buildscript { classpath group: "net.minecraftforge.gradle", name: "ForgeGradle", version: "4.1.+", changing: true } } - + +plugins { + id "com.github.johnrengelman.shadow" version "6.1.0" + id "java" // java plugin is needed for the shadow plugin to work + id "eclipse" + id "maven-publish" +} apply plugin: 'net.minecraftforge.gradle' -// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup. -apply plugin: 'eclipse' -apply plugin: 'maven-publish' -version = '1.12.x-1.0.4' -group = 'dk.zlepper.itlt' // http://maven.apache.org/guides/mini/guide-naming-conventions.html -archivesBaseName = 'itlt' +version = "1.12.x-1.0.5" +group = "dk.zlepper.itlt" // http://maven.apache.org/guides/mini/guide-naming-conventions.html +archivesBaseName = "itlt" -java.toolchain.languageVersion = JavaLanguageVersion.of(8) // Mojang ships Java 8 to end users, so your mod should target Java 8. +// Target Java 8 but use a Java 11 compiler +java.toolchain.languageVersion = JavaLanguageVersion.of(8) +sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = "1.8" +tasks.withType(JavaCompile).configureEach { + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(11) + } + sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = "1.8" + + // You can verify this is working correctly by running the build task, extracting itlt.class from + // the built jar and checking if the bytecode version is 52 using the command "javap -verbose ./itlt.class" +} -println("Java: " + System.getProperty("java.version") + " JVM: " + System.getProperty("java.vm.version") + "(" + System.getProperty("java.vendor") + ") Arch: " + System.getProperty("os.arch")) +// Use Java 8 for run tasks (e.g. runClient) +tasks.withType(JavaExec).configureEach { + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(8) + } + + // You can verify this is working correctly by running the runClient task and checking the debug.log + // in the run folder for "[dk.zlepper.itlt.itlt/]: javaVerInt: 8" +} + +println "Java: ${System.getProperty "java.version"}, JVM: ${System.getProperty "java.vm.version"} (${System.getProperty "java.vendor"}), Arch: ${System.getProperty "os.arch"}" minecraft { - // The mappings can be changed at any time, and must be in the following format. - // snapshot_YYYYMMDD Snapshot are built nightly. - // stable_# Stables are built at the discretion of the MCP team. - // Use non-default mappings at your own risk. they may not always work. - // Simply re-run your setup task after changing the mappings to update your workspace. - //mappings channel: 'snapshot', version: '20171003-1.12' - mappings channel: 'snapshot', version: '20180814-1.12' - // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. - - // accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') - - // Default run configurations. - // These can be tweaked, removed, or duplicated as needed. - runs { - client { - workingDirectory project.file('run') + mappings channel: "snapshot", version: "20180814-1.12" - // Recommended logging data for a userdev environment - property 'forge.logging.markers', 'REGISTRIES' + accessTransformer = file("src/main/resources/META-INF/accesstransformer.cfg") - // Recommended logging level for the console - property 'forge.logging.console.level', 'debug' + runs { + final File runFile = project.file("run") + client { + workingDirectory runFile + property "forge.logging.console.level", "info" + mods { + itlt { + source sourceSets.main + } + } } server { + workingDirectory runFile + property "forge.logging.console.level", "info" + mods { + itlt { + source sourceSets.main + } + } + } - // Recommended logging data for a userdev environment - property 'forge.logging.markers', 'REGISTRIES' - - // Recommended logging level for the console - property 'forge.logging.console.level', 'debug' + data { + workingDirectory runFile + property "forge.logging.console.level", "info" + args "--mod", "itlt", "--all", "--output", file("src/generated/resources/") + mods { + itlt { + source sourceSets.main + } + } } } } +configurations { + shade +} + +repositories { + maven { + url = "https://cursemaven.com" + content { includeGroup "curse.maven" } + } + mavenCentral() + maven { url = "https://repository.mulesoft.org/nexus/content/repositories/public/" } +} + dependencies { - // Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed - // that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied. - // The userdev artifact is a special name and will get all sorts of transformations applied to it. + // Forge minecraft 'net.minecraftforge:forge:1.12.2-14.23.5.2854' - // You may put jars on which you depend on in ./libs or you may define them like so.. - // compile "some.group:artifact:version:classifier" - // compile "some.group:artifact:version" + // itlt uses the "image4j" library to read .ico files + implementation "com.github.imcdonagh:image4j:0.7.2" + shade "com.github.imcdonagh:image4j:0.7.2" + + // itlt uses the "Apache Commons Imaging" library to read .icns files + /*implementation "org.apache.commons:commons-imaging:1.0-alpha2" + shade "org.apache.commons:commons-imaging:1.0-alpha2"*/ + // due to it being such a big library and not supporting class removal, I've made a single method return null, + // removed jpeg, tiff, examples and tests and then compiled it. This fork supports removing classes for formats we + // don't use, therefore allowing us to drastically reduce the shrunk jar's filesize + implementation files("thirdPartyLibs/commons-imaging-1.0-alpha2-custom.jar") + shade files("thirdPartyLibs/commons-imaging-1.0-alpha2-custom.jar") +} - // Real examples - // compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env - // compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env +ext { + MANIFEST = manifest { + attributes([ + "Specification-Title": "itlt", + "Specification-Vendor": "Paint_Ninja", + "Specification-Version": "1", // We are version 1 of ourselves + "Implementation-Title": project.name, + "Implementation-Version": project.version, + "Implementation-Vendor" : "Paint_Ninja", + "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), + "Main-Class": "dk.zlepper.itlt.Main" // so that the pop-up window code works + ]) + } as DefaultManifest +} - // The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime. - // provided 'com.mod-buildcraft:buildcraft:6.0.8:dev' +jar { + manifest.from(MANIFEST) +} - // These dependencies get remapped to your current MCP mappings - // deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev' +shadowJar { + archiveClassifier.set("") + configurations = [project.configurations.shade] - // For more info... - // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html - // http://www.gradle.org/docs/current/userguide/dependency_management.html + // Apache Commons Imaging needs to be put somewhere else to prevent a NoClassDefFoundError + relocate "org.apache.commons.imaging", "${project.group}.shadow.org.apache.commons.imaging" + manifest.from(MANIFEST) } -// Example for how to get properties into the manifest for reading by the runtime.. -jar { - manifest { - attributes([ - "Specification-Title": "itlt", - "Specification-Vendor": "Paint_Ninja", - "Specification-Version": "1", // We are version 1 of ourselves - "Implementation-Title": project.name, - "Implementation-Version": "${version}", - "Implementation-Vendor" :"Paint_Ninja", - "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") - ]) - } +reobf { + shadowJar { } } -// Example configuration to allow publishing using the maven-publish task -// This is the preferred method to reobfuscate your jar file -jar.finalizedBy('reobfJar') -// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing -//publish.dependsOn('reobfJar') +task shrinkJar(type: Jar, dependsOn: reobfShadowJar) { + archiveClassifier.set("shrunk") -publishing { - publications { - mavenJava(MavenPublication) { - artifact jar - } - } - repositories { - maven { - url "file:///${project.projectDir}/mcmodsrepo" - } - } -} \ No newline at end of file + final File inputJar = file("${buildDir}/libs/${archivesBaseName}-${project.version}.jar") + final String shadowGroup = project.group.replace('.','/') + "/shadow" + final String apacheImaging = "${shadowGroup}/org/apache/commons/imaging" + + from zipTree(inputJar) + + exclude "net/sf/image4j/example/**", "net/sf/image4j/test/**" + + exclude "${apacheImaging}/formats/gif/**", "${apacheImaging}/formats/psd/**", "${apacheImaging}/formats/rgbe/**", + "${apacheImaging}/formats/xbm/**", "${apacheImaging}/formats/xpm/**", "${apacheImaging}/formats/pnm/**", + "${apacheImaging}/formats/pcx/**", "${apacheImaging}/formats/dcx/**", "${apacheImaging}/formats/png/**", + "${apacheImaging}/formats/bmp/**", "${apacheImaging}/formats/wbmp/**", "${apacheImaging}/formats/ico/**" + + exclude "${apacheImaging}/common/itu_t4/**", "${apacheImaging}/common/mylzw/**", "${apacheImaging}/common/BasicCParser**", + "${apacheImaging}/common/RationalNumber**", "${apacheImaging}/common/GenericImageMetadata**", + "${apacheImaging}/common/ZlibDeflate**", "${apacheImaging}/common/PackBits**", "${apacheImaging}/icc/**", + "${apacheImaging}/palette/**", "${apacheImaging}/color/**", "${apacheImaging}/ImageDump**", + "${apacheImaging}/ColorTools**", "${apacheImaging}/internal/**" + + exclude "META-INF/LICENSE.txt", "META-INF/NOTICE.txt" + + manifest.from(MANIFEST) +} + +//tasks.build.dependsOn reobfShadowJar +//jar.finalizedBy reobfShadowJar +tasks.build.dependsOn shrinkJar +jar.finalizedBy shrinkJar \ No newline at end of file diff --git a/src/main/java/dk/zlepper/itlt/Itlt.java b/src/main/java/dk/zlepper/itlt/Itlt.java index 9af53a3..4db2b6f 100644 --- a/src/main/java/dk/zlepper/itlt/Itlt.java +++ b/src/main/java/dk/zlepper/itlt/Itlt.java @@ -1,6 +1,7 @@ package dk.zlepper.itlt; import com.google.gson.Gson; +import dk.zlepper.itlt.helpers.IconHandler; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.SidedProxy; import dk.zlepper.itlt.about.mod; @@ -43,7 +44,8 @@ public final class Itlt { @Mod.EventHandler @SideOnly(Side.CLIENT) - public void preinit(FMLPreInitializationEvent event) { + public void preinit(FMLPreInitializationEvent event) throws IOException + { logger = event.getModLog(); if (proxy instanceof ClientProxy) { @@ -77,13 +79,24 @@ public void preinit(FMLPreInitializationEvent event) { Property customIconProp = config.get("Display", "loadCustomIcon", true); customIconProp.setComment("Set to true to load a custom icon from config" + File.separator + "itlt" + File.separator + "icon.png"); if (customIconProp.getBoolean()) { + File customIcon = null; final File di = Paths.get(event.getModConfigurationDirectory().getAbsolutePath(), "itlt").toFile(); logger.info(di); if (di.exists()) { - final File icon = Paths.get(di.getAbsolutePath(), "icon.png").toFile(); - logger.info(icon.exists() ? "Custom modpack icon found" : "Custom modpack icon NOT found."); - if (icon.exists() && !icon.isDirectory()) { - Display.setIcon(IconLoader.load(icon)); + final File icoIcon = Paths.get(di.getAbsolutePath(), "icon.ico").toFile(); + final File icnsIcon = Paths.get(di.getAbsolutePath(), "icon.icns").toFile(); + final File pngIcon = Paths.get(di.getAbsolutePath(), "icon.png").toFile(); + logger.info(icoIcon.exists() ? "Custom modpack .ico found" : "Custom modpack .ico NOT found."); + logger.info(icnsIcon.exists() ? "Custom modpack .icns found" : "Custom modpack .icns NOT found."); + logger.info(pngIcon.exists() ? "Custom modpack .png found" : "Custom modpack .png NOT found."); + + if (icoIcon.exists() && !icoIcon.isDirectory()) customIcon = icoIcon; + else if (icnsIcon.exists() && !icnsIcon.isDirectory()) customIcon = icnsIcon; + else if (pngIcon.exists() && !pngIcon.isDirectory()) customIcon = pngIcon; + if(customIcon != null) { + IconHandler.setWindowIcon(customIcon); + } else { + logger.warn("loadCustomIcon is true but icon.ico/icns/png is missing or invalid."); } } else { logger.error("Directory for custom modpack icon not found!"); diff --git a/src/main/java/dk/zlepper/itlt/helpers/IconHandler.java b/src/main/java/dk/zlepper/itlt/helpers/IconHandler.java new file mode 100644 index 0000000..26781f6 --- /dev/null +++ b/src/main/java/dk/zlepper/itlt/helpers/IconHandler.java @@ -0,0 +1,48 @@ +package dk.zlepper.itlt.helpers; + +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.ByteBuffer; +import java.util.List; + +import dk.zlepper.itlt.Itlt; +import net.sf.image4j.codec.ico.ICODecoder; +import org.apache.commons.imaging.ImageReadException; +import org.apache.commons.imaging.formats.icns.IcnsImageParser; +import org.lwjgl.opengl.Display; + +public class IconHandler +{ + //Backport some of 2.X ico and icns support + public static void setWindowIcon(final File inputIconFile) throws IOException { + final ByteBuffer[] buffer = new ByteBuffer[1]; + List bufferedImageList; + + final String inputIconFilenameAndExt = inputIconFile.getName().toLowerCase(); + if (inputIconFilenameAndExt.endsWith(".ico")) { + // load all of the images inside the `.ico` file as a list of `BufferedImage`s + bufferedImageList = ICODecoder.read(inputIconFile); + buffer[0] = IconLoader.convertToByteBuffer(bufferedImageList.get(0)); + Display.setIcon(buffer); + Itlt.logger.info("ICO file found, using that"); + return; + } else if (inputIconFilenameAndExt.endsWith(".icns")) { + // try to load all of the images inside the `.icns` file as a list of `BufferedImage`s + try { + bufferedImageList = new IcnsImageParser().getAllBufferedImages(inputIconFile); + buffer[0] = IconLoader.convertToByteBuffer(bufferedImageList.get(0)); + Display.setIcon(buffer); + Itlt.logger.info("ICNS file found, using that"); + return; + } catch (final ImageReadException e) { + e.printStackTrace(); + return; + } + } else if (inputIconFilenameAndExt.endsWith(".png")) { + // load the `.png` file directly as an `InputStream` + Display.setIcon(IconLoader.load(inputIconFile)); + Itlt.logger.info("PNG file found, using that"); + return; + } + } +} diff --git a/src/main/java/dk/zlepper/itlt/helpers/IconLoader.java b/src/main/java/dk/zlepper/itlt/helpers/IconLoader.java index 5325502..b86b32a 100644 --- a/src/main/java/dk/zlepper/itlt/helpers/IconLoader.java +++ b/src/main/java/dk/zlepper/itlt/helpers/IconLoader.java @@ -18,9 +18,10 @@ * ratio is kept the same as the source image. * * @author Chris Molini + * @author Paint_Ninja *****************************************************************************/ -public class IconLoader -{ +public class IconLoader { + /************************************************************************* * Loads an icon in ByteBuffer form. * @@ -30,46 +31,36 @@ public class IconLoader * @return An array of ByteBuffers containing the pixel data for the icon in * varying sizes. *************************************************************************/ - public static ByteBuffer[] load(String filepath) - { + public static ByteBuffer[] load(final String filepath) { return load(new File(filepath)); } /************************************************************************* * Loads an icon in ByteBuffer form. * - * @param fil + * @param file * A File pointing to the image. * * @return An array of ByteBuffers containing the pixel data for the icon in * various sizes (as recommended by the OS). *************************************************************************/ - public static ByteBuffer[] load(File fil) - { + public static ByteBuffer[] load(final File file) { BufferedImage image = null; - try - { - image = ImageIO.read(fil); - } - catch (IOException e) - { + try { + image = ImageIO.read(file); + } catch (final IOException e) { e.printStackTrace(); } - ByteBuffer[] buffers = null; - final String OS = System.getProperty("os.name").toUpperCase(); - if(OS.contains("WIN")) - { + + final ByteBuffer[] buffers; + if (Platform.isWindows()) { buffers = new ByteBuffer[2]; buffers[0] = loadInstance(image, 16); buffers[1] = loadInstance(image, 32); - } - else if(OS.contains("MAC")) - { + } else if (Platform.isMac()) { buffers = new ByteBuffer[1]; buffers[0] = loadInstance(image, 128); - } - else - { + } else { buffers = new ByteBuffer[1]; buffers[0] = loadInstance(image, 32); } @@ -86,14 +77,12 @@ else if(OS.contains("MAC")) * * @return A ByteBuffer of pixel data at the indicated size. *************************************************************************/ - private static ByteBuffer loadInstance(BufferedImage image, int dimension) - { - BufferedImage scaledIcon = new BufferedImage(dimension, dimension, - BufferedImage.TYPE_INT_ARGB_PRE); - Graphics2D g = scaledIcon.createGraphics(); - double ratio = getIconRatio(image, scaledIcon); - double width = image.getWidth() * ratio; - double height = image.getHeight() * ratio; + private static ByteBuffer loadInstance(final BufferedImage image, final int dimension) { + final BufferedImage scaledIcon = new BufferedImage(dimension, dimension, BufferedImage.TYPE_INT_ARGB_PRE); + final Graphics2D g = scaledIcon.createGraphics(); + final double ratio = getIconRatio(image, scaledIcon); + final double width = image.getWidth() * ratio; + final double height = image.getHeight() * ratio; g.drawImage(image, (int) ((scaledIcon.getWidth() - width) / 2), (int) ((scaledIcon.getHeight() - height) / 2), (int) (width), (int) (height), null); @@ -114,24 +103,17 @@ private static ByteBuffer loadInstance(BufferedImage image, int dimension) * @return The amount to scale the source image to fit it onto the icon * appropriately. *************************************************************************/ - private static double getIconRatio(BufferedImage src, BufferedImage icon) - { - double ratio = 1; - if (src.getWidth() > icon.getWidth()) - ratio = (double) (icon.getWidth()) / src.getWidth(); - else - ratio = (int) (icon.getWidth() / src.getWidth()); - if (src.getHeight() > icon.getHeight()) - { - double r2 = (double) (icon.getHeight()) / src.getHeight(); - if (r2 < ratio) - ratio = r2; - } - else - { - double r2 = (int) (icon.getHeight() / src.getHeight()); - if (r2 < ratio) - ratio = r2; + private static double getIconRatio(final BufferedImage src, final BufferedImage icon) { + double ratio; + if (src.getWidth() > icon.getWidth()) ratio = (double) (icon.getWidth()) / src.getWidth(); + else ratio = (int) (icon.getWidth() / src.getWidth()); + + if (src.getHeight() > icon.getHeight()) { + final double r2 = (double) (icon.getHeight()) / src.getHeight(); + if (r2 < ratio) ratio = r2; + } else { + final double r2 = (int) (icon.getHeight() / src.getHeight()); + if (r2 < ratio) ratio = r2; } return ratio; } @@ -144,20 +126,19 @@ private static double getIconRatio(BufferedImage src, BufferedImage icon) * * @return A ByteBuffer that contains the pixel data of the supplied image. *************************************************************************/ - public static ByteBuffer convertToByteBuffer(BufferedImage image) - { - byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4]; + public static ByteBuffer convertToByteBuffer(final BufferedImage image) { + final byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4]; int counter = 0; - for (int i = 0; i < image.getHeight(); i++) - for (int j = 0; j < image.getWidth(); j++) - { + for (int i = 0; i < image.getHeight(); i++) { + for (int j = 0; j < image.getWidth(); j++) { int colorSpace = image.getRGB(j, i); - buffer[counter + 0] = (byte) ((colorSpace << 8) >> 24); + buffer[counter ] = (byte) ((colorSpace << 8) >> 24); buffer[counter + 1] = (byte) ((colorSpace << 16) >> 24); buffer[counter + 2] = (byte) ((colorSpace << 24) >> 24); buffer[counter + 3] = (byte) (colorSpace >> 24); counter += 4; } + } return ByteBuffer.wrap(buffer); } } \ No newline at end of file diff --git a/src/main/java/dk/zlepper/itlt/helpers/Platform.java b/src/main/java/dk/zlepper/itlt/helpers/Platform.java new file mode 100644 index 0000000..a90e0ee --- /dev/null +++ b/src/main/java/dk/zlepper/itlt/helpers/Platform.java @@ -0,0 +1,13 @@ +package dk.zlepper.itlt.helpers; + +public class Platform { + public static boolean isWindows() { + return getOsName().toLowerCase().startsWith("windows"); + } + public static boolean isMac() { + return getOsName().startsWith("Mac") || getOsName().startsWith("Darwin"); + } + private static String getOsName() { + return System.getProperty("os.name"); + } +} \ No newline at end of file diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 8225bc4..032e878 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -3,7 +3,7 @@ "modid": "itlt", "name": "It's the little things", "description": "A mod that does those \"little things\".", - "version": "1.0.4", + "version": "1.0.5", "mcversion": "1.12", "url": "https://www.curseforge.com/minecraft/mc-mods/its-the-little-things", "updateUrl": "", diff --git a/thirdPartyLibs/commons-imaging-1.0-alpha2-custom.jar b/thirdPartyLibs/commons-imaging-1.0-alpha2-custom.jar new file mode 100644 index 0000000..4dcbe8f Binary files /dev/null and b/thirdPartyLibs/commons-imaging-1.0-alpha2-custom.jar differ