227 lines
8.2 KiB
C#
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>();
|
|
}
|
|
}
|
|
|