Skip to content

Commit

Permalink
Merge branch 'dev' into feature/change-install-folder
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/Gml.Client/GmlClientManager.cs
#	src/Gml.Client/IGmlClientManager.cs
  • Loading branch information
GamerVII-NET committed Jul 27, 2024
2 parents 3bd755e + ab9c9c6 commit 3f2f87e
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 30 deletions.
42 changes: 12 additions & 30 deletions src/Gml.Client/GmlClientManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ namespace Gml.Client;
public class GmlClientManager : IGmlClientManager
{
IObservable<int> IGmlClientManager.ProgressChanged => _progressChanged;
public IObservable<bool> ProfilesChanges => _profilesChanged;
public IObservable<int> MaxFileCount => _maxFileCount;
public IObservable<int> LoadedFilesCount => _loadedFilesCount;

public string ProjectName { get; }
public string InstallationDirectory => _installationDirectory;
Expand All @@ -28,9 +31,13 @@ public class GmlClientManager : IGmlClientManager
private readonly ApiProcedures _apiProcedures;
private SystemIoProcedures _systemProcedures;
private ISubject<int> _progressChanged = new Subject<int>();
private ISubject<bool> _profilesChanged = new Subject<bool>();
private ISubject<int> _maxFileCount = new Subject<int>();
private ISubject<int> _loadedFilesCount = new Subject<int>();
private SignalRConnect? _launchbackendConnection;
private readonly string _webSocketAddress;
private readonly OsType _osType;
private IDisposable? _profilesChangedEvent;

public GmlClientManager(string installationDirectory, string gateWay, string projectName, OsType osType)
{
Expand All @@ -46,6 +53,8 @@ public GmlClientManager(string installationDirectory, string gateWay, string pro
}, osType);

_apiProcedures.ProgressChanged.Subscribe(_progressChanged);
_apiProcedures.LoadedFilesCount.Subscribe(_loadedFilesCount);
_apiProcedures.MaxFileCount.Subscribe(_maxFileCount);

ProjectName = projectName;

Expand Down Expand Up @@ -92,36 +101,7 @@ await content.Stream.CopyToAsync(fs, content.Bytes,

_progressChanged.OnNext(100);

FileReplaceAndRestart(osType, tempFileName, originalFileName);
}

private void FileReplaceAndRestart(OsType osType, string newFileName, string originalFileName)
{
string cmd;
switch (osType)
{
case OsType.Undefined:
throw new Exception("Undefined OS type is not supported");
case OsType.Linux:
case OsType.OsX:
cmd = $"sh -c \"while [ -e /proc/{Process.GetCurrentProcess().Id} ]; do sleep 1; done; mv {newFileName} {originalFileName}; exec {originalFileName}\"";
Process.Start(cmd);
break;
case OsType.Windows:
cmd = $"/C for /L %N in () do (tasklist | findstr {Process.GetCurrentProcess().Id} >NUL || (move /Y {newFileName} {originalFileName} & start {originalFileName} & exit) & timeout 1) >NUL";
var psi = new ProcessStartInfo("CMD.exe", cmd)
{
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
};
Process.Start(psi);
break;
default:
throw new ArgumentOutOfRangeException(nameof(osType), osType, null);
}

// Singleton application should end to allow script replace its file
Environment.Exit(0);
LauncherUpdater.FileReplaceAndRestart(osType, tempFileName, originalFileName);
}

[DllImport("libc")]
Expand Down Expand Up @@ -163,6 +143,7 @@ public async Task DownloadNotInstalledFiles(ProfileReadInfoDto profileInfo,
public async Task OpenServerConnection(IUser user)
{
_launchbackendConnection = new SignalRConnect($"{_webSocketAddress}/ws/launcher", user);
_profilesChangedEvent ??= _launchbackendConnection.ProfilesChanges.Subscribe(_profilesChanged);
await _launchbackendConnection.BuildAndConnect();
}

Expand All @@ -186,5 +167,6 @@ private async void UpdateInfo(long _)
void IDisposable.Dispose()
{
_launchbackendConnection?.Dispose();
_profilesChangedEvent?.Dispose();
}
}
7 changes: 7 additions & 0 deletions src/Gml.Client/Helpers/ApiProcedures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ public class ApiProcedures
private int _progress;

private ISubject<int> _progressChanged = new Subject<int>();
private ISubject<int> _maxFileCount = new Subject<int>();
private ISubject<int> _loadedFilesCount = new Subject<int>();
internal event EventHandler<string>? FileAdded;

private Dictionary<string, List<ProfileFileWatcher>> _fileWatchers = new();
private (DiscordRpcClient? Client, DiscordRpcReadDto? ClientInfo)? _discordRpcClient;
public IObservable<int> ProgressChanged => _progressChanged;
public IObservable<int> MaxFileCount => _maxFileCount;
public IObservable<int> LoadedFilesCount => _loadedFilesCount;
public ApiProcedures(HttpClient httpClient, OsType osType)
{
_httpClient = httpClient;
Expand Down Expand Up @@ -254,6 +258,8 @@ public async Task DownloadFiles(string installationDirectory, ProfileFileReadDto

var throttler = new SemaphoreSlim(loadFilesPartCount);

_maxFileCount.OnNext(_progressFilesCount);

var tasks = files.Select(file =>
DownloadFileWithRetry(installationDirectory, file, throttler, cancellationToken));

Expand Down Expand Up @@ -325,6 +331,7 @@ private async Task DownloadFile(string installationDirectory, ProfileFileReadDto
_finishedFilesCount++;
_progress = Convert.ToInt16(_finishedFilesCount * 100 / _progressFilesCount);
_progressChanged.OnNext(_progress);
_loadedFilesCount.OnNext(_finishedFilesCount);
Debug.WriteLine($"{_finishedFilesCount}/{_progressFilesCount}");
}
catch (Exception ex)
Expand Down
100 changes: 100 additions & 0 deletions src/Gml.Client/Helpers/LauncherUpdater.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System.Diagnostics;
using Gml.Web.Api.Domains.System;

namespace Gml.Client.Helpers;

public class LauncherUpdater
{
public static void FileReplaceAndRestart(OsType osType, string newFileName, string originalFileName)
{
int currentProcessId = Process.GetCurrentProcess().Id;
string scriptFileName = Path.GetTempFileName();
string scriptContent = string.Empty;

var newFileInfo = new FileInfo(newFileName);
var originalFileInfo = new FileInfo(originalFileName);

switch (osType)
{
case OsType.Linux:
case OsType.OsX:
scriptContent = $@"
#!/bin/bash
sleep 1
kill {currentProcessId}
while [ -e /proc/{currentProcessId} ]; do sleep 0.1; done
mv ""{newFileInfo.FullName}"" ""{originalFileInfo.FullName}""
nohup ""{originalFileInfo.FullName}"" &
rm ""{scriptFileName}""";
File.WriteAllText(scriptFileName, scriptContent);
ExecuteCommand($"chmod +x \"{scriptFileName}\"", osType);
ExecuteCommand($"nohup \"{scriptFileName}\" &", osType, Path.GetDirectoryName(newFileInfo.FullName));
break;

case OsType.Windows:
scriptContent = $@"
@echo off
timeout /t 1 /nobreak
taskkill /PID {currentProcessId} /F
:waitloop
timeout /t 0.1 >nul
tasklist /fi ""pid eq {currentProcessId}"" | find /i ""{currentProcessId}"" >nul
if not errorlevel 1 goto waitloop
move /Y ""{newFileInfo.FullName}"" ""{originalFileInfo.FullName}""
start """" ""{originalFileInfo.FullName}""
del ""{scriptFileName}.bat""";
File.WriteAllText(scriptFileName + ".bat", scriptContent);
ExecuteCommand($"\"{scriptFileName}.bat\"", osType, Path.GetDirectoryName(newFileInfo.FullName));
break;

default:
throw new NotSupportedException("Unsupported OS type.");
}

Environment.Exit(0);
}

private static void ExecuteCommand(string command, OsType osType, string? workingDirectory = null)
{
ProcessStartInfo processInfo;

if (osType == OsType.Windows)
{
processInfo = new ProcessStartInfo("cmd.exe", $"/C {command}")
{
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
}
else
{
processInfo = new ProcessStartInfo("bash", $"-c \"{command}\"")
{
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
}

if (workingDirectory is not null)
{
processInfo.WorkingDirectory = workingDirectory;
}

using (var process = Process.Start(processInfo))
{
process.WaitForExit();
string output = process.StandardOutput.ReadToEnd();
string error = process.StandardError.ReadToEnd();

if (process.ExitCode != 0)
{
throw new Exception($"Command '{command}' failed with error: {error}");
}
}
}

}
9 changes: 9 additions & 0 deletions src/Gml.Client/Helpers/SignalRConnect.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System.Reactive.Subjects;
using Gml.Client.Models;
using Microsoft.AspNetCore.SignalR.Client;

namespace Gml.Client.Helpers;

public class SignalRConnect : IDisposable, IAsyncDisposable
{
private readonly ISubject<bool> _profilesChanged = new Subject<bool>();
private readonly string _address;
private readonly IUser _user;
private HubConnection _hubConnection;
public IObservable<bool> ProfilesChanges => _profilesChanged;

public SignalRConnect(string address, IUser user)
{
Expand Down Expand Up @@ -53,6 +56,12 @@ private void CreateListeners()
await _hubConnection.SendAsync("ConfirmLauncherHash", "hash");
});

_hubConnection.On("RefreshProfiles", () =>
{
_profilesChanged.OnNext(true);
return Task.CompletedTask;
});

}

public async ValueTask DisposeAsync()
Expand Down
3 changes: 3 additions & 0 deletions src/Gml.Client/IGmlClientManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ namespace Gml.Client;
public interface IGmlClientManager : IDisposable
{
IObservable<int> ProgressChanged { get; }
IObservable<bool> ProfilesChanges { get; }
public string ProjectName { get; }
IObservable<int> MaxFileCount { get; }
IObservable<int> LoadedFilesCount { get; }
string InstallationDirectory { get; }
Task<ResponseMessage<List<ProfileReadDto>>> GetProfiles();
Task<ResponseMessage<ProfileReadInfoDto?>?> GetProfileInfo(ProfileCreateInfoDto profileDto);
Expand Down

0 comments on commit 3f2f87e

Please sign in to comment.