diff --git a/README.md b/README.md index bdaef1cd..aac8f463 100644 --- a/README.md +++ b/README.md @@ -40,22 +40,19 @@ the song menu. ## Building opsu! is distributed as both a Maven and Gradle project. +### Maven +Maven builds are built to the `target` directory. +* To run the project, execute the Maven goal `compile`. +* To create a single executable JAR file, execute the Maven goal `package -Djar`. + This will compile a jar to `target/opsu-${version}.jar` with the libraries, + resources and natives packed inside the jar. + ### Gradle Gradle builds are built to the `build` directory. * To run the project, execute the Gradle task `run`. -* To create a single executable JAR file, execute the Gradle task - `build`. This will compile a jar to `build/libs/opsu-${version}.jar` with - the libraries, resources and natives packed inside the jar. - -### Maven -Maven builds are built to the `target` directory. -* To run the project, execute the Maven goal `compile exec:exec`. -* To create a single executable JAR file, execute the Maven goal - `install -Djar`. This will link the LWJGL native libraries using a - [modified version](https://github.com/itdelatrisu/JarSplicePlus) of - [JarSplice](http://ninjacave.com/jarsplice), which is included in the - `tools` directory in both its original and modified forms. The resulting - file will be located in `target/opsu-${version}-runnable.jar`. +* To create a single executable JAR file, execute the Gradle task `build`. + This will compile a jar to `build/libs/opsu-${version}.jar` with the libraries, + resources and natives packed inside the jar. ## Credits This software was created by Jeffrey Han diff --git a/build.gradle b/build.gradle index 22940162..544eee25 100644 --- a/build.gradle +++ b/build.gradle @@ -12,6 +12,8 @@ version = '0.10.1' mainClassName = 'itdelatrisu.opsu.Opsu' buildDir = new File(rootProject.projectDir, "build/") +def useXDG = 'false' + sourceCompatibility = 1.7 targetCompatibility = 1.7 @@ -43,8 +45,6 @@ dependencies { compile 'org.apache.commons:commons-compress:1.9' compile 'org.tukaani:xz:1.5' compile 'com.github.jponge:lzma-java:1.3' - - } def nativePlatforms = ['windows', 'linux', 'osx'] @@ -84,7 +84,8 @@ jar { manifest { attributes 'Implementation-Title': 'opsu!', 'Implementation-Version': version, - 'Main-Class': mainClassName + 'Main-Class': mainClassName, + 'Use-XDG': useXDG } duplicatesStrategy = DuplicatesStrategy.EXCLUDE @@ -92,6 +93,8 @@ jar { from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } exclude '**/Thumbs.db' + + outputs.upToDateWhen { false } } run { diff --git a/pom.xml b/pom.xml index ce9bc066..ca910302 100644 --- a/pom.xml +++ b/pom.xml @@ -9,6 +9,7 @@ ${maven.build.timestamp} yyyy-MM-dd HH:mm itdelatrisu.opsu.Opsu + false src @@ -107,7 +108,10 @@ - ${mainClassName} + + ${mainClassName} + ${useXDG} + false diff --git a/src/itdelatrisu/opsu/NativeLoader.java b/src/itdelatrisu/opsu/NativeLoader.java index ee114146..c66ee86c 100644 --- a/src/itdelatrisu/opsu/NativeLoader.java +++ b/src/itdelatrisu/opsu/NativeLoader.java @@ -23,9 +23,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.CodeSource; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -47,41 +44,32 @@ public class NativeLoader { if (!NATIVE_DIR.exists()) NATIVE_DIR.mkdir(); - CodeSource src = NativeLoader.class.getProtectionDomain().getCodeSource(); - if (src != null) { - URI jar = null; - try { - jar = src.getLocation().toURI(); - } catch (URISyntaxException e) { - e.printStackTrace(); - return; + JarFile jarFile = Utils.getJarFile(); + if (jarFile == null) + return; + + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + if (e == null) + break; + + File f = new File(NATIVE_DIR, e.getName()); + if (isNativeFile(e.getName()) && !e.isDirectory() && e.getName().indexOf('/') == -1 && !f.exists()) { + InputStream in = jarFile.getInputStream(jarFile.getEntry(e.getName())); + OutputStream out = new FileOutputStream(f); + + byte[] buffer = new byte[65536]; + int bufferSize; + while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) + out.write(buffer, 0, bufferSize); + + in.close(); + out.close(); } - - JarFile jarFile = new JarFile(new File(jar), false); - Enumeration entries = jarFile.entries(); - - while (entries.hasMoreElements()) { - JarEntry e = entries.nextElement(); - if (e == null) - break; - - File f = new File(NATIVE_DIR, e.getName()); - if (isNativeFile(e.getName()) && !e.isDirectory() && e.getName().indexOf('/') == -1 && !f.exists()) { - InputStream in = jarFile.getInputStream(jarFile.getEntry(e.getName())); - OutputStream out = new FileOutputStream(f); - - byte[] buffer = new byte[65536]; - int bufferSize; - while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) - out.write(buffer, 0, bufferSize); - - in.close(); - out.close(); - } - } - - jarFile.close(); } + + jarFile.close(); } /** diff --git a/src/itdelatrisu/opsu/Opsu.java b/src/itdelatrisu/opsu/Opsu.java index 552ff3d2..fc67d143 100644 --- a/src/itdelatrisu/opsu/Opsu.java +++ b/src/itdelatrisu/opsu/Opsu.java @@ -18,12 +18,16 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.audio.MusicController; -import itdelatrisu.opsu.db.DBController; -import itdelatrisu.opsu.downloads.DownloadList; -import itdelatrisu.opsu.downloads.Updater; -import itdelatrisu.opsu.states.*; -import itdelatrisu.opsu.ui.UI; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.UnknownHostException; + import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.SlickException; @@ -35,11 +39,20 @@ import org.newdawn.slick.util.FileSystemLocation; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; -import java.io.*; -import java.lang.reflect.Field; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.UnknownHostException; +import itdelatrisu.opsu.audio.MusicController; +import itdelatrisu.opsu.db.DBController; +import itdelatrisu.opsu.downloads.DownloadList; +import itdelatrisu.opsu.downloads.Updater; +import itdelatrisu.opsu.states.ButtonMenu; +import itdelatrisu.opsu.states.DownloadsMenu; +import itdelatrisu.opsu.states.Game; +import itdelatrisu.opsu.states.GamePauseMenu; +import itdelatrisu.opsu.states.GameRanking; +import itdelatrisu.opsu.states.MainMenu; +import itdelatrisu.opsu.states.OptionsMenu; +import itdelatrisu.opsu.states.SongMenu; +import itdelatrisu.opsu.states.Splash; +import itdelatrisu.opsu.ui.UI; /** * Main class. @@ -143,7 +156,6 @@ public class Opsu extends StateBasedGame { Log.warn("Failed to set 'sys_paths' field.", e); } - // set the resource paths ResourceLoader.addResourceLocation(new FileSystemLocation(new File("./res/"))); diff --git a/src/itdelatrisu/opsu/Options.java b/src/itdelatrisu/opsu/Options.java index e9fdbb56..25dddfa2 100644 --- a/src/itdelatrisu/opsu/Options.java +++ b/src/itdelatrisu/opsu/Options.java @@ -18,13 +18,6 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.audio.MusicController; -import itdelatrisu.opsu.beatmap.Beatmap; -import itdelatrisu.opsu.skins.Skin; -import itdelatrisu.opsu.skins.SkinLoader; -import itdelatrisu.opsu.ui.Fonts; -import itdelatrisu.opsu.ui.UI; - import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -38,6 +31,9 @@ import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.concurrent.TimeUnit; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; import org.lwjgl.input.Keyboard; import org.newdawn.slick.GameContainer; @@ -48,10 +44,20 @@ import org.newdawn.slick.util.FileSystemLocation; import org.newdawn.slick.util.Log; import org.newdawn.slick.util.ResourceLoader; +import itdelatrisu.opsu.audio.MusicController; +import itdelatrisu.opsu.beatmap.Beatmap; +import itdelatrisu.opsu.skins.Skin; +import itdelatrisu.opsu.skins.SkinLoader; +import itdelatrisu.opsu.ui.Fonts; +import itdelatrisu.opsu.ui.UI; + /** * Handles all user options. */ public class Options { + /** Whether to use XDG directories. */ + private static final boolean USE_XDG = checkXDGFlag(); + /** The config directory. */ private static final File CONFIG_DIR = getXDGBaseDir("XDG_CONFIG_HOME", ".config"); @@ -120,15 +126,35 @@ public class Options { /** Port binding. */ private static int port = 49250; + /** + * Returns whether the XDG flag in the manifest (if any) is set to "true". + * @return true if XDG directories are enabled, false otherwise + */ + private static boolean checkXDGFlag() { + JarFile jarFile = Utils.getJarFile(); + if (jarFile == null) + return false; + try { + Manifest manifest = jarFile.getManifest(); + if (manifest == null) + return false; + Attributes attributes = manifest.getMainAttributes(); + String value = attributes.getValue("Use-XDG"); + return (value != null && value.equalsIgnoreCase("true")); + } catch (IOException e) { + return false; + } + } + /** * Returns the directory based on the XDG base directory specification for - * Unix-like operating systems, only if the system property "XDG" has been defined. + * Unix-like operating systems, only if the "XDG" flag is enabled. * @param env the environment variable to check (XDG_*_*) * @param fallback the fallback directory relative to ~home * @return the XDG base directory, or the working directory if unavailable */ private static File getXDGBaseDir(String env, String fallback) { - if (System.getProperty("XDG") == null) + if (!USE_XDG) return new File("./"); String OS = System.getProperty("os.name").toLowerCase(); diff --git a/src/itdelatrisu/opsu/Utils.java b/src/itdelatrisu/opsu/Utils.java index df07aabb..f1b1e613 100644 --- a/src/itdelatrisu/opsu/Utils.java +++ b/src/itdelatrisu/opsu/Utils.java @@ -18,15 +18,6 @@ package itdelatrisu.opsu; -import itdelatrisu.opsu.audio.SoundController; -import itdelatrisu.opsu.audio.SoundEffect; -import itdelatrisu.opsu.beatmap.HitObject; -import itdelatrisu.opsu.downloads.Download; -import itdelatrisu.opsu.downloads.DownloadNode; -import itdelatrisu.opsu.replay.PlaybackSpeed; -import itdelatrisu.opsu.ui.Fonts; -import itdelatrisu.opsu.ui.UI; - import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.BufferedReader; @@ -48,6 +39,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Scanner; +import java.util.jar.JarFile; import javax.imageio.ImageIO; @@ -66,6 +58,15 @@ import org.newdawn.slick.util.Log; import com.sun.jna.platform.FileUtils; +import itdelatrisu.opsu.audio.SoundController; +import itdelatrisu.opsu.audio.SoundEffect; +import itdelatrisu.opsu.beatmap.HitObject; +import itdelatrisu.opsu.downloads.Download; +import itdelatrisu.opsu.downloads.DownloadNode; +import itdelatrisu.opsu.replay.PlaybackSpeed; +import itdelatrisu.opsu.ui.Fonts; +import itdelatrisu.opsu.ui.UI; + /** * Contains miscellaneous utilities. */ @@ -564,8 +565,25 @@ public class Utils { return Opsu.class.getResource(String.format("%s.class", Opsu.class.getSimpleName())).toString().startsWith("jar:"); } + /** + * Returns the JarFile for the application. + * @return the JAR file, or null if it could not be determined + */ + public static JarFile getJarFile() { + if (!isJarRunning()) + return null; + + try { + return new JarFile(new File(Opsu.class.getProtectionDomain().getCodeSource().getLocation().toURI()), false); + } catch (URISyntaxException | IOException e) { + Log.error("Could not determine the JAR file.", e); + return null; + } + } + /** * Returns the directory where the application is being run. + * @return the directory, or null if it could not be determined */ public static File getRunningDirectory() { try { diff --git a/src/itdelatrisu/opsu/objects/curves/Curve.java b/src/itdelatrisu/opsu/objects/curves/Curve.java index 966f7c42..ec8a199d 100644 --- a/src/itdelatrisu/opsu/objects/curves/Curve.java +++ b/src/itdelatrisu/opsu/objects/curves/Curve.java @@ -150,6 +150,7 @@ public abstract class Curve { * @param i the control point index */ public float getY(int i) { return (i == 0) ? y : sliderY[i - 1]; } + /** * Discards the slider cache (only used for mmsliders). */ diff --git a/tools/JarSplicePlus.jar b/tools/JarSplicePlus.jar deleted file mode 100644 index b6dacdae..00000000 Binary files a/tools/JarSplicePlus.jar and /dev/null differ