[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.

This commit is contained in:
Lê Thành Đạt committed 2025-09-20 02:07:53 -04:00
1 parent ae96a1a4da
commit 4ebce2b32a
9 files changed
+642 -13

No files matched your search

Binary file not shown.
-7
View File
@@ -246,12 +246,5 @@
<artifactId>sqlite-jdbc</artifactId>
<version>3.45.1.0</version>
</dependency>
<dependency>
<groupId>com.yourcompany</groupId>
<artifactId>jasper-fonts-timesnewroman</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/fonts/timesnewroman-fonts.jar</systemPath>
</dependency>
</dependencies>
</project>
+28 -4
View File
@@ -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 {
@@ -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<TextWebSocketFram
public static ChannelHandlerContext ctx;
public static TextWebSocketFrame frame;
public static final ClientHandler INSTANCE = new ClientHandler();
// Network resilience tracking
private static volatile boolean internetIssueDialogShown = false;
private static volatile long lastNetworkIssueTime = 0;
private static final long NETWORK_ISSUE_DIALOG_COOLDOWN = 30000; // 30 seconds
private static volatile boolean dialogCurrentlyShowing = false;
private static final Map<Class<?>, List<ResponseListener<?>>> listeners = new ConcurrentHashMap<>();
@@ -113,6 +122,14 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.WRITER_IDLE) {
// Send ping to keep connection alive
log.debug("Sending ping to server");
ctx.writeAndFlush(new PingWebSocketFrame());
} else if (event.state() == IdleState.READER_IDLE) {
// Server hasn't responded in a while - show internet issue dialog but don't close immediately
log.warn("Server not responding - possible network issues");
showInternetIssueDialog();
// Try to send a ping to test connection
ctx.writeAndFlush(new PingWebSocketFrame());
}
}
@@ -120,12 +137,213 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof IOException) {
log.error("Connection closed");
log.error("Connection exception: {}", cause.getMessage());
// Check if it's a network-related exception that might be temporary
if (cause instanceof IOException ||
cause instanceof java.nio.channels.ClosedChannelException ||
cause instanceof java.net.SocketException) {
// Show internet issue dialog instead of immediately closing
showInternetIssueDialogBlocking();
// Only close after multiple failures or if explicitly requested
log.warn("Network exception occurred, showing warning dialog");
} else {
// For other exceptions, show the connection lost dialog and exit
showConnectionLostDialog();
System.exit(0);
}
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.warn("Channel became inactive - connection lost");
// Show internet issue dialog and give it time to display
showInternetIssueDialogBlocking();
// Add a small delay to ensure dialog shows before channel cleanup
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
super.channelInactive(ctx);
// The dialog will handle the exit decision
}
private void showInternetIssueDialog() {
long currentTime = System.currentTimeMillis();
// Only show dialog if cooldown period has passed to avoid spam
if (internetIssueDialogShown && (currentTime - lastNetworkIssueTime) < NETWORK_ISSUE_DIALOG_COOLDOWN) {
return;
}
internetIssueDialogShown = true;
lastNetworkIssueTime = currentTime;
SwingUtilities.invokeLater(() -> {
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 <T> void notifyListener(ResponseListener<?> listener, T response) {
try {
+4
View File
@@ -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());
}
});
+20
View File
@@ -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
)
+175
View File
@@ -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."
+20
View File
@@ -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
)
+175
View File
@@ -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."