From 5225920c78a92d98302e821bc8faa125d2ee56de Mon Sep 17 00:00:00 2001 From: Vince Date: Fri, 27 Mar 2026 17:59:54 +0800 Subject: [PATCH 1/3] FIX --- .github/workflows/windows-build.yml | 71 ++-- docs/plans/2026-03-27-fix-resource-loading.md | 348 ++++++++++++++++++ src/DatabaseManager.java | 31 +- src/Game.java | 3 +- src/MissionPanel.java | 23 +- src/SettingPanel.java | 18 +- src/SoundManager.java | 12 +- src/StartPanel.java | 11 +- 8 files changed, 457 insertions(+), 60 deletions(-) create mode 100644 docs/plans/2026-03-27-fix-resource-loading.md diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 8aa9aef..9989f56 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -1,49 +1,68 @@ -name: Build Windows EXE +name: Build Installers on: push: branches: [ main ] - tags: + tags: - 'v*' jobs: - build-windows: - runs-on: windows-latest + build: + strategy: + matrix: + include: + - os: windows-latest + type: exe + artifact: AquaVision-Windows + - os: macos-latest + type: dmg + artifact: AquaVision-macOS + - os: ubuntu-latest + type: deb + artifact: AquaVision-Linux + runs-on: ${{ matrix.os }} + steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Java 17 + - name: Set up Java 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' + + - name: Download sqlite-jdbc + shell: bash + run: | + curl -L -o sqlite-jdbc.jar https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar - name: Compile Java sources - shell: pwsh + shell: bash run: | - mkdir build - javac -encoding UTF-8 -d build (Get-ChildItem -Recurse -Include *.java src).FullName + mkdir -p build + javac -encoding UTF-8 -cp sqlite-jdbc.jar -d build src/*.java - - name: Create runnable JAR + - name: Create JAR with resources + shell: bash run: | + cp -r resources/* build/ jar cfe AquaVision.jar Main -C build . - - name: Build Windows EXE - shell: cmd + - name: Build native installer + shell: bash run: | - mkdir output - jpackage ^ - --name AquaVision ^ - --input . ^ - --main-jar AquaVision.jar ^ - --main-class Main ^ - --icon resources/appLogo.ico ^ - --type exe ^ - --dest output - - - name: Upload EXE artifact + mkdir -p output + jpackage \ + --name AquaVision \ + --input . \ + --main-jar AquaVision.jar \ + --main-class Main \ + --type ${{ matrix.type }} \ + --dest output \ + --app-version "1.0.0" + + - name: Upload artifact uses: actions/upload-artifact@v4 with: - name: AquaVision-Windows - # This looks inside the 'output' folder for any .exe file - path: output/*.exe + name: ${{ matrix.artifact }} + path: output/* diff --git a/docs/plans/2026-03-27-fix-resource-loading.md b/docs/plans/2026-03-27-fix-resource-loading.md new file mode 100644 index 0000000..1d6b8c5 --- /dev/null +++ b/docs/plans/2026-03-27-fix-resource-loading.md @@ -0,0 +1,348 @@ +# Fix Resource Loading for jpackage Builds + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Fix all resource loading so the app works correctly when packaged with jpackage as a native installer. + +**Architecture:** Replace all file-system relative paths with classpath-based resource loading (`getClass().getResource()`). Include resources in JAR at build time. Handle SQLite DB path using app-relative directory resolution. + +**Tech Stack:** Java 21, Swing, SQLite (sqlite-jdbc), GitHub Actions, jpackage + +--- + +## Problem Summary + +All resources (images, audio, DB) are loaded via relative file paths like `new ImageIcon("resources/background.png")`. This only works when CWD = project root (IDE). After jpackage, CWD is unpredictable, so all resources fail to load silently, causing white screens. + +Additionally, `safeLoadImage()` in MissionPanel has a double-prefix bug, sqlite-jdbc is not included in the JAR, and the workflow only builds for Windows. + +## Resource Loading Points to Fix + +| File | Line | Current (broken) | Target (fixed) | +|------|------|-------------------|-----------------| +| StartPanel.java | 16 | `new ImageIcon("resources/background.png")` | `getClass().getResource("/background.png")` | +| MissionPanel.java | 47,50,61,101 | `safeLoadImage("resources/X.png")` | `safeLoadImage("/X.png")` | +| MissionPanel.java | 78 | `getResource("resources/" + fileName)` | `getResource(fileName)` — remove double prefix | +| MissionPanel.java | 82 | `new ImageIcon(fileName)` fallback | Remove file-system fallback | +| MissionPanel.java | 269 | `"jdbc:sqlite:progress.db"` | Use app-dir-relative path | +| SettingPanel.java | 21 | `new ImageIcon("resources/close.png")` | `getClass().getResource("/close.png")` | +| SettingPanel.java | 47 | `new ImageIcon("resources/level_active.png")` | `getClass().getResource("/level_active.png")` | +| SoundManager.java | 16 | `new File("resources/Child_Game_Bg_Music.wav")` | `getResource("/Child_Game_Bg_Music.wav")` | +| DatabaseManager.java | 7 | `"jdbc:sqlite:progress.db"` | Use app-dir-relative path | + +--- + +### Task 1: Fix MissionPanel resource loading + +**Files:** +- Modify: `src/MissionPanel.java` + +**Step 1: Fix `safeLoadImage` — remove double prefix, remove file-system fallback, add null check** + +```java +private Image safeLoadImage(String resourcePath) { + try { + URL url = getClass().getResource(resourcePath); + if (url != null) { + return new ImageIcon(url).getImage(); + } + System.err.println("Resource not found: " + resourcePath); + return null; + } catch (Exception e) { + System.err.println("Error loading resource: " + resourcePath + " - " + e.getMessage()); + return null; + } +} +``` + +**Step 2: Fix all callers — change `"resources/X.png"` to `"/X.png"`** + +```java +backgroundImage = safeLoadImage("/MissionBackground.png"); +Image imgActive = safeLoadImage("/level_active.png"); +Image imgLocked = safeLoadImage("/level_locked.png"); +Image img = safeLoadImage("/return.png"); +``` + +**Step 3: Commit** + +```bash +git add src/MissionPanel.java +git commit -m "fix: MissionPanel resource loading — use classpath instead of file paths" +``` + +--- + +### Task 2: Fix StartPanel resource loading + +**Files:** +- Modify: `src/StartPanel.java` + +**Step 1: Replace file-system ImageIcon with classpath loading** + +Change line 16-18 from: +```java +ImageIcon backgroundImg = new ImageIcon("resources/background.png"); +``` +To: +```java +URL bgUrl = getClass().getResource("/background.png"); +ImageIcon backgroundImg = (bgUrl != null) ? new ImageIcon(bgUrl) : new ImageIcon(); +``` + +Add import `java.net.URL` if missing. + +**Step 2: Commit** + +```bash +git add src/StartPanel.java +git commit -m "fix: StartPanel background — use classpath resource loading" +``` + +--- + +### Task 3: Fix SettingPanel resource loading + +**Files:** +- Modify: `src/SettingPanel.java` + +**Step 1: Fix close button icon (line 21)** + +```java +URL closeUrl = getClass().getResource("/close.png"); +ImageIcon icon = (closeUrl != null) ? new ImageIcon(closeUrl) : new ImageIcon(); +``` + +**Step 2: Fix slider icon (line 47)** + +```java +URL sliderUrl = getClass().getResource("/level_active.png"); +ImageIcon sliderIcon = (sliderUrl != null) ? new ImageIcon(sliderUrl) : new ImageIcon(); +``` + +Add import `java.net.URL`. + +**Step 3: Commit** + +```bash +git add src/SettingPanel.java +git commit -m "fix: SettingPanel icons — use classpath resource loading" +``` + +--- + +### Task 4: Fix SoundManager resource loading + +**Files:** +- Modify: `src/SoundManager.java` + +**Step 1: Change from File to classpath URL** + +```java +public static void init() { + try { + URL soundUrl = SoundManager.class.getResource("/Child_Game_Bg_Music.wav"); + if (soundUrl == null) { + System.err.println("Sound file not found in classpath"); + return; + } + AudioInputStream audio = AudioSystem.getAudioInputStream(soundUrl); + music = AudioSystem.getClip(); + music.open(audio); + volume = (FloatControl) music.getControl(FloatControl.Type.MASTER_GAIN); + music.loop(Clip.LOOP_CONTINUOUSLY); + } catch (Exception e) { + e.printStackTrace(); + } +} +``` + +Add import `java.net.URL`, remove import `java.io.File`. + +**Step 2: Commit** + +```bash +git add src/SoundManager.java +git commit -m "fix: SoundManager — load audio from classpath instead of file system" +``` + +--- + +### Task 5: Fix DatabaseManager path + +**Files:** +- Modify: `src/DatabaseManager.java` +- Modify: `src/MissionPanel.java` (the inline DB connection at line 269) + +**Step 1: Resolve DB path relative to JAR location** + +In DatabaseManager, replace the static DB_URL with a resolved path: + +```java +private static final String DB_URL; + +static { + String dbPath; + try { + // Resolve path relative to where the JAR/app is located + String jarDir = System.getProperty("user.dir"); + dbPath = new java.io.File(jarDir, "progress.db").getAbsolutePath(); + } catch (Exception e) { + dbPath = "progress.db"; + } + DB_URL = "jdbc:sqlite:" + dbPath; +} +``` + +Note: `user.dir` is the working directory. For jpackage apps on Windows this may not be ideal, but sqlite-jdbc will create the file if it doesn't exist. A better approach for production would be `user.home`, but to keep behavior consistent with current (DB alongside app), we use `user.dir` for now. + +Actually, a more robust approach: use app data directory. + +```java +private static final String DB_URL; + +static { + String userHome = System.getProperty("user.home"); + java.io.File appDir = new java.io.File(userHome, ".aquavision"); + appDir.mkdirs(); + String dbPath = new java.io.File(appDir, "progress.db").getAbsolutePath(); + DB_URL = "jdbc:sqlite:" + dbPath; +} +``` + +**Step 2: Fix MissionPanel inline DB connection (line 269)** + +Replace the hardcoded connection string with `DatabaseManager.getConnection()` or use the same path. Simplest: add a public method to DatabaseManager. + +Add to DatabaseManager: +```java +public static Connection getConnection() throws SQLException { + return DriverManager.getConnection(DB_URL); +} +``` + +Then in MissionPanel line 269: +```java +try (Connection conn = DatabaseManager.getConnection(); +``` + +**Step 3: Commit** + +```bash +git add src/DatabaseManager.java src/MissionPanel.java +git commit -m "fix: DatabaseManager — use stable app data directory for SQLite DB" +``` + +--- + +### Task 6: Fix GitHub Actions workflow — include resources in JAR and add sqlite-jdbc + +**Files:** +- Modify: `.github/workflows/windows-build.yml` + +**Step 1: Download sqlite-jdbc, include resources in JAR, fix compilation classpath** + +```yaml +name: Build Installers +on: + push: + branches: [ main ] + tags: + - 'v*' + +jobs: + build: + strategy: + matrix: + include: + - os: windows-latest + type: exe + artifact: AquaVision-Windows + - os: macos-latest + type: dmg + artifact: AquaVision-macOS + - os: ubuntu-latest + type: deb + artifact: AquaVision-Linux + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Java 21 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '21' + + - name: Download sqlite-jdbc + run: | + curl -L -o sqlite-jdbc.jar https://repo1.maven.org/maven2/org/xerial/sqlite-jdbc/3.45.1.0/sqlite-jdbc-3.45.1.0.jar + + - name: Compile Java sources + shell: bash + run: | + mkdir -p build + javac -encoding UTF-8 -cp sqlite-jdbc.jar -d build src/*.java + + - name: Create JAR with resources + shell: bash + run: | + cp -r resources/* build/ + jar cfe AquaVision.jar Main -C build . + + - name: Build native installer + shell: bash + run: | + mkdir -p output + jpackage \ + --name AquaVision \ + --input . \ + --main-jar AquaVision.jar \ + --main-class Main \ + --type ${{ matrix.type }} \ + --dest output \ + --java-options "-Dsun.java2d.opengl=true" \ + --app-version "1.0.0" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }} + path: output/* +``` + +Note: The `--input .` flag includes the `sqlite-jdbc.jar` in the app directory, and jpackage auto-adds JARs from the input to the classpath. The resources are inside `AquaVision.jar` so they are accessible via `getClass().getResource()`. + +**Step 2: Commit** + +```bash +git add .github/workflows/windows-build.yml +git commit -m "feat: multi-platform build workflow with resources in JAR and sqlite-jdbc" +``` + +--- + +### Task 7: Verification — local build test + +**Step 1: Compile and build JAR locally** + +```bash +mkdir -p build +javac -encoding UTF-8 -d build src/*.java +cp -r resources/* build/ +jar cfe AquaVision.jar Main -C build . +``` + +(Note: compilation will warn about missing sqlite-jdbc at compile time only if needed — the code uses java.sql.* which is in the JDK. sqlite-jdbc is only needed at runtime.) + +**Step 2: Test JAR runs** + +```bash +java -jar AquaVision.jar +``` + +Verify: app launches, Start menu shows background, clicking Start shows MissionPanel with background and level buttons visible. + +**Step 3: Commit all changes if not already committed** diff --git a/src/DatabaseManager.java b/src/DatabaseManager.java index 2a5a68b..8469928 100644 --- a/src/DatabaseManager.java +++ b/src/DatabaseManager.java @@ -4,7 +4,18 @@ import java.util.List; public class DatabaseManager { - private static final String DB_URL = "jdbc:sqlite:progress.db"; + private static final String DB_URL; + + static { + String userHome = System.getProperty("user.home"); + java.io.File appDir = new java.io.File(userHome, ".aquavision"); + appDir.mkdirs(); + DB_URL = "jdbc:sqlite:" + new java.io.File(appDir, "progress.db").getAbsolutePath(); + } + + public static Connection getConnection() throws SQLException { + return DriverManager.getConnection(DB_URL); + } public static void initialize() { String sql = """ @@ -37,7 +48,7 @@ CREATE TABLE IF NOT EXISTS attempt_history ( ); """; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) { stmt.execute(sql); @@ -61,7 +72,7 @@ ON CONFLICT(mission) DO UPDATE SET last_updated = CURRENT_TIMESTAMP """; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, mission); @@ -81,7 +92,7 @@ ON CONFLICT(mission) DO UPDATE SET last_updated = CURRENT_TIMESTAMP """; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, mission); @@ -101,7 +112,7 @@ public static void incrementCompletion(int mission) { WHERE mission = ? """; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, mission); @@ -115,7 +126,7 @@ public static void incrementCompletion(int mission) { public static String getProgressReport(int mission) { String sql = "SELECT * FROM mission_progress WHERE mission = ?"; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, mission); @@ -158,7 +169,7 @@ ON CONFLICT(id) DO UPDATE SET last_updated = CURRENT_TIMESTAMP """; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, isRightEye ? 1 : 0); @@ -174,7 +185,7 @@ ON CONFLICT(id) DO UPDATE SET public static void loadUserSettings() { String sql = "SELECT * FROM user_settings WHERE id = 1"; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { @@ -200,7 +211,7 @@ public static void logAttempt(int mission, int highestPhase, boolean completed, VALUES (?, CURRENT_TIMESTAMP, ?, ?, ?) """; - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, mission); @@ -218,7 +229,7 @@ public static String generateAsciiSuccessRateGraph(int mission) { List successes = new ArrayList<>(); - try (Connection conn = DriverManager.getConnection(DB_URL); + try (Connection conn = getConnection(); PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, mission); diff --git a/src/Game.java b/src/Game.java index 4e254b8..fe10c8d 100644 --- a/src/Game.java +++ b/src/Game.java @@ -4,7 +4,6 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.sql.Connection; -import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; @@ -286,7 +285,7 @@ private void failMission() { int previousRecord = 0; - try (Connection conn = DriverManager.getConnection("jdbc:sqlite:progress.db"); + try (Connection conn = DatabaseManager.getConnection(); PreparedStatement pstmt = conn.prepareStatement( "SELECT highest_phase_reached FROM mission_progress WHERE mission = ?")) { diff --git a/src/MissionPanel.java b/src/MissionPanel.java index 6cbb60e..dd463e9 100644 --- a/src/MissionPanel.java +++ b/src/MissionPanel.java @@ -2,7 +2,10 @@ import java.awt.*; import java.awt.image.ImageFilter; import java.net.URL; -import java.sql.*; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.List; @@ -44,10 +47,10 @@ private void loadImages() { System.out.println("--- Loading Images ---"); // Background - backgroundImage = safeLoadImage("resources/MissionBackground.png"); + backgroundImage = safeLoadImage("/MissionBackground.png"); // Active icon - Image imgActive = safeLoadImage("resources/level_active.png"); + Image imgActive = safeLoadImage("/level_active.png"); if (imgActive != null) { Image scaled = imgActive.getScaledInstance(BUTTON_SIZE, BUTTON_SIZE, Image.SCALE_SMOOTH); shellIcon = new ImageIcon(scaled); @@ -58,7 +61,7 @@ private void loadImages() { } // Locked icon - Image imgLocked = safeLoadImage("resources/level_locked.png"); + Image imgLocked = safeLoadImage("/level_locked.png"); if (imgLocked != null) { Image scaled = imgLocked.getScaledInstance(BUTTON_SIZE, BUTTON_SIZE, Image.SCALE_SMOOTH); lockedIcon = new ImageIcon(scaled); @@ -73,14 +76,16 @@ private void loadImages() { } } - private Image safeLoadImage(String fileName) { + private Image safeLoadImage(String resourcePath) { try { - URL url = getClass().getResource("resources/" + fileName); + URL url = getClass().getResource(resourcePath); if (url != null) { return new ImageIcon(url).getImage(); } - return new ImageIcon(fileName).getImage(); + System.err.println("Resource not found: " + resourcePath); + return null; } catch (Exception e) { + System.err.println("Error loading resource: " + resourcePath + " - " + e.getMessage()); return null; } } @@ -98,7 +103,7 @@ private void initButtons() { JButton returnButton = new JButton(); returnButton.setBounds(10, 10, 50, 50); - Image img = safeLoadImage("resources/return.png"); + Image img = safeLoadImage("/return.png"); if (img != null) { Image scaled = img.getScaledInstance(100, 50, Image.SCALE_SMOOTH); returnButton.setIcon(new ImageIcon(scaled)); @@ -266,7 +271,7 @@ private void loadUnlockedLevelsFromDB() { maxUnlockedLevel = 1; // Varsayılan: sadece Mission 1 açık - try (Connection conn = DriverManager.getConnection("jdbc:sqlite:progress.db"); + try (Connection conn = DatabaseManager.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT MAX(mission) AS last_completed FROM mission_progress WHERE successful_completions > 0")) { diff --git a/src/SettingPanel.java b/src/SettingPanel.java index 02cf0e9..18cdfcb 100644 --- a/src/SettingPanel.java +++ b/src/SettingPanel.java @@ -5,6 +5,7 @@ import java.awt.*; import java.awt.event.ActionListener; import java.awt.geom.RoundRectangle2D; +import java.net.URL; public class SettingPanel extends BasePanel { @@ -18,10 +19,14 @@ public SettingPanel() { setSize(400, 550); // --- Kapatma Butonu --- - ImageIcon icon = new ImageIcon("resources/close.png"); - Image scaled = (icon.getImage() != null) ? icon.getImage().getScaledInstance(60, 40, Image.SCALE_SMOOTH) : null; - JButton closeBtn = new JButton((scaled != null) ? new ImageIcon(scaled) : null); - if (scaled == null) closeBtn.setText("X"); + URL closeUrl = getClass().getResource("/close.png"); + JButton closeBtn; + if (closeUrl != null) { + Image scaled = new ImageIcon(closeUrl).getImage().getScaledInstance(60, 40, Image.SCALE_SMOOTH); + closeBtn = new JButton(new ImageIcon(scaled)); + } else { + closeBtn = new JButton("X"); + } closeBtn.setBounds(320, 10, 60, 40); closeBtn.setBorderPainted(false); closeBtn.setContentAreaFilled(false); closeBtn.setFocusPainted(false); closeBtn.setOpaque(false); closeBtn.addActionListener(e -> { Window w = SwingUtilities.getWindowAncestor(this); if (w != null) w.dispose(); }); @@ -44,8 +49,9 @@ public SettingPanel() { JSlider slider = new JSlider(0, 100, 50); slider.setBounds(30, 85, 300, 40); slider.setPaintTicks(false); slider.setPaintLabels(false); - ImageIcon sliderIcon = new ImageIcon("resources/level_active.png"); - slider.setUI(new SeaSliderUI(slider, (sliderIcon.getImage() != null) ? sliderIcon.getImage() : null)); + URL sliderUrl = getClass().getResource("/level_active.png"); + Image thumbImg = (sliderUrl != null) ? new ImageIcon(sliderUrl).getImage() : null; + slider.setUI(new SeaSliderUI(slider, thumbImg)); slider.setOpaque(false); slider.setBackground(new Color(189, 237, 255)); slider.addChangeListener(e -> { float dB = -70f + 76f * (float) Math.pow(slider.getValue() / 100f, 0.42); diff --git a/src/SoundManager.java b/src/SoundManager.java index 3616ac0..623563c 100644 --- a/src/SoundManager.java +++ b/src/SoundManager.java @@ -2,7 +2,7 @@ import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.FloatControl; -import java.io.File; +import java.net.URL; public class SoundManager { @@ -11,12 +11,16 @@ public class SoundManager { public static void init() { try { - AudioInputStream audio = - AudioSystem.getAudioInputStream( - new File("resources/Child_Game_Bg_Music.wav")); + URL soundUrl = SoundManager.class.getResource("/Child_Game_Bg_Music.wav"); + if (soundUrl == null) { + System.err.println("Sound file not found in classpath"); + return; + } + AudioInputStream audio = AudioSystem.getAudioInputStream(soundUrl); music = AudioSystem.getClip(); music.open(audio); + audio.close(); volume = (FloatControl) music.getControl(FloatControl.Type.MASTER_GAIN); diff --git a/src/StartPanel.java b/src/StartPanel.java index 795f479..2a2b26f 100644 --- a/src/StartPanel.java +++ b/src/StartPanel.java @@ -3,6 +3,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.RoundRectangle2D; +import java.net.URL; public class StartPanel extends BasePanel { private final Image scaledBackground; @@ -13,9 +14,13 @@ public StartPanel() { setLayout(null); // Background - ImageIcon backgroundImg = new ImageIcon("resources/background.png"); - Image img = backgroundImg.getImage(); - scaledBackground = img.getScaledInstance(600, 600, Image.SCALE_SMOOTH); + URL bgUrl = getClass().getResource("/background.png"); + if (bgUrl != null) { + scaledBackground = new ImageIcon(bgUrl).getImage().getScaledInstance(600, 600, Image.SCALE_SMOOTH); + } else { + System.err.println("Resource not found: /background.png"); + scaledBackground = null; + } // --- CHANGED TO ROUNDEDBUTTON --- RoundedButton startBtn = new RoundedButton("Start"); From ff4245f35564583dc8643d5d73d607959b6b11e1 Mon Sep 17 00:00:00 2001 From: Vince Date: Fri, 27 Mar 2026 18:11:28 +0800 Subject: [PATCH 2/3] fixed build --- .github/workflows/windows-build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 9989f56..2eb3335 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -16,9 +16,6 @@ jobs: - os: macos-latest type: dmg artifact: AquaVision-macOS - - os: ubuntu-latest - type: deb - artifact: AquaVision-Linux runs-on: ${{ matrix.os }} steps: From e842ac648093bd482106c5f9cb10c255f581d809 Mon Sep 17 00:00:00 2001 From: Vince Date: Fri, 27 Mar 2026 19:04:13 +0800 Subject: [PATCH 3/3] trigger CI build