diff --git a/database/BSK.db-shm b/database/BSK.db-shm
index 6c77a27..224e88b 100644
Binary files a/database/BSK.db-shm and b/database/BSK.db-shm differ
diff --git a/pom.xml b/pom.xml
index 8ad3f8f..c1073c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -246,12 +246,5 @@
sqlite-jdbc
3.45.1.0
-
- com.yourcompany
- jasper-fonts-timesnewroman
- 1.0
- system
- ${project.basedir}/src/main/resources/fonts/timesnewroman-fonts.jar
-
\ No newline at end of file
diff --git a/src/main/java/BsK/client/Client.java b/src/main/java/BsK/client/Client.java
index a98fa62..2d25915 100644
--- a/src/main/java/BsK/client/Client.java
+++ b/src/main/java/BsK/client/Client.java
@@ -13,6 +13,7 @@ import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
+import io.netty.handler.timeout.IdleStateHandler;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
@@ -45,11 +46,13 @@ public class Client {
1. Máy chủ chưa được khởi động
2. Địa chỉ hoặc cổng không chính xác
3. Tường lửa đang chặn kết nối
+ 4. Mạng internet/WiFi bị ngắt kết nối
Giải pháp:
- 1. Kiểm tra xem máy chủ đã được khởi động chưa
- 2. Kiểm tra lại cấu hình trong file config.properties
- 3. Tắt ứng dụng và khởi động lại
+ 1. Kiểm tra kết nối internet/WiFi
+ 2. Kiểm tra xem máy chủ đã được khởi động chưa
+ 3. Kiểm tra lại cấu hình trong file config.properties
+ 4. Tắt ứng dụng và khởi động lại
Chi tiết lỗi: %s
""",
@@ -186,16 +189,37 @@ public class Client {
true,
new DefaultHttpHeaders(),
50 * 1024 * 1024))
+ // Add IdleStateHandler with longer timeouts for network resilience
+ // Read timeout: 60 seconds, Write timeout: 30 seconds
+ .addLast(new IdleStateHandler(60, 30, 0))
.addLast("ws", ClientHandler.INSTANCE);
}
});
log.info("Connecting to {}:{}", serverAddress, 1999);
Channel channel = bootstrap.connect(serverAddress, 1999).sync().channel();
+
+ // Store the channel for potential reconnection
+ ClientHandler.ctx = channel.pipeline().get(ClientHandler.class).ctx;
+
+ // Wait for channel to close, but handle it gracefully
channel.closeFuture().sync();
} catch (Exception e) {
log.error("Connection error", e);
- showConnectionErrorDialog(serverAddress, 1999, e);
+
+ // Show more specific error message for different types of connection failures
+ if (e instanceof java.net.ConnectException) {
+ showConnectionErrorDialog(serverAddress, 1999,
+ new Exception("Không thể kết nối đến máy chủ. Máy chủ có thể chưa được khởi động hoặc địa chỉ/cổng không đúng."));
+ } else if (e instanceof java.net.UnknownHostException) {
+ showConnectionErrorDialog(serverAddress, 1999,
+ new Exception("Không tìm thấy máy chủ. Kiểm tra lại địa chỉ máy chủ và kết nối mạng."));
+ } else if (e instanceof java.net.SocketTimeoutException) {
+ showConnectionErrorDialog(serverAddress, 1999,
+ new Exception("Hết thời gian chờ kết nối. Kiểm tra kết nối mạng và thử lại."));
+ } else {
+ showConnectionErrorDialog(serverAddress, 1999, e);
+ }
return;
} finally {
try {
diff --git a/src/main/java/BsK/client/network/handler/ClientHandler.java b/src/main/java/BsK/client/network/handler/ClientHandler.java
index d6c9c3b..b4194b1 100644
--- a/src/main/java/BsK/client/network/handler/ClientHandler.java
+++ b/src/main/java/BsK/client/network/handler/ClientHandler.java
@@ -13,10 +13,13 @@ import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j;
@@ -25,6 +28,12 @@ public class ClientHandler extends SimpleChannelInboundHandler, List>> listeners = new ConcurrentHashMap<>();
@@ -113,6 +122,14 @@ public class ClientHandler extends SimpleChannelInboundHandler {
+ String message = """
+ Phát hiện vấn đề kết nối mạng!
+
+ Tình trạng:
+ • Mạng internet chậm hoặc không ổn định
+ • Một số yêu cầu có thể bị trễ hoặc thất bại
+ • Ứng dụng sẽ tiếp tục hoạt động nhưng có thể chậm
+
+ Nguyên nhân có thể:
+ 1. Kết nối WiFi/Internet không ổn định
+ 2. Tốc độ mạng chậm
+ 3. Gián đoạn mạng tạm thời
+
+ Giải pháp:
+ 1. Kiểm tra kết nối internet/WiFi
+ 2. Thử thao tác lại nếu có lỗi
+ 3. Đợi mạng ổn định hơn
+ 4. Khởi động lại ứng dụng nếu cần thiết
+
+ Ứng dụng sẽ tiếp tục hoạt động...
+ """;
+
+ int option = JOptionPane.showOptionDialog(
+ null,
+ message,
+ "Cảnh Báo - Vấn Đề Mạng",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE,
+ null,
+ new String[]{"Tiếp Tục", "Đóng Ứng Dụng"},
+ "Tiếp Tục"
+ );
+
+ if (option == 1) { // User chose to close application
+ System.exit(0);
+ }
+
+ // Reset the flag after some time to allow showing the dialog again if needed
+ new Thread(() -> {
+ try {
+ Thread.sleep(NETWORK_ISSUE_DIALOG_COOLDOWN);
+ internetIssueDialogShown = false;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }).start();
+ });
+ }
+
+ private void showInternetIssueDialogBlocking() {
+ long currentTime = System.currentTimeMillis();
+
+ // Only show dialog if cooldown period has passed to avoid spam
+ if ((internetIssueDialogShown && (currentTime - lastNetworkIssueTime) < NETWORK_ISSUE_DIALOG_COOLDOWN) || dialogCurrentlyShowing) {
+ return;
+ }
+
+ internetIssueDialogShown = true;
+ dialogCurrentlyShowing = true;
+ lastNetworkIssueTime = currentTime;
+
+ // Show dialog directly on current thread to ensure it displays before app can exit
+ try {
+ // Force the dialog to show on the EDT and block until it's handled
+ if (SwingUtilities.isEventDispatchThread()) {
+ // If already on EDT, show directly
+ showNetworkIssueDialog();
+ } else {
+ // If on another thread, switch to EDT and wait
+ SwingUtilities.invokeAndWait(this::showNetworkIssueDialog);
+ }
+ } catch (InterruptedException | InvocationTargetException e) {
+ dialogCurrentlyShowing = false;
+ System.err.println("Error showing dialog: " + e.getMessage());
+ // Show a simple message as fallback
+ System.err.println("NETWORK ISSUE DETECTED - Please restart the application");
+ }
+
+ dialogCurrentlyShowing = false;
+
+ // Reset the flag after some time to allow showing the dialog again if needed
+ new Thread(() -> {
+ try {
+ Thread.sleep(NETWORK_ISSUE_DIALOG_COOLDOWN);
+ internetIssueDialogShown = false;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }, "NetworkDialogReset").start();
+ }
+
+ private void showNetworkIssueDialog() {
+ String message = """
+ Phát hiện vấn đề kết nối mạng!
+
+ Tình trạng:
+ • Mạng internet chậm hoặc không ổn định
+ • Một số yêu cầu có thể bị trễ hoặc thất bại
+ • Ứng dụng sẽ tiếp tục hoạt động nhưng có thể chậm
+
+ Nguyên nhân có thể:
+ 1. Kết nối WiFi/Internet không ổn định
+ 2. Tốc độ mạng chậm
+ 3. Gián đoạn mạng tạm thời
+
+ Giải pháp:
+ 1. Kiểm tra kết nối internet/WiFi
+ 2. Thử thao tác lại nếu có lỗi
+ 3. Đợi mạng ổn định hơn
+ 4. Khởi động lại ứng dụng nếu cần thiết
+
+ Ứng dụng sẽ tiếp tục hoạt động...
+ """;
+
+ int option = JOptionPane.showOptionDialog(
+ null,
+ message,
+ "Cảnh Báo - Vấn Đề Mạng",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE,
+ null,
+ new String[]{"Tiếp Tục", "Đóng Ứng Dụng"},
+ "Tiếp Tục"
+ );
+
+ if (option == 1) { // User chose to close application
+ System.exit(0);
+ }
+ }
+
+ private void showConnectionLostDialog() {
+ SwingUtilities.invokeLater(() -> {
+ String message = """
+ Mất kết nối với máy chủ!
+
+ Nguyên nhân có thể:
+ 1. Mạng internet bị ngắt kết nối
+ 2. WiFi bị mất kết nối
+ 3. Máy chủ đã ngừng hoạt động
+ 4. Lỗi mạng tạm thời
+
+ Giải pháp:
+ 1. Kiểm tra kết nối internet/WiFi
+ 2. Khởi động lại ứng dụng
+ 3. Liên hệ quản trị viên nếu vấn đề tiếp tục
+
+ Ứng dụng sẽ đóng sau khi nhấn OK.
+ """;
+
+ JOptionPane.showMessageDialog(
+ null,
+ message,
+ "Cảnh Báo - Mất Kết Nối",
+ JOptionPane.WARNING_MESSAGE
+ );
+ });
+ }
+
@SuppressWarnings("unchecked")
private void notifyListener(ResponseListener> listener, T response) {
try {
diff --git a/src/main/java/BsK/server/Server.java b/src/main/java/BsK/server/Server.java
index e020e20..4ff0779 100644
--- a/src/main/java/BsK/server/Server.java
+++ b/src/main/java/BsK/server/Server.java
@@ -17,6 +17,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
+import io.netty.handler.timeout.IdleStateHandler;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
@@ -293,6 +294,9 @@ public class Server {
.addLast(new ChunkedWriteHandler())
.addLast(new HttpObjectAggregator(50 * 1024 * 1024))
.addLast(new WebSocketServerProtocolHandler("/", null, true, 50 * 1024 * 1024))
+ // Add IdleStateHandler with longer timeout to be more tolerant of network issues
+ // Reader timeout: 90 seconds (longer than client's 60s to allow for network delays)
+ .addLast(new IdleStateHandler(90, 0, 0))
.addLast(new ServerHandler());
}
});
diff --git a/update_client.bat b/update_client.bat
new file mode 100644
index 0000000..93c79ba
--- /dev/null
+++ b/update_client.bat
@@ -0,0 +1,20 @@
+@echo off
+REM BSK Client Update Script Launcher
+REM This batch file runs the PowerShell update script with proper execution policy
+
+echo BSK Client Update Launcher
+echo =========================
+echo.
+
+REM Change to the script directory
+cd /d "%~dp0"
+
+REM Run the PowerShell script with execution policy bypass
+powershell -ExecutionPolicy Bypass -File ".\update_client_script.ps1"
+
+REM Keep the window open if there's an error
+if errorlevel 1 (
+ echo.
+ echo An error occurred. Press any key to exit...
+ pause >nul
+)
diff --git a/update_client_script.ps1 b/update_client_script.ps1
new file mode 100644
index 0000000..7ef4e2e
--- /dev/null
+++ b/update_client_script.ps1
@@ -0,0 +1,175 @@
+<#
+.SYNOPSIS
+ Checks for a new version of the client JAR application, downloads it, and replaces the old version.
+
+.DESCRIPTION
+ This script is dedicated to updating the client application.
+ It reads a local version_client.json file to determine the current version.
+ It then fetches a remote latest_client.json file from a specified URL to see if a newer version is available.
+ If an update is found, it stops the running client, downloads the new JAR,
+ backs it up, installs the new one, and restarts the client.
+
+.NOTES
+ Author: Dat Le
+ Date: 2025-09-07
+ Version: 1.1.0 (Client Only)
+#>
+
+# --- CONFIGURATION ---
+# IMPORTANT: This URL should now point to your 'latest_client.json' file on GitHub.
+$remoteVersionInfoUrl = "https://github.com/lds217/BSK-All-in-One-Clinic-Management-System/releases/download/v1.1.0/latest.json"
+
+# --- SCRIPT START ---
+$ProgressPreference = 'Continue'
+
+# Get the directory where this script is located.
+$scriptPath = $PSScriptRoot
+# Get the directory where this script is located.
+$scriptPath = $PSScriptRoot
+
+# Define the paths for the local files (now client-specific)
+$localVersionFile = Join-Path $scriptPath "version_client.json"
+$jarFileName = "client.jar"
+$localJarFile = Join-Path $scriptPath $jarFileName
+$backupJarFile = Join-Path $scriptPath "$($jarFileName).old"
+
+# --- Step 1: Read the local version information ---
+Write-Host "Reading local client version info from '$localVersionFile'..."
+if (-not (Test-Path $localVersionFile)) {
+ Write-Error "Error: Local version file not found at '$localVersionFile'. Cannot proceed."
+ Read-Host "Press Enter to exit"
+ exit 1
+}
+
+try {
+ $localVersionInfo = Get-Content $localVersionFile | ConvertFrom-Json
+ Write-Host " - Local Client Version: $($localVersionInfo.currentVersion)"
+}
+catch {
+ Write-Error "Error: Could not read or parse the local version_client.json file. Is it valid JSON?"
+ Read-Host "Press Enter to exit"
+ exit 1
+}
+
+
+# --- Step 2: Fetch the remote version information ---
+Write-Host "Fetching remote client version info from '$remoteVersionInfoUrl'..."
+try {
+ $remoteVersionInfo = Invoke-RestMethod -Uri $remoteVersionInfoUrl
+ Write-Host " - Latest Remote Version: $($remoteVersionInfo.latestVersion)"
+}
+catch {
+ Write-Error "Error: Failed to fetch or parse remote version info. Check the URL and your internet connection."
+ Read-Host "Press Enter to exit"
+ exit 1
+}
+
+
+# --- Step 3: Compare versions ---
+if ([version]$localVersionInfo.currentVersion -ge [version]$remoteVersionInfo.latestVersion) {
+ Write-Host "`nResult: Your client application is up-to-date! (Version $($localVersionInfo.currentVersion))"
+}
+else {
+ Write-Host "`nResult: New version available! (Version $($remoteVersionInfo.latestVersion)). Starting update process."
+ Write-Host "Release Notes: $($remoteVersionInfo.releaseNotes)"
+
+ # Get the correct download URL (now simplified)
+ $downloadUrl = $remoteVersionInfo.files.client
+ if (-not $downloadUrl) {
+ Write-Error "Error: 'downloadUrl' not found in remote JSON."
+ Read-Host "Press Enter to exit"
+ exit 1
+ }
+
+ Write-Host "Download URL: $downloadUrl"
+
+ # --- Step 4: Stop the application ---
+ Write-Host "`nStopping application..."
+ try {
+ # This command finds the java process running 'client.jar' and stops it.
+ Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like "*$jarFileName*" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
+ Write-Host " - Application stopped successfully."
+ Start-Sleep -Seconds 3 # Give the OS time to release file locks
+ }
+ catch {
+ Write-Warning "Could not automatically stop the application. Please stop it manually before continuing."
+ Read-Host "Press Enter to continue after stopping the application"
+ }
+
+
+ # --- Step 5: Download and Replace ---
+ $tempDownloadPath = Join-Path $scriptPath "client.jar.download"
+
+ Write-Host "`nDownloading new version..."
+ try {
+ # First, get the file size for progress calculation
+ $response = Invoke-WebRequest -Uri $downloadUrl -Method Head
+ $totalSize = [int64]$response.Headers.'Content-Length'
+
+ Write-Host "File size: $([math]::Round($totalSize/1MB, 2)) MB"
+ Write-Host "Starting download with progress indicator..."
+
+ # Use Start-BitsTransfer for better progress display if available
+ if (Get-Command Start-BitsTransfer -ErrorAction SilentlyContinue) {
+ Start-BitsTransfer -Source $downloadUrl -Destination $tempDownloadPath -DisplayName "Downloading client.jar" -Description "BSK Client Update"
+ }
+ else {
+ # Fallback: Custom progress implementation
+ $webClient = New-Object System.Net.WebClient
+
+ # Register progress event
+ Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
+ $percent = $Event.SourceEventArgs.ProgressPercentage
+ $received = $Event.SourceEventArgs.BytesReceived
+ $total = $Event.SourceEventArgs.TotalBytesToReceive
+ $speed = [math]::Round($received / 1MB, 2)
+
+ Write-Progress -Activity "Downloading client.jar" -Status "$percent% Complete" -PercentComplete $percent -CurrentOperation "Downloaded: $speed MB / $([math]::Round($total/1MB, 2)) MB"
+ } | Out-Null
+
+ try {
+ $webClient.DownloadFile($downloadUrl, $tempDownloadPath)
+ Write-Progress -Activity "Downloading client.jar" -Completed
+ }
+ finally {
+ $webClient.Dispose()
+ Get-EventSubscriber | Unregister-Event
+ }
+ }
+
+ Write-Host "Download completed successfully!"
+ }
+ catch {
+ Write-Error "Error: Download failed. Please check the URL and your connection."
+ if (Test-Path $tempDownloadPath) { Remove-Item $tempDownloadPath }
+ Read-Host "Press Enter to exit"
+ exit 1
+ }
+
+ Write-Host "`nInstalling update..."
+ if (Test-Path $localJarFile) {
+ Write-Host " - Backing up old version to '$backupJarFile'..."
+ Move-Item -Path $localJarFile -Destination $backupJarFile -Force
+ }
+
+ Write-Host " - Installing new version..."
+ Move-Item -Path $tempDownloadPath -Destination $localJarFile -Force
+
+
+ # --- Step 6: Update the local version file ---
+ Write-Host " - Updating local version number..."
+ # Create a new JSON object to ensure the file is clean
+ @{ currentVersion = $remoteVersionInfo.latestVersion } | ConvertTo-Json -Depth 1 | Set-Content $localVersionFile
+
+
+ # --- Step 7: Restart the application ---
+ # !! CRITICAL !! You MUST customize this step.
+ Write-Host "`nRestarting application (you may need to customize this step)..."
+ # Example: Start-Process "java" -ArgumentList "-jar", $localJarFile
+ # Example: Start-Process ".\start_client.bat"
+
+ Write-Host "`nUpdate complete! Now running version $($remoteVersionInfo.latestVersion)."
+}
+
+# Pause at the end
+Read-Host "`nPress Enter to exit."
diff --git a/update_server.bat b/update_server.bat
new file mode 100644
index 0000000..8ca2095
--- /dev/null
+++ b/update_server.bat
@@ -0,0 +1,20 @@
+@echo off
+REM BSK Server Update Script Launcher
+REM This batch file runs the PowerShell update script with proper execution policy
+
+echo BSK Server Update Launcher
+echo ========================
+echo.
+
+REM Change to the script directory
+cd /d "%~dp0"
+
+REM Run the PowerShell script with execution policy bypass
+powershell -ExecutionPolicy Bypass -File ".\update_server_script.ps1"
+
+REM Keep the window open if there's an error
+if errorlevel 1 (
+ echo.
+ echo An error occurred. Press any key to exit...
+ pause >nul
+)
diff --git a/update_server_script.ps1 b/update_server_script.ps1
new file mode 100644
index 0000000..c8f1141
--- /dev/null
+++ b/update_server_script.ps1
@@ -0,0 +1,175 @@
+<#
+.SYNOPSIS
+ Checks for a new version of the server JAR application, downloads it, and replaces the old version.
+
+.DESCRIPTION
+ This script is dedicated to updating the server application.
+ It reads a local version_server.json file to determine the current version.
+ It then fetches a remote latest_server.json file from a specified URL to see if a newer version is available.
+ If an update is found, it stops the running server, downloads the new JAR,
+ backs it up, installs the new one, and restarts the server.
+
+.NOTES
+ Author: Dat Le
+ Date: 2025-09-07
+ Version: 1.1.0 (Server Only)
+#>
+
+# --- CONFIGURATION ---
+# IMPORTANT: This URL should now point to your 'latest_server.json' file on GitHub.
+$remoteVersionInfoUrl = "https://github.com/lds217/BSK-All-in-One-Clinic-Management-System/releases/download/v1.1.0/latest.json"
+
+# --- SCRIPT START ---
+$ProgressPreference = 'Continue'
+
+# Get the directory where this script is located.
+$scriptPath = $PSScriptRoot
+# Get the directory where this script is located.
+$scriptPath = $PSScriptRoot
+
+# Define the paths for the local files (now server-specific)
+$localVersionFile = Join-Path $scriptPath "version_server.json"
+$jarFileName = "server.jar"
+$localJarFile = Join-Path $scriptPath $jarFileName
+$backupJarFile = Join-Path $scriptPath "$($jarFileName).old"
+
+# --- Step 1: Read the local version information ---
+Write-Host "Reading local server version info from '$localVersionFile'..."
+if (-not (Test-Path $localVersionFile)) {
+ Write-Error "Error: Local version file not found at '$localVersionFile'. Cannot proceed."
+ Read-Host "Press Enter to exit"
+ exit 1
+}
+
+try {
+ $localVersionInfo = Get-Content $localVersionFile | ConvertFrom-Json
+ Write-Host " - Local Server Version: $($localVersionInfo.currentVersion)"
+}
+catch {
+ Write-Error "Error: Could not read or parse the local version_server.json file. Is it valid JSON?"
+ Read-Host "Press Enter to exit"
+ exit 1
+}
+
+
+# --- Step 2: Fetch the remote version information ---
+Write-Host "Fetching remote server version info from '$remoteVersionInfoUrl'..."
+try {
+ $remoteVersionInfo = Invoke-RestMethod -Uri $remoteVersionInfoUrl
+ Write-Host " - Latest Remote Version: $($remoteVersionInfo.latestVersion)"
+}
+catch {
+ Write-Error "Error: Failed to fetch or parse remote version info. Check the URL and your internet connection."
+ Read-Host "Press Enter to exit"
+ exit 1
+}
+
+
+# --- Step 3: Compare versions ---
+if ([version]$localVersionInfo.currentVersion -ge [version]$remoteVersionInfo.latestVersion) {
+ Write-Host "`nResult: Your server application is up-to-date! (Version $($localVersionInfo.currentVersion))"
+}
+else {
+ Write-Host "`nResult: New version available! (Version $($remoteVersionInfo.latestVersion)). Starting update process."
+ Write-Host "Release Notes: $($remoteVersionInfo.releaseNotes)"
+
+ # Get the correct download URL (now simplified)
+ $downloadUrl = $remoteVersionInfo.files.server
+ if (-not $downloadUrl) {
+ Write-Error "Error: 'downloadUrl' not found in remote JSON."
+ Read-Host "Press Enter to exit"
+ exit 1
+ }
+
+ Write-Host "Download URL: $downloadUrl"
+
+ # --- Step 4: Stop the application ---
+ Write-Host "`nStopping application..."
+ try {
+ # This command finds the java process running 'server.jar' and stops it.
+ Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like "*$jarFileName*" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
+ Write-Host " - Application stopped successfully."
+ Start-Sleep -Seconds 3 # Give the OS time to release file locks
+ }
+ catch {
+ Write-Warning "Could not automatically stop the application. Please stop it manually before continuing."
+ Read-Host "Press Enter to continue after stopping the application"
+ }
+
+
+ # --- Step 5: Download and Replace ---
+ $tempDownloadPath = Join-Path $scriptPath "server.jar.download"
+
+ Write-Host "`nDownloading new version..."
+ try {
+ # First, get the file size for progress calculation
+ $response = Invoke-WebRequest -Uri $downloadUrl -Method Head
+ $totalSize = [int64]$response.Headers.'Content-Length'
+
+ Write-Host "File size: $([math]::Round($totalSize/1MB, 2)) MB"
+ Write-Host "Starting download with progress indicator..."
+
+ # Use Start-BitsTransfer for better progress display if available
+ if (Get-Command Start-BitsTransfer -ErrorAction SilentlyContinue) {
+ Start-BitsTransfer -Source $downloadUrl -Destination $tempDownloadPath -DisplayName "Downloading server.jar" -Description "BSK Server Update"
+ }
+ else {
+ # Fallback: Custom progress implementation
+ $webClient = New-Object System.Net.WebClient
+
+ # Register progress event
+ Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
+ $percent = $Event.SourceEventArgs.ProgressPercentage
+ $received = $Event.SourceEventArgs.BytesReceived
+ $total = $Event.SourceEventArgs.TotalBytesToReceive
+ $speed = [math]::Round($received / 1MB, 2)
+
+ Write-Progress -Activity "Downloading server.jar" -Status "$percent% Complete" -PercentComplete $percent -CurrentOperation "Downloaded: $speed MB / $([math]::Round($total/1MB, 2)) MB"
+ } | Out-Null
+
+ try {
+ $webClient.DownloadFile($downloadUrl, $tempDownloadPath)
+ Write-Progress -Activity "Downloading server.jar" -Completed
+ }
+ finally {
+ $webClient.Dispose()
+ Get-EventSubscriber | Unregister-Event
+ }
+ }
+
+ Write-Host "Download completed successfully!"
+ }
+ catch {
+ Write-Error "Error: Download failed. Please check the URL and your connection."
+ if (Test-Path $tempDownloadPath) { Remove-Item $tempDownloadPath }
+ Read-Host "Press Enter to exit"
+ exit 1
+ }
+
+ Write-Host "`nInstalling update..."
+ if (Test-Path $localJarFile) {
+ Write-Host " - Backing up old version to '$backupJarFile'..."
+ Move-Item -Path $localJarFile -Destination $backupJarFile -Force
+ }
+
+ Write-Host " - Installing new version..."
+ Move-Item -Path $tempDownloadPath -Destination $localJarFile -Force
+
+
+ # --- Step 6: Update the local version file ---
+ Write-Host " - Updating local version number..."
+ # Create a new JSON object to ensure the file is clean
+ @{ currentVersion = $remoteVersionInfo.latestVersion } | ConvertTo-Json -Depth 1 | Set-Content $localVersionFile
+
+
+ # --- Step 7: Restart the application ---
+ # !! CRITICAL !! You MUST customize this step.
+ Write-Host "`nRestarting application (you may need to customize this step)..."
+ # Example: Start-Process "java" -ArgumentList "-jar", $localJarFile
+ # Example: Start-Process ".\start_server.bat"
+
+ Write-Host "`nUpdate complete! Now running version $($remoteVersionInfo.latestVersion)."
+}
+
+# Pause at the end
+Read-Host "`nPress Enter to exit."