diff --git a/flixelgdx/core/build.gradle b/flixelgdx/core/build.gradle index aee4fdf..42fa47f 100644 --- a/flixelgdx/core/build.gradle +++ b/flixelgdx/core/build.gradle @@ -3,6 +3,15 @@ eclipse.project.name = appName + '-flixelgdx-core' apply plugin: "java-library" +// Embed version from build so Flixel.getVersion() can read it at runtime (Gradle does not set JAR manifest like Maven). +def generateFlixelVersion = tasks.register('generateFlixelVersion', WriteProperties) { + outputFile = layout.buildDirectory.file("generated/version/version.properties") + property("version", project.version) +} +processResources.from(generateFlixelVersion) { + into "me/stringdotjar/flixelgdx" +} + java { sourceCompatibility = 17 targetCompatibility = 17 diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java index 9ffcd3f..24f3790 100644 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java +++ b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java @@ -1,5 +1,6 @@ package me.stringdotjar.flixelgdx; +import com.badlogic.gdx.Application.ApplicationType; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.files.FileHandle; @@ -22,6 +23,9 @@ import me.stringdotjar.flixelgdx.signal.FlixelSignalData.SoundPlayedSignalData; import org.jetbrains.annotations.NotNull; +import java.io.InputStream; +import java.util.Properties; + /** * Global manager and utility class for Flixel. * @@ -479,6 +483,22 @@ public static boolean isFullscreen() { return Gdx.graphics.isFullscreen(); } + public static ApplicationType getPlatform() { + return Gdx.app.getType(); + } + + public static String getVersion() { + try (InputStream in = Flixel.class.getResourceAsStream("version.properties")) { + if (in != null) { + Properties p = new Properties(); + p.load(in); + String v = p.getProperty("version"); + if (v != null && !v.isEmpty()) return v; + } + } catch (Exception ignored) {} + return "Unknown"; + } + public static void setLogMode(@NotNull FlixelLogMode mode) { defaultLogger.setLogMode(mode); } diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java index 1cf9760..985ef80 100644 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java +++ b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java @@ -2,7 +2,6 @@ import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.Array; import me.stringdotjar.flixelgdx.util.FlixelConstants; import me.stringdotjar.flixelgdx.util.FlixelRuntimeUtil; import org.jetbrains.annotations.NotNull; @@ -141,8 +140,8 @@ protected void outputLog(String tag, Object message, FlixelLogLevel level) { // Console: use current log mode. String coloredLog; if (logMode == FlixelLogMode.SIMPLE) { - coloredLog = colorText(simpleFile, color, true, false, underlineFile) - + ": " + coloredLog = colorText(simpleFile + ":", color, true, false, underlineFile) + + " " + colorText(rawMessage, color, false, true, false); } else { String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java new file mode 100644 index 0000000..091027d --- /dev/null +++ b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java @@ -0,0 +1,80 @@ +package me.stringdotjar.flixelgdx.util; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * Utility class for obtaining Git information about the current repository Flixel is being ran in. + */ +public final class FlixelGitUtil { + + /** + * A record holding all info about the current Git repository. + * + * @param commit The current commit. + * @param branch The current branch. + * @param remoteUrl The repository that Polyverse is being worked on. + * @param isModified Does the current repository have any changes made to it? + */ + public record RepoInfo(String commit, String branch, String remoteUrl, String isModified) { + @Override + public String commit() { + if (commit == null || commit.isBlank()) { + return "Could not determine Git commit."; + } + return commit.length() > 7 ? commit.substring(0, 8) : commit; + } + + @Override + public String branch() { + if (branch == null || branch.isBlank()) { + return "Could not determine Git branch."; + } + return branch; + } + + @Override + public String remoteUrl() { + if (remoteUrl == null || remoteUrl.isBlank()) { + return "Could not determine Git remote URL."; + } + return remoteUrl; + } + + @Override + public String isModified() { + return (isModified != null && !isModified.isBlank()) ? "Yes" : "No"; + } + } + + /** + * Gets basic Git info about the current app running. + * + * @return A {@code RepoInfo} record with basic info. + */ + public static RepoInfo getRepoInfo() { + String commit = runGitCommand("rev-parse", "HEAD"); + String branch = runGitCommand("rev-parse", "--abbrev-ref", "HEAD"); + String remoteUrl = runGitCommand("config", "--get", "remote.origin.url"); + String isModified = runGitCommand("status", "--porcelain"); + return new RepoInfo(commit, branch, remoteUrl, isModified); + } + + private static String runGitCommand(String... args) { + try { + String[] command = new String[args.length + 1]; + command[0] = "git"; + System.arraycopy(args, 0, command, 1, args.length); + ProcessBuilder pb = new ProcessBuilder(command); + pb.redirectErrorStream(true); + Process process = pb.start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + return reader.readLine(); + } + } catch (Exception e) { + return null; + } + } + + private FlixelGitUtil() {} +} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java index be8f40f..9079425 100644 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java +++ b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java @@ -47,6 +47,69 @@ public static boolean isRunningFromJar() { } } + /** + * Returns {@code true} when the application is running inside an IDE (IntelliJ, Eclipse, Cursor, + * VS Code, etc.), and {@code false} when running from the distribution JAR or plain classpath. + * + *

Detection uses IDE-specific system properties, classpath hints, and the code source path. + * Gradle-based runs are detected via {@code build/classes} (exploded) or a JAR under + * {@code build/libs/} that is not the distribution JAR (no {@code Main-Class} in manifest). + * + * @return {@code true} if running in an IDE, {@code false} otherwise. + */ + public static boolean isRunningInIDE() { + // IntelliJ (run/debug). + if (System.getProperty("idea.launcher.port") != null) { + return true; + } + // IntelliJ fallback: idea_rt.jar on classpath (e.g. debug). + if (System.getProperty("java.class.path", "").contains("idea_rt.jar")) { + return true; + } + // Eclipse. + if (System.getProperty("eclipse.application") != null) { + return true; + } + var path = getWorkingDirectory(); + if (path == null) { + return false; + } + // IntelliJ default output. + if (path.contains("out/production")) { + return true; + } + // Eclipse default output. + if (path.contains("bin/")) { + return true; + } + // Gradle: exploded classes (e.g. build/classes/java/main). + if (path.contains("build/classes")) { + return true; + } + // Gradle: module JAR on classpath (build/libs/*.jar without Main-Class); distribution JAR has Main-Class. + if (path.contains("build/libs") && path.endsWith(".jar") && !isRunningFromJar()) { + return true; + } + return false; + } + + /** + * Detects the current runtime environment. + * + *

This method is used to determine if the application is running in the IDE, from a JAR, or from the classpath. + * + * @return The detected environment. + */ + public static RunEnvironment detectEnvironment() { + if (isRunningInIDE()) { + return RunEnvironment.IDE; + } + if (isRunningFromJar()) { + return RunEnvironment.JAR; + } + return RunEnvironment.CLASSPATH; + } + /** * Returns the working directory of the game. * @@ -72,8 +135,7 @@ public static String getWorkingDirectory() { * @return The root package name of the library. */ public static String getLibraryRoot() { - return FlixelRuntimeUtil.class.getPackageName() - .replaceAll("\\.[^.]+$", ""); + return FlixelRuntimeUtil.class.getPackageName().replaceAll("\\.[^.]+$", ""); } /** @@ -122,5 +184,14 @@ public static String getFullExceptionMessage(Throwable exception) { return messageBuilder.toString(); } + /** + * Enum representing the environment in which Flixel is running in. + */ + public enum RunEnvironment { + IDE, + JAR, + CLASSPATH + } + private FlixelRuntimeUtil() {} } diff --git a/polyverse/build.gradle b/polyverse/build.gradle index 983a7e0..6c123dd 100644 --- a/polyverse/build.gradle +++ b/polyverse/build.gradle @@ -1,6 +1,15 @@ [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' eclipse.project.name = appName + '-polyverse' +// Embed version from gradle.properties (projectVersion) so Polyverse.getVersion() can read it at runtime. +def generatePolyverseVersion = tasks.register('generatePolyverseVersion', WriteProperties) { + outputFile = layout.buildDirectory.file("generated/version/version.properties") + property("version", project.version) +} +processResources.from(generatePolyverseVersion) { + into "me/stringdotjar/polyverse" +} + dependencies { implementation project(":flixelgdx:core") diff --git a/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java b/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java index a58ceb9..1ec8ce5 100644 --- a/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java +++ b/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java @@ -3,14 +3,15 @@ import com.badlogic.gdx.files.FileHandle; import groovy.lang.GroovyClassLoader; import me.stringdotjar.flixelgdx.Flixel; -import me.stringdotjar.flixelgdx.util.FlixelRuntimeUtil; import me.stringdotjar.polyverse.script.type.Script; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.function.Consumer; /** Core manager class for managing the Polyverse modding environment. */ @@ -139,6 +140,23 @@ public static void forAllScripts(Consumer action) { } } + /** + * Gets Polyverse's current version from the value set in {@code gradle.properties} (projectVersion). + * + * @return The current version, or {@code DEV} if the version resource is not available (e.g. run from IDE without building). + */ + public static String getVersion() { + try (InputStream in = Polyverse.class.getResourceAsStream("version.properties")) { + if (in != null) { + Properties p = new Properties(); + p.load(in); + String v = p.getProperty("version"); + if (v != null && !v.isEmpty()) return v; + } + } catch (Exception ignored) {} + return "Unknown"; + } + private static void executeScriptList(List scriptList, Consumer action) { // Use a standard for-loop to prevent ConcurrentModificationException and ensure we are // iterating over the current snapshot of scripts. Plus, it's also to prevent stuttering since we are using diff --git a/polyverse/src/main/java/me/stringdotjar/polyverse/PolyverseApp.java b/polyverse/src/main/java/me/stringdotjar/polyverse/PolyverseApp.java index b528de3..f152c90 100644 --- a/polyverse/src/main/java/me/stringdotjar/polyverse/PolyverseApp.java +++ b/polyverse/src/main/java/me/stringdotjar/polyverse/PolyverseApp.java @@ -4,8 +4,9 @@ import me.stringdotjar.flixelgdx.Flixel; import me.stringdotjar.flixelgdx.FlixelGame; import me.stringdotjar.flixelgdx.display.FlixelState; -import me.stringdotjar.flixelgdx.logging.FlixelLogMode; +import me.stringdotjar.flixelgdx.util.FlixelGitUtil; import me.stringdotjar.flixelgdx.util.FlixelPathsUtil; +import me.stringdotjar.flixelgdx.util.FlixelRuntimeUtil; import me.stringdotjar.polyverse.script.type.Script; import me.stringdotjar.polyverse.script.type.SystemScript; @@ -26,7 +27,10 @@ public void create() { Flixel.setDefaultLogTag("Polyverse"); + displaySystemAndEngineInfo(); + displayGitInfo(); configureScriptSystem(); // Scripting and modding support. + Flixel.info("Polyverse environment has been successfully initialized."); } @Override @@ -78,6 +82,31 @@ public void resize(int width, int height) { Flixel.info("Window resized. (WIDTH=" + width + ", HEIGHT=" + height + ")"); } + private void displaySystemAndEngineInfo() { + Flixel.info("Polyverse Version: " + Polyverse.getVersion()); + Flixel.info("Flixel Version: " + Flixel.getVersion()); + Flixel.info("Java Version: " + System.getProperty("java.version")); + Flixel.info("Java Vendor: " + System.getProperty("java.vendor")); + Flixel.info("Java Vendor URL: " + System.getProperty("java.vendor.url")); + Flixel.info("Runtime Environment: " + FlixelRuntimeUtil.detectEnvironment()); + Flixel.info("Platform: " + Flixel.getPlatform()); + Flixel.info("Operating System: " + System.getProperty("os.name")); + Flixel.info("Operating System Version: " + System.getProperty("os.version")); + } + + private void displayGitInfo() { + if (FlixelRuntimeUtil.isRunningFromJar()) { + return; + } + + var info = FlixelGitUtil.getRepoInfo(); + + Flixel.info("Git Commit: " + info.commit()); + Flixel.info("Git Branch: " + info.branch()); + Flixel.info("Git Remote URL: " + info.remoteUrl()); + Flixel.info("Git Modified?: " + info.isModified()); + } + private void configureScriptSystem() { Flixel.info("Configuring script system...");