diff --git a/.gitignore b/.gitignore index 4d4d829..a86f146 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ bin/ obj/ logs/ -profiles/ \ No newline at end of file +profiles/ +jre/ +setup/ \ No newline at end of file diff --git a/Program.cs b/Program.cs index 1c5d057..e4a4928 100644 --- a/Program.cs +++ b/Program.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using MCSJ.Tools; using MCSJ.Tools.LogSystem; +using MCSJ.Tools.JreDownload; namespace MCSJ { @@ -15,6 +16,10 @@ namespace MCSJ LogMain.Debug($"日志文件: {LogCreator.GetLogFilePath()}"); LogMain.Info("MC服务器下载工具启动"); + var httpClient = new HttpClient { + Timeout = TimeSpan.FromMinutes(5), + DefaultRequestHeaders = { { "User-Agent", "MCSJ-JRE-Downloader" } } + }; var versionManager = new VersionManager(); var downloadService = new DownloadService(versionManager); LogMain.Debug("服务初始化完成"); @@ -24,7 +29,8 @@ namespace MCSJ Console.WriteLine("MC服务器下载工具"); Console.WriteLine("1. 显示所有版本"); Console.WriteLine("2. 下载指定版本"); - Console.WriteLine("3. 退出"); + Console.WriteLine("3. 下载JRE"); + Console.WriteLine("4. 退出"); Console.Write("请选择操作: "); var input = Console.ReadLine(); @@ -44,6 +50,19 @@ namespace MCSJ LogMain.Info($"版本下载完成: {version}"); break; case "3": + Console.Write("请输入要下载的JRE版本(如jre8,jre11等): "); + var jreVersion = Console.ReadLine(); + if (string.IsNullOrWhiteSpace(jreVersion)) + { + Console.WriteLine("JRE版本不能为空"); + continue; + } + LogMain.Info($"开始下载JRE: {jreVersion}"); + var jreDownloadService = new JreDownloadService(httpClient); + await jreDownloadService.DownloadAndSetupJre(jreVersion); + LogMain.Info($"JRE下载完成: {jreVersion}"); + break; + case "4": LogMain.Info("程序正常退出"); return; default: diff --git a/Tools/DownloadService.cs b/Tools/DownloadService.cs index a1e5c52..10210e2 100644 --- a/Tools/DownloadService.cs +++ b/Tools/DownloadService.cs @@ -16,10 +16,16 @@ namespace MCSJ.Tools _httpClient = new HttpClient(); } - public async Task DownloadVersion(string version) + public async Task DownloadVersion(string? version) { + if (string.IsNullOrWhiteSpace(version)) + { + Console.WriteLine("版本名称不能为空"); + return; + } + var url = _versionManager.GetDownloadUrl(version); - if (url == null) + if (string.IsNullOrEmpty(url)) { Console.WriteLine($"版本 {version} 不存在"); return; @@ -30,8 +36,8 @@ namespace MCSJ.Tools if (!Directory.Exists(profilesRoot)) Directory.CreateDirectory(profilesRoot); - string targetFolder = null; - string profilePath = null; + string? targetFolder = null; + string? profilePath = null; while (true) { Console.Write($"请输入存放文件夹名称(直接回车默认用版本名 '{version}'):"); diff --git a/Tools/VersionManager.cs b/Tools/VersionManager.cs index 59f174d..0f8514b 100644 --- a/Tools/VersionManager.cs +++ b/Tools/VersionManager.cs @@ -71,6 +71,13 @@ namespace MCSJ.Tools public void DisplayAllVersions() { + var filePath = Path.Combine("resources", "serverlist.txt"); + if (!File.Exists(filePath)) + { + Console.WriteLine("版本列表文件不存在"); + return; + } + Console.WriteLine("可用版本列表:"); foreach (var version in _versions.Keys) { @@ -78,7 +85,7 @@ namespace MCSJ.Tools } } - public string GetDownloadUrl(string version) + public string? GetDownloadUrl(string version) { return _versions.TryGetValue(version, out var url) ? url : null; } diff --git a/Tools/jredownload/JreDownloadProgress.cs b/Tools/jredownload/JreDownloadProgress.cs new file mode 100644 index 0000000..9a31785 --- /dev/null +++ b/Tools/jredownload/JreDownloadProgress.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; + +namespace MCSJ.Tools.JreDownload +{ + public class JreDownloadProgress : IProgress<(long bytesReceived, long totalBytesToReceive, int progressPercentage)> + { + public void Report((long bytesReceived, long totalBytesToReceive, int progressPercentage) value) + { + // 保存当前控制台颜色 + var originalColor = Console.ForegroundColor; + + try + { + // 计算下载百分比 + int percentage = value.progressPercentage; + + // 格式化文件大小(使用MB单位) + string received = FormatFileSizeMB(value.bytesReceived); + string total = FormatFileSizeMB(value.totalBytesToReceive); + + // 创建进度条 + string progressBar = CreateProgressBar(percentage); + + // 显示进度信息(仅进度条部分为绿色) + Console.Write("\r下载进度: "); + Console.ForegroundColor = ConsoleColor.Green; + Console.Write($"{progressBar}"); + Console.ForegroundColor = originalColor; + Console.Write($" {percentage}% [{FormatFileSizeMB(value.bytesReceived)} / {FormatFileSizeMB(value.totalBytesToReceive)}]"); + + // 下载完成时换行 + if (percentage == 100) + { + Console.WriteLine(); + } + } + finally + { + // 恢复原始控制台颜色 + Console.ForegroundColor = originalColor; + } + } + + private string CreateProgressBar(int percentage) + { + int width = 20; // 进度条宽度 + int progress = percentage * width / 100; + + return $"[{new string('#', progress)}{new string('-', width - progress)}]"; + } + + private string FormatFileSizeMB(long bytes) + { + double mb = bytes / (1024.0 * 1024.0); + return $"{mb:0.##} MB"; + } + } +} diff --git a/Tools/jredownload/JreDownloadService.cs b/Tools/jredownload/JreDownloadService.cs new file mode 100644 index 0000000..fec5554 --- /dev/null +++ b/Tools/jredownload/JreDownloadService.cs @@ -0,0 +1,193 @@ +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.IO.Compression; + +namespace MCSJ.Tools.JreDownload +{ + public class JreDownloadService + { + private readonly HttpClient _httpClient; + private const string JreListPath = "resources/jrelist.txt"; + private const string JreRootFolder = "jre"; + private const string SetupFolder = "setup"; + + public JreDownloadService(HttpClient httpClient) + { + _httpClient = httpClient; + } + + public async Task DownloadAndSetupJre(string version) + { + // 1. 读取jrelist.txt获取下载链接 + var downloadUrl = GetDownloadUrl(version); + if (string.IsNullOrEmpty(downloadUrl)) + { + Console.WriteLine($"找不到版本 {version} 的下载链接"); + return; + } + + // 2. 下载压缩包 + var progress = new JreDownloadProgress(); + var tempZipPath = await DownloadJreZip(downloadUrl, version, progress); + if (string.IsNullOrEmpty(tempZipPath)) + { + Console.WriteLine("下载失败"); + return; + } + + // 3. 解压到jre文件夹 + var jreFolder = Path.Combine(JreRootFolder, version); + if (!ExtractJre(tempZipPath, jreFolder)) + { + Console.WriteLine("解压失败"); + return; + } + + // 4. 查找java.exe和javaw.exe + var javaExePath = FindJavaExe(jreFolder, "java.exe"); + var javawExePath = FindJavaExe(jreFolder, "javaw.exe"); + + if (javaExePath == null || javawExePath == null) + { + Console.WriteLine("找不到java.exe或javaw.exe"); + return; + } + + // 5. 生成jre.toml + CreateJreToml(version, javaExePath, javawExePath); + + // 6. 清理临时文件 + File.Delete(tempZipPath); + + Console.WriteLine($"JRE {version} 安装完成"); + } + + private string? GetDownloadUrl(string version) + { + if (string.IsNullOrWhiteSpace(version)) + return null; + + if (!File.Exists(JreListPath)) + return null; + + var lines = File.ReadAllLines(JreListPath); + return lines.FirstOrDefault(l => l.StartsWith(version + ":"))?.Split(':').LastOrDefault(); + } + + private async Task DownloadJreZip(string url, string version, IProgress<(long, long, int)>? progress = null) + { + var tempPath = Path.GetTempFileName(); + try + { + Console.WriteLine($"开始下载 JRE {version}..."); + // 确保URL是绝对路径,自动补全https协议头 + if (!url.StartsWith("http:") && !url.StartsWith("https:")) + { + url = "https:" + url; + } + + if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) + { + Console.WriteLine($"无效的下载URL: {url}"); + return null; + } + + Console.WriteLine($"正在准备下载 {url}..."); + var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + response.EnsureSuccessStatusCode(); + + var totalBytes = response.Content.Headers.ContentLength ?? 0; + Console.WriteLine($"文件总大小: {totalBytes} 字节"); + long bytesRead = 0; + var lastReportTime = DateTime.MinValue; + Console.WriteLine("开始下载..."); + + using (var stream = await response.Content.ReadAsStreamAsync()) + using (var fileStream = new FileStream(tempPath, FileMode.Create)) + { + var buffer = new byte[8192]; + int bytesReadThisPass; + + while ((bytesReadThisPass = await stream.ReadAsync(buffer)) != 0) + { + await fileStream.WriteAsync(buffer.AsMemory(0, bytesReadThisPass)); + bytesRead += bytesReadThisPass; + + // 限制进度报告频率,避免过多控制台输出 + if (DateTime.Now - lastReportTime > TimeSpan.FromMilliseconds(100) || bytesRead == totalBytes) + { + int progressPercentage = totalBytes > 0 ? (int)(bytesRead * 100 / totalBytes) : 0; + Console.WriteLine($"报告进度: {bytesRead}/{totalBytes} ({progressPercentage}%)"); + progress?.Report((bytesRead, totalBytes, progressPercentage)); + lastReportTime = DateTime.Now; + } + } + } + + return tempPath; + } + catch (Exception ex) + { + Console.WriteLine($"下载失败: {ex.Message}"); + return null; + } + } + + private bool ExtractJre(string zipPath, string targetFolder) + { + try + { + if (Directory.Exists(targetFolder)) + Directory.Delete(targetFolder, true); + + Directory.CreateDirectory(targetFolder); + ZipFile.ExtractToDirectory(zipPath, targetFolder); + return true; + } + catch (Exception ex) + { + Console.WriteLine($"解压失败: {ex.Message}"); + return false; + } + } + + private string? FindJavaExe(string folder, string exeName) + { + if (string.IsNullOrWhiteSpace(folder) || string.IsNullOrWhiteSpace(exeName)) + return null; + + if (!Directory.Exists(folder)) + return null; + + return Directory.GetFiles(folder, exeName, SearchOption.AllDirectories).FirstOrDefault(); + } + + private void CreateJreToml(string version, string javaExePath, string javawExePath) + { + if (!Directory.Exists(SetupFolder)) + Directory.CreateDirectory(SetupFolder); + + var tomlPath = Path.Combine(SetupFolder, "jre.toml"); + var content = $@"[jre.{version}] +java_path = '{javaExePath}' +javaw_path = '{javawExePath}' +"; + + if (File.Exists(tomlPath)) + { + var existingContent = File.ReadAllText(tomlPath); + if (!existingContent.Contains($"[jre.{version}]")) + { + File.AppendAllText(tomlPath, content); + } + } + else + { + File.WriteAllText(tomlPath, content); + } + } + } +}