diff --git a/.classpath b/.classpath index e7e7bd6..3b53867 100644 --- a/.classpath +++ b/.classpath @@ -1,17 +1,8 @@ - - - - - - - - - - - - + + + diff --git a/build.gradle.kts b/build.gradle.kts index 6996468..b5da36f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,7 @@ val appProperties = Properties().apply { file("app.properties").inputStream().use { load(it) } } +val bootMain = "boot.StarRodBootstrap" val appMain = "app.StarRodMain" val appVersion = appProperties.getProperty("version") @@ -16,12 +17,13 @@ plugins { id("java") id("com.github.johnrengelman.shadow") version "8.1.1" id("net.nemerosa.versioning") version "3.1.0" - id("com.jaredsburrows.license") version "0.9.7" + id("com.cmgapps.licenses") version "4.8.0" } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } } dependencies { @@ -93,8 +95,51 @@ dependencies { implementation("org.ahocorasick:ahocorasick:0.6.3") } + +val bootBuildDir = layout.buildDirectory.dir("bootstrap") +val appBuildDir = layout.buildDirectory.dir("app") +val licenseBuildDir = layout.buildDirectory.dir("reports/licenses/licenseReport") +val releaseBuildDir = layout.buildDirectory.dir("release") + tasks { + val compileBoot = register("compileBoot") { + source = fileTree("src/bootstrap/java") + destinationDirectory.set(bootBuildDir.get().asFile) + classpath = files() // use empty classpath since there are no dependencies + options.release.set(8) + } + + val jarBoot = register("jarBoot") { + dependsOn(compileBoot) + archiveBaseName.set("boot") + manifest { + attributes["Main-Class"] = bootMain + } + from(bootBuildDir) + } + + val compileApp = register("compileApp") { + source = fileTree("src/main/java") + destinationDirectory.set(appBuildDir.get().asFile) + classpath = sourceSets.main.get().compileClasspath // use the default classpath + options.release.set(17) + options.compilerArgs.add("-Xlint:deprecation") + } + + val jarApp = register("jarApp") { + dependsOn(compileApp) + archiveBaseName.set("main-app") + manifest { + attributes["Main-Class"] = appMain + } + from(appBuildDir) + } + shadowJar { + dependsOn(jarBoot, jarApp) + + from(bootBuildDir, appBuildDir) + mergeServiceFiles("META-INF/spring.*") exclude("META-INF/*.SF") exclude("META-INF/*.DSA") @@ -103,8 +148,8 @@ tasks { archiveFileName.set("StarRod.jar") manifest { - attributes["Main-Class"] = "$appMain" - attributes["App-Version"] = "$appVersion" + attributes["Main-Class"] = bootMain + attributes["App-Version"] = appVersion attributes["Build-Branch"] = versioning.info.branchId attributes["Build-Commit"] = versioning.info.commit if (versioning.info.tag != null) @@ -112,7 +157,9 @@ tasks { } } - register("createReleaseZip", Zip::class) { + register("createReleaseZip") { + dependsOn(clean, licenseReport, shadowJar) + group = "release" description = "Create zip file for Star Rod release" @@ -122,8 +169,8 @@ tasks { into("database") } - from(file(layout.buildDirectory.dir("reports"))) { - into("database") + from(file(licenseBuildDir)) { + into("database/licenses") } from(file("exec")) { @@ -141,12 +188,6 @@ tasks { } ) - destinationDirectory.set(layout.buildDirectory.dir("release")) - } - - named("createReleaseZip") { - dependsOn("clean") - dependsOn("shadowJar") - dependsOn("licenseReport") + destinationDirectory.set(releaseBuildDir) } } diff --git a/src/bootstrap/java/boot/StarRodBootstrap.java b/src/bootstrap/java/boot/StarRodBootstrap.java new file mode 100644 index 0000000..3fded9b --- /dev/null +++ b/src/bootstrap/java/boot/StarRodBootstrap.java @@ -0,0 +1,127 @@ +package boot; + +import java.awt.Desktop; +import java.awt.GraphicsEnvironment; +import java.net.URI; + +import javax.swing.JOptionPane; + +public class StarRodBootstrap +{ + public static void main(String[] args) + { + boolean isCommandLine = args.length > 0 || GraphicsEnvironment.isHeadless(); + + String version = System.getProperty("java.version"); + if (getMajorVersion(version) < 17) { + showDownloadMessage(isCommandLine, version); + System.exit(-1); + } + + try { + launchApp(isCommandLine, args); + } + catch (Exception e) { + System.err.println("Exception during bootstrap: " + e.getMessage()); + e.printStackTrace(); + } + } + + private static int getMajorVersion(String version) + { + String[] parts = version.split("\\."); + if (parts[0].equals("1")) { + // Java 8 or earlier: format "1.x" + return Integer.parseInt(parts[1]); + } + else { + // Java 9 or later: format "x" + return Integer.parseInt(parts[0]); + } + } + + private static void launchApp(boolean isCommandLine, String[] args) throws Exception + { + String javaHome = System.getProperty("java.home"); + String javaExec = javaHome + "/bin/java"; + + String jarPath = StarRodBootstrap.class + .getProtectionDomain() + .getCodeSource() + .getLocation() + .toURI() + .getPath(); + + ProcessBuilder processBuilder; + if (args.length > 0) { + processBuilder = new ProcessBuilder( + javaExec, + "-cp", + jarPath, + "app.StarRodMain", + String.join(" ", args).trim()); + } + else { + processBuilder = new ProcessBuilder( + javaExec, + "-cp", + jarPath, + "app.StarRodMain"); + } + + processBuilder.inheritIO(); + Process process = processBuilder.start(); + process.waitFor(); + } + + private static void showDownloadMessage(boolean isCommandLine, String version) + { + String downloadUrl = "https://www.oracle.com/java/technologies/downloads/"; + + if (isCommandLine) { + System.out.println("Java 17 or later is required to run this application."); + System.out.println("Your current version: " + version); + System.out.println("Download the latest version from:"); + System.out.println(downloadUrl); + } + else { + + String errorMsg = "" + + "Java 17 or later is required to run this application.
" + + "Your current version: " + version + "

" + + "Download the latest version from:
" + + "" + downloadUrl + "" + + ""; + + int result = JOptionPane.showOptionDialog( + null, + errorMsg, + "Java Version Error", + JOptionPane.DEFAULT_OPTION, + JOptionPane.ERROR_MESSAGE, + null, + new String[] { "Open Link", "Close" }, + "Open Link" + ); + + if (result == 0) { + openLink(downloadUrl); + } + } + } + + private static void openLink(String url) + { + try { + if (Desktop.isDesktopSupported()) { + Desktop.getDesktop().browse(new URI(url)); + } + else { + System.err.println("Desktop is not supported. Open this link manually: " + url); + } + } + catch (Exception e) { + System.err.println("Failed to open link: " + e.getMessage()); + } + } +} diff --git a/src/main/java/app/StarRodMain.java b/src/main/java/app/StarRodMain.java index b19bd5d..627de1e 100644 --- a/src/main/java/app/StarRodMain.java +++ b/src/main/java/app/StarRodMain.java @@ -585,7 +585,7 @@ public class StarRodMain extends StarRodFrame break; default: - Logger.logfError("Unrecognized command line arg: ", args[i]); + Logger.logfError("Unrecognized command line arg: %s", args[i]); } } } diff --git a/src/main/java/common/BaseEditor.java b/src/main/java/common/BaseEditor.java index 6b298af..87bec06 100644 --- a/src/main/java/common/BaseEditor.java +++ b/src/main/java/common/BaseEditor.java @@ -52,7 +52,6 @@ import app.config.Options.Scope; import common.KeyboardInput.KeyboardInputListener; import common.MouseInput.MouseManagerListener; import game.map.editor.CommandManager; -import game.map.editor.GLEditor; import game.map.editor.Tickable; import game.map.editor.commands.AbstractCommand; import net.miginfocom.swing.MigLayout; diff --git a/src/main/java/game/map/editor/EditorCanvas.java b/src/main/java/common/EditorCanvas.java similarity index 98% rename from src/main/java/game/map/editor/EditorCanvas.java rename to src/main/java/common/EditorCanvas.java index d1d9d3f..dfc8294 100644 --- a/src/main/java/game/map/editor/EditorCanvas.java +++ b/src/main/java/common/EditorCanvas.java @@ -1,4 +1,4 @@ -package game.map.editor; +package common; import static org.lwjgl.opengl.GL.createCapabilities; import static org.lwjgl.opengl.GL11.GL_VERSION; diff --git a/src/main/java/game/map/editor/GLEditor.java b/src/main/java/common/GLEditor.java similarity index 96% rename from src/main/java/game/map/editor/GLEditor.java rename to src/main/java/common/GLEditor.java index e0cd676..b65d283 100644 --- a/src/main/java/game/map/editor/GLEditor.java +++ b/src/main/java/common/GLEditor.java @@ -1,4 +1,4 @@ -package game.map.editor; +package common; import java.awt.Window; diff --git a/src/main/java/game/ProjectDatabase.java b/src/main/java/game/ProjectDatabase.java index 18d1e31..179a06b 100644 --- a/src/main/java/game/ProjectDatabase.java +++ b/src/main/java/game/ProjectDatabase.java @@ -50,7 +50,7 @@ public class ProjectDatabase decompEnums = new CaseInsensitiveMap<>(); DecompEnum.addEnums(decompEnums, Directories.PROJ_INCLUDE.file("enums.h").getAbsolutePath()); DecompEnum.addEnums(decompEnums, Directories.PROJ_INCLUDE.file("effects.h").getAbsolutePath()); - DecompEnum.addEnums(decompEnums, Directories.PROJ_SRC.file("battle/formation_names.h").getAbsolutePath()); + DecompEnum.addEnums(decompEnums, Directories.PROJ_SRC.file("battle/battle_names.h").getAbsolutePath()); DecompEnum.addEnums(decompEnums, Directories.PROJ_SRC.file("battle/stage_names.h").getAbsolutePath()); ESurfaceTypes = decompEnums.get("SurfaceType"); diff --git a/src/main/java/game/map/editor/MapEditor.java b/src/main/java/game/map/editor/MapEditor.java index 4219936..a3d1cae 100644 --- a/src/main/java/game/map/editor/MapEditor.java +++ b/src/main/java/game/map/editor/MapEditor.java @@ -53,6 +53,7 @@ import assets.AssetManager; import assets.ui.SelectMapDialog; import assets.ui.SelectTexDialog; import common.FrameLimiter; +import common.GLEditor; import common.KeyboardInput; import common.KeyboardInput.KeyInputEvent; import common.KeyboardInput.KeyboardInputListener; @@ -183,6 +184,7 @@ public class MapEditor extends GLEditor implements MouseManagerListener, Keyboar private boolean loading = true; private boolean exitCompletely = !Environment.mainConfig.getBoolean(Options.ExitToMenu); + private boolean showLoadingScreen; public boolean needsTextureReload; public boolean needsBackgroundReload; @@ -451,7 +453,6 @@ public class MapEditor extends GLEditor implements MouseManagerListener, Keyboar private CommandManager commandManager; // handles comnmand execution and undo/redo private DrawTrianglesManager drawTriManager; public SwingGUI gui; - private boolean showLoadingScreen; public boolean showLightingPanel; public boolean showMapCameraTab; diff --git a/src/main/java/game/map/editor/camera/OrthographicCamera.java b/src/main/java/game/map/editor/camera/OrthographicCamera.java index 896241e..05018ef 100644 --- a/src/main/java/game/map/editor/camera/OrthographicCamera.java +++ b/src/main/java/game/map/editor/camera/OrthographicCamera.java @@ -279,11 +279,26 @@ public class OrthographicCamera extends MapEditCamera } } + @Override + public void drawBackground() + { + if (editor.gridEnabled) + drawGrid(); + + if (editor.showAxes) + drawAxes(); + + if (editor.gridEnabled || editor.showAxes) { + RenderState.setDepthWrite(false); + LineRenderQueue.render(true); + RenderState.setDepthWrite(true); + } + } + /** * Draw the grid in the background. */ - @Override - public void drawBackground() + public void drawGrid() { int hmin = 0, hmax = 0; int vmin = 0, vmax = 0; @@ -393,20 +408,6 @@ public class OrthographicCamera extends MapEditCamera } } - if (editor.showAxes) { - LineRenderQueue.addLine( - LineRenderQueue.addVertex().setPosition(Short.MIN_VALUE, 0, 0).setColor(PresetColor.RED).getIndex(), - LineRenderQueue.addVertex().setPosition(Short.MAX_VALUE, 0, 0).setColor(PresetColor.RED).getIndex()); - - LineRenderQueue.addLine( - LineRenderQueue.addVertex().setPosition(0, Short.MIN_VALUE, 0).setColor(PresetColor.GREEN).getIndex(), - LineRenderQueue.addVertex().setPosition(0, Short.MAX_VALUE, 0).setColor(PresetColor.GREEN).getIndex()); - - LineRenderQueue.addLine( - LineRenderQueue.addVertex().setPosition(0, 0, Short.MIN_VALUE).setColor(PresetColor.BLUE).getIndex(), - LineRenderQueue.addVertex().setPosition(0, 0, Short.MAX_VALUE).setColor(PresetColor.BLUE).getIndex()); - } - RenderState.setLineWidth(2.0f); RenderState.setColor(0.35f, 0.35f, 0.35f, 1.0f); int v1, v2, v3, v4; @@ -440,11 +441,21 @@ public class OrthographicCamera extends MapEditCamera default: break; } + } + + public void drawAxes() + { + LineRenderQueue.addLine( + LineRenderQueue.addVertex().setPosition(Short.MIN_VALUE, 0, 0).setColor(PresetColor.RED).getIndex(), + LineRenderQueue.addVertex().setPosition(Short.MAX_VALUE, 0, 0).setColor(PresetColor.RED).getIndex()); - RenderState.setDepthWrite(false); - LineRenderQueue.render(true); - RenderState.setDepthWrite(true); + LineRenderQueue.addLine( + LineRenderQueue.addVertex().setPosition(0, Short.MIN_VALUE, 0).setColor(PresetColor.GREEN).getIndex(), + LineRenderQueue.addVertex().setPosition(0, Short.MAX_VALUE, 0).setColor(PresetColor.GREEN).getIndex()); + LineRenderQueue.addLine( + LineRenderQueue.addVertex().setPosition(0, 0, Short.MIN_VALUE).setColor(PresetColor.BLUE).getIndex(), + LineRenderQueue.addVertex().setPosition(0, 0, Short.MAX_VALUE).setColor(PresetColor.BLUE).getIndex()); } @Override diff --git a/src/main/java/game/map/editor/camera/OrthographicViewport.java b/src/main/java/game/map/editor/camera/OrthographicViewport.java index 2fbcab0..d760c12 100644 --- a/src/main/java/game/map/editor/camera/OrthographicViewport.java +++ b/src/main/java/game/map/editor/camera/OrthographicViewport.java @@ -112,8 +112,7 @@ public class OrthographicViewport extends MapEditViewport camera.glLoadTransform(); RenderState.setModelMatrix(null); - if (editor.gridEnabled) - camera.drawBackground(); + camera.drawBackground(); Map shapeMap = editor.getGeometryMap(); Map hitMap = editor.getCollisionMap(); diff --git a/src/main/java/game/map/editor/ui/SwingGUI.java b/src/main/java/game/map/editor/ui/SwingGUI.java index 0d22349..7033e08 100644 --- a/src/main/java/game/map/editor/ui/SwingGUI.java +++ b/src/main/java/game/map/editor/ui/SwingGUI.java @@ -57,6 +57,7 @@ import assets.AssetManager; import assets.ui.SelectBackgroundDialog; import assets.ui.SelectMapDialog; import assets.ui.SelectTexDialog; +import common.EditorCanvas; import game.ProjectDatabase; import game.map.Map; import game.map.Map.SetBackground; @@ -64,7 +65,6 @@ import game.map.Map.ToggleBackground; import game.map.Map.ToggleStage; import game.map.MapObject; import game.map.MapObject.MapObjectType; -import game.map.editor.EditorCanvas; import game.map.editor.EditorShortcut; import game.map.editor.MapEditor; import game.map.editor.MapEditor.EditorMode; diff --git a/src/main/java/game/map/impex/ExportDialog.java b/src/main/java/game/map/impex/ExportDialog.java new file mode 100644 index 0000000..56f3b0c --- /dev/null +++ b/src/main/java/game/map/impex/ExportDialog.java @@ -0,0 +1,82 @@ +package game.map.impex; + +import java.awt.event.WindowEvent; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import app.Environment; +import app.SwingUtils; +import game.map.editor.ui.dialogs.SaveFileChooser; +import net.miginfocom.swing.MigLayout; + +public class ExportDialog extends JDialog +{ + public static final String FRAME_TITLE = "Export"; + + private JComboBox fileTypeComboBox; + private JCheckBox cbSelectedOnly; + + public ExportDialog(JFrame parent) + { + super(parent); + + JButton selectButton = new JButton("Export"); + SwingUtils.addBorderPadding(selectButton); + selectButton.addActionListener((e) -> { + + File mapDir = Environment.getProjectDirectory(); + SaveFileChooser chooser = new SaveFileChooser(mapDir, "Export Geometry", + "Exportables", "obj", "fbx", "prefab"); + + //TODO select file + setVisible(false); + }); + + JButton cancelButton = new JButton("Cancel"); + SwingUtils.addBorderPadding(cancelButton); + cancelButton.addActionListener((e) -> { + setVisible(false); + }); + + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) + { + setVisible(false); + } + }); + + fileTypeComboBox = new JComboBox<>(ImpexFormat.values()); + SwingUtils.addBorderPadding(fileTypeComboBox); + + cbSelectedOnly = new JCheckBox(" Selected object only"); + cbSelectedOnly.setSelected(true); + + setLayout(new MigLayout("ins 16, fill, hidemode 3, wrap 2")); + + add(new JLabel("File Format")); + add(fileTypeComboBox, "growx"); + add(cbSelectedOnly, "span, growx, gapbottom 8"); + + add(new JPanel(), "span, split 3, growx, sg but"); + add(cancelButton, "growx, sg but"); + add(selectButton, "growx, sg but"); + + pack(); + setResizable(false); + + setTitle(FRAME_TITLE); + setIconImage(Environment.getDefaultIconImage()); + setLocationRelativeTo(parent); + setModal(false); + } +} diff --git a/src/main/java/game/map/impex/ImpexFormat.java b/src/main/java/game/map/impex/ImpexFormat.java new file mode 100644 index 0000000..0aa1368 --- /dev/null +++ b/src/main/java/game/map/impex/ImpexFormat.java @@ -0,0 +1,21 @@ +package game.map.impex; + +public enum ImpexFormat +{ + PREFAB("prefab"), + OBJ("obj"), + FBX("fbx"); + + private final String name; + + private ImpexFormat(String name) + { + this.name = name; + } + + @Override + public String toString() + { + return name; + } +} diff --git a/src/main/java/game/map/impex/ImportDialog.java b/src/main/java/game/map/impex/ImportDialog.java new file mode 100644 index 0000000..e336fbe --- /dev/null +++ b/src/main/java/game/map/impex/ImportDialog.java @@ -0,0 +1,75 @@ +package game.map.impex; + +import java.awt.event.WindowEvent; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import app.Environment; +import app.SwingUtils; +import net.miginfocom.swing.MigLayout; + +public class ImportDialog extends JDialog +{ + public static final String FRAME_TITLE = "Import"; + + private JComboBox fileTypeComboBox; + private JCheckBox cbSelectedOnly; + + public ImportDialog(JFrame parent) + { + super(parent); + + JButton selectButton = new JButton("Import"); + SwingUtils.addBorderPadding(selectButton); + selectButton.addActionListener((e) -> { + //TODO select file + setVisible(false); + }); + + JButton cancelButton = new JButton("Cancel"); + SwingUtils.addBorderPadding(cancelButton); + cancelButton.addActionListener((e) -> { + setVisible(false); + }); + + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) + { + setVisible(false); + } + }); + + fileTypeComboBox = new JComboBox<>(ImpexFormat.values()); + SwingUtils.addBorderPadding(fileTypeComboBox); + + cbSelectedOnly = new JCheckBox(" Selected object only"); + cbSelectedOnly.setSelected(true); + + setLayout(new MigLayout("ins 16, fill, hidemode 3, wrap 2")); + + add(new JLabel("File Format")); + add(fileTypeComboBox, "growx"); + add(cbSelectedOnly, "span, growx, gapbottom 8"); + + add(new JPanel(), "span, split 3, growx, sg but"); + add(cancelButton, "growx, sg but"); + add(selectButton, "growx, sg but"); + + pack(); + setResizable(false); + + setTitle(FRAME_TITLE); + setIconImage(Environment.getDefaultIconImage()); + setLocationRelativeTo(parent); + setModal(false); + } +}