- add: Service think

This commit is contained in:
2024-12-22 16:38:47 +03:00
parent d9161f837b
commit 4d64c995f1
38 changed files with 4625 additions and 80 deletions

View File

@@ -0,0 +1,62 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace Nebula.Launcher.Services;
public class ConVar
{
public string Name { get; }
public Type Type { get; }
public object? DefaultValue { get; }
private ConVar(string name, Type type, object? defaultValue)
{
Name = name;
Type = type;
DefaultValue = defaultValue;
}
public static ConVar Build<T>(string name, T? defaultValue = default)
{
return new ConVar(name, typeof(T), defaultValue);
}
}
[ServiceRegister]
public class ConfigurationService
{
public ConfigurationService()
{
}
public object? GetConfigValue(ConVar conVar)
{
return conVar.DefaultValue;
}
public T? GetConfigValue<T>(ConVar conVar)
{
var value = GetConfigValue(conVar);
if (value is not T tv) return default;
return tv;
}
public bool TryGetConfigValue(ConVar conVar,[NotNullWhen(true)] out object? value)
{
value = GetConfigValue(conVar);
return value != null;
}
public bool TryGetConfigValue<T>(ConVar conVar, [NotNullWhen(true)] out T? value)
{
value = GetConfigValue<T>(conVar);
return value != null;
}
public void SetValue(ConVar conVar, object value)
{
if(conVar.Type != value.GetType())
return;
}
}

View File

@@ -0,0 +1,70 @@
using System;
using System.IO;
using Nebula.Launcher.Services.Logging;
namespace Nebula.Launcher.Services;
[ServiceRegister]
public class DebugService : IDisposable
{
public ILogger Logger;
private static string LogPath = Path.Combine(FileService.RootPath, "log");
public DateTime LogDate = DateTime.Now;
private FileStream LogStream;
private StreamWriter LogWriter;
public DebugService(ILogger logger)
{
Logger = logger;
if (!Directory.Exists(LogPath))
Directory.CreateDirectory(LogPath);
var filename = String.Format("{0:yyyy-MM-dd}.txt", DateTime.Now);
LogStream = File.Open(Path.Combine(LogPath, filename),
FileMode.Append, FileAccess.Write);
LogWriter = new StreamWriter(LogStream);
}
public void Debug(string message)
{
Log(LoggerCategory.Debug, message);
}
public void Error(string message)
{
Log(LoggerCategory.Error, message);
}
public void Log(string message)
{
Log(LoggerCategory.Log, message);
}
public void Dispose()
{
LogWriter.Dispose();
LogStream.Dispose();
}
private void Log(LoggerCategory category, string message)
{
Logger.Log(category, message);
SaveToLog(category, message);
}
private void SaveToLog(LoggerCategory category, string message)
{
LogWriter.WriteLine($"[{category}] {message}");
LogWriter.Flush();
}
}
public enum LoggerCategory
{
Log,
Debug,
Error
}

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using Nebula.Launcher.FileApis;
using Nebula.Launcher.FileApis.Interfaces;
using Nebula.Launcher.Utils;
using Robust.LoaderApi;
namespace Nebula.Launcher.Services;
public class FileService
{
public static string RootPath = Path.Join(Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData), "./Datum/");
private readonly DebugService _debugService;
public readonly IReadWriteFileApi ContentFileApi;
public readonly IReadWriteFileApi EngineFileApi;
public readonly IReadWriteFileApi ManifestFileApi;
private HashApi? _hashApi;
public FileService(DebugService debugService)
{
_debugService = debugService;
ContentFileApi = CreateFileApi("content/");
EngineFileApi = CreateFileApi("engine/");
ManifestFileApi = CreateFileApi("manifest/");
}
public List<RobustManifestItem> ManifestItems
{
set => _hashApi = new HashApi(value, ContentFileApi);
}
public HashApi HashApi
{
get
{
if (_hashApi is null) throw new Exception("Hash API is not initialized!");
return _hashApi;
}
set => _hashApi = value;
}
public IReadWriteFileApi CreateFileApi(string path)
{
return new FileApi(Path.Join(RootPath, path));
}
public ZipFileApi OpenZip(string path, IFileApi fileApi)
{
if (!fileApi.TryOpen(path, out var zipStream))
return null;
var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read);
var prefix = "";
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) prefix = "Space Station 14.app/Contents/Resources/";
return new ZipFileApi(zipArchive, prefix);
}
}

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Threading;
using Nebula.Launcher.Models;
namespace Nebula.Launcher.Services;
[ServiceRegister]
public class HubService
{
private readonly RestService _restService;
public Action<HubServerChangedEventArgs>? HubServerChangedEventArgs;
public readonly ObservableCollection<string> HubList = new();
private readonly Dictionary<string, List<ServerInfo>> _servers = new();
public HubService(ConfigurationService configurationService, RestService restService)
{
_restService = restService;
HubList.CollectionChanged += HubListCollectionChanged;
foreach (var hubUrl in configurationService.GetConfigValue<string[]>(CurrentConVar.Hub)!)
{
HubList.Add(hubUrl);
}
}
private async void HubListCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems is not null)
{
foreach (var hubUri in e.NewItems)
{
var urlStr = (string)hubUri;
var servers = await _restService.GetAsyncDefault<List<ServerInfo>>(new Uri(urlStr), [], CancellationToken.None);
_servers[urlStr] = servers;
HubServerChangedEventArgs?.Invoke(new HubServerChangedEventArgs(servers, HubServerChangeAction.Add));
}
}
if (e.OldItems is not null)
{
foreach (var hubUri in e.OldItems)
{
var urlStr = (string)hubUri;
if (_servers.TryGetValue(urlStr, out var serverInfos))
{
_servers.Remove(urlStr);
HubServerChangedEventArgs?.Invoke(new HubServerChangedEventArgs(serverInfos, HubServerChangeAction.Remove));
}
}
}
}
}
public class HubServerChangedEventArgs : EventArgs
{
public HubServerChangeAction Action;
public List<ServerInfo> Items;
public HubServerChangedEventArgs(List<ServerInfo> items, HubServerChangeAction action)
{
Items = items;
Action = action;
}
}
public enum HubServerChangeAction
{
Add, Remove,
}

View File

@@ -0,0 +1,15 @@
using System;
namespace Nebula.Launcher.Services.Logging;
[ServiceRegister(typeof(ILogger))]
public class ConsoleLogger : ILogger
{
public void Log(LoggerCategory loggerCategory, string message)
{
Console.ForegroundColor = ConsoleColor.DarkCyan;
Console.Write($"[{Enum.GetName(loggerCategory)}] ");
Console.ResetColor();
Console.WriteLine(message);
}
}

View File

@@ -0,0 +1,6 @@
namespace Nebula.Launcher.Services.Logging;
public interface ILogger
{
public void Log(LoggerCategory loggerCategory, string message);
}

View File

@@ -0,0 +1,154 @@
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
namespace Nebula.Launcher.Services;
[ServiceRegister]
public class RestService
{
private readonly HttpClient _client = new();
private readonly DebugService _debug;
private readonly JsonSerializerOptions _serializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
public RestService(DebugService debug)
{
_debug = debug;
}
public async Task<RestResult<T>> GetAsync<T>(Uri uri, CancellationToken cancellationToken)
{
_debug.Debug("GET " + uri);
try
{
var response = await _client.GetAsync(uri, cancellationToken);
return await ReadResult<T>(response, cancellationToken);
}
catch (Exception ex)
{
_debug.Error("ERROR WHILE CONNECTION " + uri + ": " + ex.Message);
return new RestResult<T>(default, ex.Message, HttpStatusCode.RequestTimeout);
}
}
public async Task<T> GetAsyncDefault<T>(Uri uri, T defaultValue, CancellationToken cancellationToken)
{
var result = await GetAsync<T>(uri, cancellationToken);
return result.Value ?? defaultValue;
}
public async Task<RestResult<K>> PostAsync<K, T>(T information, Uri uri, CancellationToken cancellationToken)
{
_debug.Debug("POST " + uri);
try
{
var json = JsonSerializer.Serialize(information, _serializerOptions);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _client.PostAsync(uri, content, cancellationToken);
return await ReadResult<K>(response, cancellationToken);
}
catch (Exception ex)
{
_debug.Debug("ERROR " + ex.Message);
return new RestResult<K>(default, ex.Message, HttpStatusCode.RequestTimeout);
}
}
public async Task<RestResult<T>> PostAsync<T>(Stream stream, Uri uri, CancellationToken cancellationToken)
{
_debug.Debug("POST " + uri);
try
{
using var multipartFormContent =
new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture));
multipartFormContent.Add(new StreamContent(stream), "formFile", "image.png");
var response = await _client.PostAsync(uri, multipartFormContent, cancellationToken);
return await ReadResult<T>(response, cancellationToken);
}
catch (Exception ex)
{
_debug.Error("ERROR " + ex.Message);
if (ex.StackTrace != null) _debug.Error(ex.StackTrace);
return new RestResult<T>(default, ex.Message, HttpStatusCode.RequestTimeout);
}
}
public async Task<RestResult<T>> DeleteAsync<T>(Uri uri, CancellationToken cancellationToken)
{
_debug.Debug("DELETE " + uri);
try
{
var response = await _client.DeleteAsync(uri, cancellationToken);
return await ReadResult<T>(response, cancellationToken);
}
catch (Exception ex)
{
_debug.Debug("ERROR " + ex.Message);
return new RestResult<T>(default, ex.Message, HttpStatusCode.RequestTimeout);
}
}
private async Task<RestResult<T>> ReadResult<T>(HttpResponseMessage response, CancellationToken cancellationToken)
{
var content = await response.Content.ReadAsStringAsync(cancellationToken);
//_debug.Debug("CONTENT:" + content);
if (response.IsSuccessStatusCode)
{
_debug.Debug($"SUCCESSFUL GET CONTENT {typeof(T)}");
if (typeof(T) == typeof(RawResult))
return (new RestResult<RawResult>(new RawResult(content), null, response.StatusCode) as RestResult<T>)!;
return new RestResult<T>(JsonSerializer.Deserialize<T>(content, _serializerOptions), null,
response.StatusCode);
}
_debug.Error("ERROR " + response.StatusCode + " " + content);
return new RestResult<T>(default, "response code:" + response.StatusCode, response.StatusCode);
}
}
public class RestResult<T>
{
public string? Message;
public HttpStatusCode StatusCode;
public T? Value;
public RestResult(T? value, string? message, HttpStatusCode statusCode)
{
Value = value;
Message = message;
StatusCode = statusCode;
}
public static implicit operator T?(RestResult<T> result)
{
return result.Value;
}
}
public class RawResult
{
public string Result;
public RawResult(string result)
{
Result = result;
}
public static implicit operator string(RawResult result)
{
return result.Result;
}
}