Synchronize saves
This commit is contained in:
100
OpenSaveCloudClient/Core/LogManager.cs
Normal file
100
OpenSaveCloudClient/Core/LogManager.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using OpenSaveCloudClient.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
public class LogManager
|
||||
{
|
||||
|
||||
private static LogManager? instance;
|
||||
|
||||
private readonly List<Log> messages;
|
||||
|
||||
public List<Log> Messages { get { return messages; } }
|
||||
|
||||
private LogManager() {
|
||||
messages = new List<Log>();
|
||||
}
|
||||
|
||||
public static LogManager GetInstance()
|
||||
{
|
||||
if (instance == null) { instance = new LogManager(); }
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void AddError(Exception ex)
|
||||
{
|
||||
Log log = new()
|
||||
{
|
||||
Message = ex.Message,
|
||||
Severity = LogSeverity.Error,
|
||||
};
|
||||
messages.Add(log);
|
||||
NewMessageEventArgs args = new()
|
||||
{
|
||||
Message = ex.Message,
|
||||
Severity = LogSeverity.Error,
|
||||
};
|
||||
OnNewMessage(args);
|
||||
}
|
||||
|
||||
public void AddInformation(string message)
|
||||
{
|
||||
Log log = new()
|
||||
{
|
||||
Message = message,
|
||||
Severity = LogSeverity.Information,
|
||||
};
|
||||
messages.Add(log);
|
||||
NewMessageEventArgs args = new()
|
||||
{
|
||||
Message = message,
|
||||
Severity = LogSeverity.Information,
|
||||
};
|
||||
OnNewMessage(args);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
messages.Clear();
|
||||
OnClear(new ClearEventArgs());
|
||||
}
|
||||
|
||||
protected virtual void OnNewMessage(NewMessageEventArgs e)
|
||||
{
|
||||
EventHandler<NewMessageEventArgs> handler = NewMessage;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnClear(ClearEventArgs e)
|
||||
{
|
||||
EventHandler<ClearEventArgs> handler = Cleared;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<NewMessageEventArgs> NewMessage;
|
||||
public event EventHandler<ClearEventArgs> Cleared;
|
||||
|
||||
}
|
||||
|
||||
public class NewMessageEventArgs : EventArgs
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public LogSeverity Severity { get; set; }
|
||||
}
|
||||
|
||||
public class ClearEventArgs : EventArgs
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using System.Text.Json;
|
||||
using OpenSaveCloudClient.Models.Remote;
|
||||
using OpenSaveCloudClient.Models;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
@@ -20,7 +21,11 @@ namespace OpenSaveCloudClient.Core
|
||||
private bool connected;
|
||||
private ServerInformation? serverInformation;
|
||||
|
||||
private LogManager logManager;
|
||||
private TaskManager taskManager;
|
||||
private Configuration configuration;
|
||||
private SaveManager saveManager;
|
||||
|
||||
|
||||
public string? Host { get { return host; } }
|
||||
public int Port { get { return port; } }
|
||||
@@ -31,6 +36,9 @@ namespace OpenSaveCloudClient.Core
|
||||
private ServerConnector()
|
||||
{
|
||||
configuration = Configuration.GetInstance();
|
||||
logManager = LogManager.GetInstance();
|
||||
taskManager = TaskManager.GetInstance();
|
||||
saveManager = SaveManager.GetInstance();
|
||||
}
|
||||
|
||||
public static ServerConnector GetInstance()
|
||||
@@ -49,33 +57,16 @@ namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
host = "http://" + host;
|
||||
}
|
||||
logManager.AddInformation(String.Format("Binding server {0}:{1}", host, port));
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
GetServerInformation();
|
||||
}
|
||||
|
||||
private void GetServerInformation()
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpClient client = new();
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/system/information", host, port)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
serverInformation = JsonSerializer.Deserialize<ServerInformation>(responseText);
|
||||
if (serverInformation != null)
|
||||
{
|
||||
bind = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
}
|
||||
|
||||
public void Login(string username, string password)
|
||||
{
|
||||
logManager.AddInformation("Loging in to the server");
|
||||
string uuidTask = taskManager.StartTask("Login to the server", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
@@ -91,19 +82,33 @@ namespace OpenSaveCloudClient.Core
|
||||
token = accessToken.Token;
|
||||
connected = true;
|
||||
SaveToConfig();
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
}
|
||||
else
|
||||
{
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
|
||||
public void Reconnect()
|
||||
{
|
||||
string? uuidTask = null;
|
||||
try
|
||||
{
|
||||
if (ReloadFromConfiguration())
|
||||
{
|
||||
uuidTask = taskManager.StartTask("Login to the server", true, 1);
|
||||
HttpClient client = new HttpClient();
|
||||
string json = JsonSerializer.Serialize(new AccessToken { Token = token });
|
||||
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
@@ -116,16 +121,225 @@ namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
connected = true;
|
||||
SaveToConfig();
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logout();
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logout();
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
if (uuidTask != null)
|
||||
{
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Logout()
|
||||
{
|
||||
serverInformation = null;
|
||||
bind = false;
|
||||
connected = false;
|
||||
token = "";
|
||||
configuration.SetValue("authentication.host", null);
|
||||
configuration.SetValue("authentication.port", null);
|
||||
configuration.SetValue("authentication.token", null);
|
||||
configuration.Flush();
|
||||
}
|
||||
|
||||
public Game? CreateGame(string name)
|
||||
{
|
||||
logManager.AddInformation("Creating game to server database");
|
||||
string uuidTask = taskManager.StartTask("Creating game to server database", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
string json = JsonSerializer.Serialize(new NewGameInfo { Name = name });
|
||||
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/create", host, port), content).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
logManager.AddInformation("Game created!");
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<Game>(responseText);
|
||||
} else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Synchronize()
|
||||
{
|
||||
string appdata = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "osc");
|
||||
string cachePath = Path.Combine(appdata, "cache");
|
||||
logManager.AddInformation("Starting synchronization");
|
||||
List<GameSave> games = saveManager.Saves;
|
||||
string uuidTask = taskManager.StartTask("Synchronizing games", true, games.Count);
|
||||
foreach (GameSave game in games)
|
||||
{
|
||||
try
|
||||
{
|
||||
Game? g = GetGameInfoByID(game.Id);
|
||||
if (g != null)
|
||||
{
|
||||
if (g.Available)
|
||||
{
|
||||
if (g.Hash != game.Hash)
|
||||
{
|
||||
if (g.Revision != game.Revision)
|
||||
{
|
||||
logManager.AddInformation(String.Format("'{0}' was updated from another computer", game.Name));
|
||||
} else
|
||||
{
|
||||
logManager.AddInformation(String.Format("'{0} need to be updated'", game.Name));
|
||||
GameUploadToken? gut = LockGameToUpload(game.Id);
|
||||
if (gut != null)
|
||||
{
|
||||
string archivePath = Path.Combine(cachePath, game.Uuid + ".bin");
|
||||
UploadSave(gut.UploadToken, archivePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddInformation(String.Format("'{0}' is up to date", game.Name));
|
||||
}
|
||||
} else
|
||||
{
|
||||
logManager.AddInformation(String.Format("First upload of '{0}'", game.Name));
|
||||
GameUploadToken? gut = LockGameToUpload(game.Id);
|
||||
if (gut != null)
|
||||
{
|
||||
string archivePath = Path.Combine(cachePath, game.Uuid + ".bin");
|
||||
UploadSave(gut.UploadToken, archivePath);
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
logManager.AddError(new Exception("Failed to get game information, the save will not be synchronized"));
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
}
|
||||
taskManager.UpdateTaskProgress(uuidTask, 1);
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
}
|
||||
|
||||
public Game? GetGameInfoByID(int gameId)
|
||||
{
|
||||
logManager.AddInformation("Getting game information from the server database");
|
||||
string uuidTask = taskManager.StartTask("Getting game information", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/game/{2}", host, port, gameId)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<Game>(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void UploadSave(string uploadToken, string filePath)
|
||||
{
|
||||
logManager.AddInformation("Uploading save");
|
||||
string uuidTask = taskManager.StartTask("Uploading", true, 1);
|
||||
try
|
||||
{
|
||||
MultipartFormDataContent multipartFormContent = new();
|
||||
var fileStreamContent = new StreamContent(File.OpenRead(filePath));
|
||||
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
||||
multipartFormContent.Add(fileStreamContent, name: "file", fileName: "file.bin");
|
||||
|
||||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
client.DefaultRequestHeaders.Add("X-UPLOAD-KEY", uploadToken);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload", host, port), multipartFormContent).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
|
||||
private GameUploadToken? LockGameToUpload(int gameId)
|
||||
{
|
||||
logManager.AddInformation("Locking game in the server");
|
||||
string uuidTask = taskManager.StartTask("Locking game", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
string json = JsonSerializer.Serialize(new UploadGameInfo { GameId = gameId });
|
||||
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload/init", host, port), content).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
logManager.AddInformation("Game locked");
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<GameUploadToken>(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool ReloadFromConfiguration()
|
||||
@@ -150,23 +364,36 @@ namespace OpenSaveCloudClient.Core
|
||||
}
|
||||
token = oldToken;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Logout()
|
||||
private void GetServerInformation()
|
||||
{
|
||||
serverInformation = null;
|
||||
bind = false;
|
||||
connected = false;
|
||||
token = "";
|
||||
configuration.SetValue("authentication.host", null);
|
||||
configuration.SetValue("authentication.port", null);
|
||||
configuration.SetValue("authentication.token", null);
|
||||
configuration.Flush();
|
||||
logManager.AddInformation("Getting server information");
|
||||
try
|
||||
{
|
||||
HttpClient client = new();
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/system/information", host, port)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
serverInformation = JsonSerializer.Deserialize<ServerInformation>(responseText);
|
||||
if (serverInformation != null)
|
||||
{
|
||||
logManager.AddInformation("Server is connected");
|
||||
bind = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveToConfig()
|
||||
|
||||
@@ -16,13 +16,15 @@ namespace OpenSaveCloudClient.Core
|
||||
private readonly Dictionary<string, AsyncTaskInformation> _tasks;
|
||||
private readonly Mutex mut;
|
||||
|
||||
public List<AsyncTaskInformation> TasksInformation { get { return _tasks.Values.ToList(); } }
|
||||
|
||||
private TaskManager()
|
||||
{
|
||||
_tasks = new Dictionary<string, AsyncTaskInformation>();
|
||||
mut = new Mutex();
|
||||
timer = new System.Timers.Timer
|
||||
{
|
||||
Interval = 2000
|
||||
Interval = 10000
|
||||
};
|
||||
timer.Elapsed += timer_Tick;
|
||||
timer.Start();
|
||||
@@ -37,16 +39,59 @@ namespace OpenSaveCloudClient.Core
|
||||
return instance;
|
||||
}
|
||||
|
||||
public string StartTask(string label, int progressMax)
|
||||
public string StartTask(string label, bool undefined, int progressMax)
|
||||
{
|
||||
string uuid = Guid.NewGuid().ToString();
|
||||
_tasks.Add(uuid, new AsyncTaskInformation(label, progressMax));
|
||||
AsyncTaskInformation ati = new(uuid, label, undefined, progressMax);
|
||||
_tasks.Add(uuid, ati);
|
||||
TaskChangedEventArgs args = new()
|
||||
{
|
||||
TaskInformation = ati
|
||||
};
|
||||
OnTaskChanged(args);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public AsyncTaskInformation GetTask(string uuid)
|
||||
public void UpdateTaskProgress(string uuid, int progress)
|
||||
{
|
||||
return _tasks[uuid];
|
||||
try
|
||||
{
|
||||
AsyncTaskInformation task = _tasks[uuid];
|
||||
task.Add(progress);
|
||||
task.Undefined = false;
|
||||
TaskChangedEventArgs args = new()
|
||||
{
|
||||
TaskInformation = task
|
||||
};
|
||||
OnTaskChanged(args);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateTaskStatus(string uuid, AsyncTaskStatus status)
|
||||
{
|
||||
try
|
||||
{
|
||||
AsyncTaskInformation task = _tasks[uuid];
|
||||
if (status != AsyncTaskStatus.Running)
|
||||
{
|
||||
task.Progress = task.Max;
|
||||
task.Undefined = false;
|
||||
}
|
||||
task.Status = status;
|
||||
TaskChangedEventArgs args = new()
|
||||
{
|
||||
TaskInformation = task
|
||||
};
|
||||
OnTaskChanged(args);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void timer_Tick(object? sender, EventArgs e)
|
||||
@@ -66,6 +111,10 @@ namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
_tasks.Remove(uuid);
|
||||
}
|
||||
if (toDelete.Count > 0)
|
||||
{
|
||||
OnTaskCleared(new TaskClearedEventArgs());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -73,5 +122,35 @@ namespace OpenSaveCloudClient.Core
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnTaskChanged(TaskChangedEventArgs e)
|
||||
{
|
||||
EventHandler<TaskChangedEventArgs> handler = TaskChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnTaskCleared(TaskClearedEventArgs e)
|
||||
{
|
||||
EventHandler<TaskClearedEventArgs> handler = TaskCleared;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<TaskChangedEventArgs> TaskChanged;
|
||||
public event EventHandler<TaskClearedEventArgs> TaskCleared;
|
||||
|
||||
}
|
||||
|
||||
public class TaskChangedEventArgs : EventArgs
|
||||
{
|
||||
public AsyncTaskInformation TaskInformation { get; set; }
|
||||
}
|
||||
|
||||
public class TaskClearedEventArgs : EventArgs
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user