This repository has been archived on 2026-01-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

227 lines
8.2 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Deployment.Application;
using System.Diagnostics;
using System.Drawing;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Http;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using System.Reflection.Emit;
namespace ProgramLauncher
{
public partial class Form1 : Form
{
static string downloadServerUrl = "http://xr.flexing.ai:3030/XR_Server";
static string downloadFolderName = "Build";
static string jsonFileName = "file_list.json";
static string localVersionID = "versionID.txt";
long totalBytesToDownload = 0;
long totalBytesDownloaded = 0;
static readonly HttpClient httpClient = new HttpClient();
public Form1()
{
downloadServerUrl = CombineUrl(downloadServerUrl, Program.projectName);
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
Label_UpdateFileName.Text = string.Empty;
await CheckUpdate();
}
private void WriteText(string text)
{
Label_UpdateFileName.Text = text;
}
private async Task CheckUpdate()
{
var serverFileList = await GetServerFileList(downloadServerUrl);
if (serverFileList == null) return;
string latestVersionID = serverFileList.VersionID;
string localVersionID = GetLocalVersion();
if (latestVersionID != localVersionID)
{
// ✅ [1] 전체 크기 계산
foreach (var file in serverFileList.Files)
{
string fileUrl = CombineUrl(downloadServerUrl, Path.Combine(downloadFolderName, file.Key));
using (var response = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, fileUrl)))
{
if (response.IsSuccessStatusCode && response.Content.Headers.ContentLength.HasValue)
{
totalBytesToDownload += response.Content.Headers.ContentLength.Value;
}
}
}
// ✅ [2] ProgressBar 초기화
ProgressBar.Invoke((MethodInvoker)(() =>
{
ProgressBar.Minimum = 0;
ProgressBar.Maximum = (int)Math.Max(totalBytesToDownload, 1); // 0 방지
ProgressBar.Value = 0;
}));
foreach (var file in serverFileList.Files)
{
string localFilePath = Path.Combine(downloadFolderName, file.Key);
string serverHash = file.Value;
if (!File.Exists(localFilePath) || GetFileHash(localFilePath) != serverHash)
{
Label_UpdateFileName.Text = localFilePath;
string downloadFolder = CombineUrl(downloadServerUrl, downloadFolderName);
string downloadFile = Path.Combine(downloadFolder, file.Key);
await DownloadFile(downloadFile, localFilePath, (downloadedBytes) =>
{
totalBytesDownloaded += downloadedBytes;
ProgressBar.Invoke((MethodInvoker)(() =>
{
ProgressBar.Value = (int)Math.Min(totalBytesDownloaded, ProgressBar.Maximum);
}));
});
}
}
var serverFilePaths = new HashSet<string>(
serverFileList.Files.Keys.Select(file => file.Replace('\\', '/')) // 서버의 파일 경로를 통일
);
foreach (string localFile in Directory.GetFiles(downloadFolderName, "*", SearchOption.AllDirectories))
{
// 현재 파일의 상대 경로를 구함 (localFolder 기준)
string relativePath = localFile.Substring(downloadFolderName.Length + 1).Replace('\\', '/');
if (!serverFilePaths.Contains(relativePath))
{
File.Delete(localFile);
}
}
SaveLocalVersion(latestVersionID);
}
StartExecutableFile(downloadFolderName);
this.Close();
}
private async Task<ServerFileList> GetServerFileList(string serverUrl)
{
using (WebClient client = new WebClient())
{
try
{
string serverJsonUrl = CombineUrl(serverUrl, jsonFileName);
string json = await client.DownloadStringTaskAsync(serverJsonUrl);
return JsonConvert.DeserializeObject<ServerFileList>(json);
}
catch (Exception ex)
{
return null;
}
}
}
private string GetLocalVersion()
{
return File.Exists(localVersionID) ? File.ReadAllText(localVersionID).Trim() : "";
}
private void SaveLocalVersion(string version)
{
File.WriteAllText(localVersionID, version);
}
private string GetFileHash(string filePath)
{
using (var sha256 = SHA256.Create())
using (var stream = File.OpenRead(filePath))
{
byte[] hash = sha256.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
}
private async Task DownloadFile(string url, string outputPath, Action<long> reportProgress)
{
try
{
Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? "");
using (var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode();
using (var stream = await response.Content.ReadAsStreamAsync())
using (var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None))
{
byte[] buffer = new byte[81920];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await fileStream.WriteAsync(buffer, 0, bytesRead);
reportProgress(bytesRead); // 매번 다운로드된 바이트 보고
}
}
}
}
catch (Exception ex)
{
WriteText($"❌ 다운로드 실패: {ex.Message}");
}
}
private void StartExecutableFile(string executeFolder)
{
string[] exeFiles = Directory.GetFiles(executeFolder, "*.exe", SearchOption.TopDirectoryOnly);
if (exeFiles.Length == 0)
{
return;
}
string executeFilePath = exeFiles.FirstOrDefault(file => !file.EndsWith("UnityCrashHandler64.exe", StringComparison.OrdinalIgnoreCase));
string executeFileName = Path.GetFileName(executeFilePath);
Process process = new Process();
process.StartInfo.UseShellExecute = true;
process.StartInfo.WorkingDirectory = executeFolder;
process.StartInfo.FileName = executeFileName;
process.Start();
}
private string CombineUrl(string baseUrl, string relativePath)
{
return $"{baseUrl.TrimEnd('/')}/{relativePath.TrimStart('/')}";
}
}
class ServerFileList
{
public string VersionID { get; set; } = "";
public Dictionary<string, string> Files { get; set; } = new Dictionary<string, string>();
}
}