From 4ebce2b32aa2cb6db0b516e06f9fe47b34b2d497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Th=C3=A0nh=20=C4=90=E1=BA=A1t?= <91755733+lds217@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:07:53 -0400 Subject: [PATCH] [update] Remove unused Times New Roman font dependency from pom.xml. Enhance Client and ClientHandler classes with improved network resilience features, including IdleStateHandler for better timeout management and user-friendly error dialogs for connection issues. Update Server class to include IdleStateHandler for improved tolerance of network delays. --- database/BSK.db-shm | Bin 32768 -> 32768 bytes pom.xml | 7 - src/main/java/BsK/client/Client.java | 32 ++- .../client/network/handler/ClientHandler.java | 222 +++++++++++++++++- src/main/java/BsK/server/Server.java | 4 + update_client.bat | 20 ++ update_client_script.ps1 | 175 ++++++++++++++ update_server.bat | 20 ++ update_server_script.ps1 | 175 ++++++++++++++ 9 files changed, 642 insertions(+), 13 deletions(-) create mode 100644 update_client.bat create mode 100644 update_client_script.ps1 create mode 100644 update_server.bat create mode 100644 update_server_script.ps1 diff --git a/database/BSK.db-shm b/database/BSK.db-shm index 6c77a27819dd570f1c43713b252d5ce155028f07..224e88b3b64b01f6c92085bfdc20fb4c435f018d 100644 GIT binary patch delta 55 ucmZo@U}|V!;*@x#%K!!wIpqZ(gvJRTH0HR4EXWELoEWRj_;h1qeLVn*%n#}S delta 55 vcmZo@U}|V!;*@x#%K!q56FKDt--gBsUNh#ng)GPl7MvKX%=l 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."