From 96d0490338e1d9ce4a9a94544326d244d15e2852 Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Tue, 23 Jan 2024 17:21:13 +0600 Subject: [PATCH] new gui and update logic --- minecraft-launcher-client/ConsoleBuffer.cs | 75 +++++ minecraft-launcher-client/ConsoleWrapper.cs | 189 +++++++++++ minecraft-launcher-client/Launcher.cs | 304 +++++++----------- minecraft-launcher-client/Network.cs | 111 ------- minecraft-launcher-client/NetworkManager.cs | 160 +++++++++ minecraft-launcher-client/gui/exit.gui | 29 -- minecraft-launcher-client/gui/login.gui | 29 -- minecraft-launcher-client/gui/settings.gui | 29 -- .../gui/test_label.colormap | 0 .../gui/test_label.textmap | 3 - minecraft-launcher-client/gui/window.dtsod | 16 - .../launcher-client.csproj | 15 +- .../launcher_version.txt | 1 + minecraft-launcher-client/publish.bat | 3 +- minecraft-launcher-server/Server.cs | 91 ++---- minecraft-launcher-server/Updates.cs | 68 ++++ minecraft-launcher-server/publish.sh | 1 + 17 files changed, 648 insertions(+), 476 deletions(-) create mode 100644 minecraft-launcher-client/ConsoleBuffer.cs create mode 100644 minecraft-launcher-client/ConsoleWrapper.cs delete mode 100644 minecraft-launcher-client/Network.cs create mode 100644 minecraft-launcher-client/NetworkManager.cs delete mode 100644 minecraft-launcher-client/gui/exit.gui delete mode 100644 minecraft-launcher-client/gui/login.gui delete mode 100644 minecraft-launcher-client/gui/settings.gui delete mode 100644 minecraft-launcher-client/gui/test_label.colormap delete mode 100644 minecraft-launcher-client/gui/test_label.textmap delete mode 100644 minecraft-launcher-client/gui/window.dtsod create mode 100644 minecraft-launcher-client/launcher_version.txt create mode 100644 minecraft-launcher-server/Updates.cs diff --git a/minecraft-launcher-client/ConsoleBuffer.cs b/minecraft-launcher-client/ConsoleBuffer.cs new file mode 100644 index 0000000..054fd27 --- /dev/null +++ b/minecraft-launcher-client/ConsoleBuffer.cs @@ -0,0 +1,75 @@ +using System; + +namespace launcher_client; + +public class ConsoleBuffer +{ + public int Width; + public int Height; + + private int _x, _y; + private char[][] _buffer; + + public ConsoleBuffer() + { + Width = Console.WindowWidth; + Height = Console.WindowHeight - 1; + + _buffer = new char[Height][]; + for(int y = 0; y < Height; y++) + { + _buffer[y] = new char[Width]; + for (int x = 0; x < Width; x++) + _buffer[y][x] = ' '; + } + } + + + public void SetCursorPosition(int x, int y) + { + if (x < 0 || y < 0 || x >= Width || y >= Height) + throw new Exception($"invalid cursor position ({x}, {y}) (width: {Width}, height: {Height})"); + _x = x; + _y = y; + } + + public void Write(char c) + { + _buffer[_y][_x] = c; + _x++; + if (_x == Width) + { + _x = 0; + _y++; + } + } + + public void Write(string s) + { + for(int i = 0; i < s.Length; i++) + { + char c = s[i]; + switch (c) + { + case '\r': + throw new Exception("restricted character: '\\r'"); + case '\n': + throw new Exception("restricted character: '\\n'"); + case '\t': + Write(" "); + break; + default: + Write(c); + break; + } + } + } + + public void Print() + { + Console.SetCursorPosition(0, 0); + for (int y = 0; y < Height; y++) + Console.Write(new string(_buffer[y])); + Console.SetCursorPosition(0, 0); + } +} \ No newline at end of file diff --git a/minecraft-launcher-client/ConsoleWrapper.cs b/minecraft-launcher-client/ConsoleWrapper.cs new file mode 100644 index 0000000..f137e0b --- /dev/null +++ b/minecraft-launcher-client/ConsoleWrapper.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DTLib; +using DTLib.Extensions; + +namespace launcher_client; + +public class ConsoleWrapper : IConsoleWrapper +{ + private List _textLines = new(); + private int _linesUp; + private string _headerText = ""; + private string _footerText = ""; + + private Timer _consoleSizeCheckTimer; + + public int TextAreaW; + public int TextAreaH; + + + public ConsoleWrapper() + { + int lastW = Console.WindowWidth; + int lastH = Console.WindowHeight; + _consoleSizeCheckTimer = new Timer(true, 50, () => + { + if(Console.WindowWidth != lastW || Console.WindowHeight != lastH) + { + lastW = Console.WindowWidth; + lastH = Console.WindowHeight; + Console.Clear(); + DrawGui(); + } + }); + } + + /// + /// starts automatig gui redraw on console size change + /// + public void StartUpdating() + { + _consoleSizeCheckTimer.Start(); + } + + public void StopUpdating() + { + _consoleSizeCheckTimer.Stop(); + } + + public void WriteLine(string msg) + { + _textLines.Add(msg); + DrawGui(); + } + + public void SetHeader(string s) => _headerText = s; + + public void SetFooter(string s) => _footerText = s; + + public void ScrollDown(int lines = 1) + { + _linesUp -= lines; + if (_linesUp < 0) + _linesUp = 0; + DrawGui(); + } + + public void ScrollUp(int lines = 1) + { + _linesUp += lines; + DrawGui(); + } + + public void DrawGui() + { + var b = new ConsoleBuffer(); + TextAreaW = b.Width - 6; + TextAreaH = b.Height - 6; + Console.ForegroundColor = ConsoleColor.White; + Console.CursorVisible = false; + DrawBorders(b); + DrawText(b); + DrawScrollBar(b); + DrawHeader(b); + DrawFooter(b); + b.Print(); + } + + private void DrawBorders(ConsoleBuffer b) + { + b.Write( '┏' + '━'.Multiply(b.Width - 2) + '┓'); + b.Write( '┃' + ' '.Multiply(b.Width - 2) + '┃'); + b.Write( '┣' + '━'.Multiply(b.Width - 4) + "┳━┫"); + for (int y = 0; y < b.Height - 6; y++) + b.Write('┃' + ' '.Multiply(b.Width - 4) + "┃ ┃"); + b.Write( '┣' + '━'.Multiply(b.Width - 4) + "┻━┫"); + b.Write( '┃' + ' '.Multiply(b.Width - 2) + '┃'); + b.Write( '┗' + '━'.Multiply(b.Width - 2) + '┛'); + } + + private void DrawScrollBar(ConsoleBuffer b) + { + int scrollBarX = b.Width - 2; + int scrollBarY = 3; + + int slideH = 0; + if (_textLines.Count >= TextAreaH) + slideH = (int)Math.Ceiling((double)TextAreaH * TextAreaH / _textLines.Count); + int slidePos = (int)Math.Ceiling((double) _linesUp / TextAreaH) + 1; + for(int y = 0; y < slideH; y++) + { + b.SetCursorPosition(scrollBarX, scrollBarY + TextAreaH - y - slidePos); + b.Write('▒'); + } + } + + private void DrawHeader(ConsoleBuffer b) + { + b.SetCursorPosition(2, 1); + b.Write(ChopLongLine(_headerText, b.Width - 4)); + } + + private void DrawFooter(ConsoleBuffer b) + { + b.SetCursorPosition(2, b.Height - 2); + b.Write(ChopLongLine(_footerText, b.Width - 4)); + } + + private static string ChopLongLine(string s, int maxLength) + { + if (s.Length <= maxLength) + return s; + + return s.Remove(maxLength - 3) + "..."; + } + + private void DrawText(ConsoleBuffer b) + { + int textAreaX = 2; + int textAreaY = 3; + + var realLines = _textLines + .SelectMany(s => SplitStringToLines(s, TextAreaW)) + .ToArray(); + + int linesUp = _linesUp + TextAreaH; + if (linesUp > realLines.Length) + { + linesUp = realLines.Length; + _linesUp = Math.Max(0, realLines.Length - TextAreaH); + } + + for (int y = 0; y < TextAreaH; y++) + { + b.SetCursorPosition(textAreaX, textAreaY + y); + int li = realLines.Length - linesUp + y; + if (li >= realLines.Length) + break; + b.Write(realLines[li]); + } + } + + private static ICollection SplitStringToLines(string _s, int lineW) + { + var split = _s.Replace("\r", "").Split('\n'); + if (_s.Length <= lineW) + return split; + + List lines = new(); + for (int spi = 0; spi < split.Length; spi++) + { + string s = split[spi]; + int linesCount = s.Length / lineW; + if (s.Length % lineW != 0) + linesCount++; + + for (int i = 0; i < linesCount; i++) + lines.Add(s.Substring(i * lineW, Math.Min(lineW, s.Length - i * lineW))); + } + + return lines; + } + + public void Dispose() + { + StopUpdating(); + } +} \ No newline at end of file diff --git a/minecraft-launcher-client/Launcher.cs b/minecraft-launcher-client/Launcher.cs index af3d80e..cba5c74 100644 --- a/minecraft-launcher-client/Launcher.cs +++ b/minecraft-launcher-client/Launcher.cs @@ -1,239 +1,169 @@ using System; using System.Diagnostics; -using System.Dynamic; using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Threading; using DTLib.Console; -using DTLib.Extensions; using DTLib.Logging; -using DTLib.Network; using DTLib.Filesystem; -using Directory = DTLib.Filesystem.Directory; -using File = DTLib.Filesystem.File; -using static launcher_client.Network; + namespace launcher_client; internal static partial class Launcher { - private static FileLogger _fileLogger = new("launcher-logs", "launcher-client"); - public static ILogger Logger = new CompositeLogger( - _fileLogger, - new ConsoleLogger()); - public static LauncherConfig Config = null!; - public static bool debug, offline, updated; - private static dynamic tabs = new ExpandoObject(); - + public static bool Debug, Offline, Updated; + private static void Main(string[] args) { + // console arguments parsing +#if DEBUG + Debug = true; +#else + if (args.Contains("debug")) + Debug = true; +#endif + if (args.Contains("offline")) + Offline = true; + if (args.Contains("updated")) + Updated = true; + + // console initialization + Console.Title = "anarx_2"; + Console.OutputEncoding = Encoding.UTF8; + Console.InputEncoding = Encoding.UTF8; + using ConsoleWrapper console = new ConsoleWrapper(); + console.StartUpdating(); + + // logger initialization + FileLogger fileLogger = new FileLogger("launcher-logs", "launcher-client"); + ConsoleWrapperLogger consoleWrapperLogger = new ConsoleWrapperLogger(console); + ILogger Logger = new CompositeLogger( + fileLogger, + consoleWrapperLogger + ); + Logger.DebugLogEnabled = true; // always print debug log to file + consoleWrapperLogger.DebugLogEnabled = Debug; + try { - Console.Title = "anarx_2"; - Console.OutputEncoding = Encoding.UTF8; - Console.InputEncoding = Encoding.UTF8; - Console.CursorVisible = false; - -#if DEBUG - debug = true; -#else - if (args.Contains("debug")) debug = true; -#endif - if (args.Contains("offline")) offline = true; - if (args.Contains("updated")) updated = true; - - Config = LauncherConfig.LoadOrCreateDefault(); + var config = LauncherConfig.LoadOrCreateDefault(); + NetworkManager networkManager = new NetworkManager(Logger, config.ServerAddress, config.ServerPort); - Logger.DebugLogEnabled = debug; - Logger.LogInfo("Main", "launcher is starting"); + Logger.LogInfo(nameof(Main), "launcher started"); if(File.Exists("minecraft-launcher.exe_old")) File.Delete("minecraft-launcher.exe_old"); - // обновление лаунчера - if (!updated && !offline) + // self-update + if (!Updated && !Offline) { - ConnectToLauncherServer(); - mainSocket.SendPackage("requesting launcher update"); - Fsp.DownloadFile("minecraft-launcher.exe_new"); - Logger.LogInfo("Main", "minecraft-launcher.exe_new downloaded"); - System.IO.File.Move("minecraft-launcher.exe", "minecraft-launcher.exe_old"); - Process.Start("cmd","/c " + - "move minecraft-launcher.exe_new minecraft-launcher.exe && " + - "minecraft-launcher.exe updated"); - return; + Logger.LogInfo(nameof(Main), "checking for launcher update"); + networkManager.ConnectToLauncherServer(); + bool updateDownloaded = networkManager.TryDownloadLauncherUpdate("minecraft-launcher.exe_new"); + if(updateDownloaded) + { + System.IO.File.Move("minecraft-launcher.exe", "minecraft-launcher.exe_old"); + Process.Start("cmd", "/c " + + "move minecraft-launcher.exe_new minecraft-launcher.exe && " + + "minecraft-launcher.exe updated"); + return; + } + networkManager.DisconnectFromLauncherServer(); } // если уже обновлён - tabs.Login = EmbeddedResources.ReadText("launcher_client.gui.login.gui"); - tabs.Settings = EmbeddedResources.ReadText("launcher_client.gui.settings.gui"); - tabs.Exit = EmbeddedResources.ReadText("launcher_client.gui.exit.gui"); - tabs.Log = ""; - tabs.Current = ""; - string username = ""; - if (!Config.Username.IsNullOrEmpty()) - { - tabs.Login = tabs.Login.Remove(833, Config.Username.Length).Insert(833, Config.Username); - username = Config.Username; - } - RenderTab(tabs.Login); + console.SetHeader($"username: {config.Username} | game memory: {config.GameMemory}M"); + console.SetFooter("[L] launch game | [N] change username | [H] help"); + console.DrawGui(); - while (true) try - // ReSharper disable once BadChildStatementIndent + while (true) { - var pressedKey = Console.ReadKey(true); // Считывание ввода - switch (pressedKey.Key) + try { - case ConsoleKey.F1: - RenderTab(tabs.Login); - break; - case ConsoleKey.N: - if (tabs.Current == tabs.Login) - { - tabs.Login = tabs.Login - .Remove(751, 20).Insert(751, "┏━━━━━━━━━━━━━━━━━━┓") - .Remove(831, 20).Insert(831, "┃ ┃") - .Remove(911, 20).Insert(911, "┗━━━━━━━━━━━━━━━━━━┛"); - RenderTab(tabs.Login); - var _username = ReadString(33, 10, 15); - tabs.Login = tabs.Login - .Remove(751, 20).Insert(751, "┌──────────────────┐") - .Remove(831, 20).Insert(831, "│ │") - .Remove(911, 20).Insert(911, "└──────────────────┘"); - RenderTab(tabs.Login); - if (_username.Length < 5) - throw new Exception("username length should be > 4 and < 17"); - Config.Username = _username; - Config.Save(); - username = _username; - tabs.Login = tabs.Login.Remove(833, _username.Length).Insert(833, _username); - RenderTab(tabs.Login); - } - break; - case ConsoleKey.L: - if (tabs.Current == tabs.Login) - { - RenderTab(tabs.Current); - if (username.Length < 2) throw new Exception("username is too short"); + var pressedKey = Console.ReadKey(true); // Считывание ввода + switch (pressedKey.Key) + { + case ConsoleKey.UpArrow: + console.ScrollUp(); + break; + case ConsoleKey.DownArrow: + console.ScrollDown(); + break; + case ConsoleKey.PageUp: + console.ScrollUp(console.TextAreaH); + break; + case ConsoleKey.PageDown: + console.ScrollDown(console.TextAreaH); + break; + case ConsoleKey.N: + // todo ReadLine wrapper + Console.SetCursorPosition(0, Console.WindowHeight - 1); + Console.CursorVisible = true; + string? _username = Console.ReadLine(); + if (_username == null || _username.Length < 2) + throw new Exception("too short username"); + config.Username = _username; + config.Save(); + + console.DrawGui(); + break; + case ConsoleKey.L: + if (config.Username.Length < 2) + throw new Exception("username is too short"); + // обновление клиента - if (!offline) + if (!Offline) { - ConnectToLauncherServer(); - UpdateGame(); + networkManager.ConnectToLauncherServer(); + networkManager.UpdateGame(); } // запуск майнкрафта - Logger.LogInfo("Main", "launching minecraft"); - string gameOptions = ConstructGameLaunchArgs(Config.Username, - NameUUIDFromString("OfflinePlayer:" + Config.Username), - Config.GameMemory, - Config.GameWindowWidth, - Config.GameWindowHeight, + Logger.LogInfo(nameof(Main), "launching minecraft"); + string gameOptions = ConstructGameLaunchArgs(config.Username, + NameUUIDFromString("OfflinePlayer:" + config.Username), + config.GameMemory, + config.GameWindowWidth, + config.GameWindowHeight, Directory.GetCurrent()); Logger.LogDebug("LaunchGame", gameOptions); - var gameProcess = Process.Start(Config.JavaPath.Str, gameOptions); + var gameProcess = Process.Start(config.JavaPath.Str, gameOptions); gameProcess?.WaitForExit(); - Logger.LogInfo("Main", "minecraft closed"); - } - break; - case ConsoleKey.F2: - tabs.Log = File.ReadAllText(_fileLogger.LogfileName); - RenderTab(tabs.Log, 9999); - break; - case ConsoleKey.F3: - RenderTab(tabs.Settings); - break; - case ConsoleKey.F4: - RenderTab(tabs.Exit); - break; - case ConsoleKey.Enter: - if (tabs.Current == tabs.Exit) - { - Console.Clear(); - // Console.BufferHeight = 9999; - return; - } - break; - case ConsoleKey.F5: - if (tabs.Current == tabs.Log) goto case ConsoleKey.F2; - RenderTab(tabs.Current); - Console.CursorVisible = false; - break; + Logger.LogInfo(nameof(Main), "minecraft closed"); + break; + case ConsoleKey.H: + console.WriteLine("help:"); + console.WriteLine(" Q: How to use this launcher?"); + console.WriteLine(" A: Set username if it isn't set and launch the game."); + console.WriteLine(" Q: How to change game memory and other settings?"); + console.WriteLine(" A: Edit the `minecraft-launcher.dtsod` file."); + console.WriteLine(" Q: How to disable game files update on launch?"); + console.WriteLine(" A: Restart the launcher with argument 'offline'."); + console.WriteLine(" Q: What to do if launcher doesn't work?"); + console.WriteLine(" A: Send latest log file from `launcher-logs/` to Timerix."); + break; + } + } + catch (Exception ex) + { + Logger.LogError(nameof(Main), ex); } - } - catch (Exception ex) - { - Logger.LogError("Main", ex); } } catch (Exception ex) { - Logger.LogError("Main", ex); + console.StopUpdating(); + Logger.LogError(nameof(Main), ex); ColoredConsole.Write("gray", "press any key to close..."); Console.ReadKey(); } - Console.CursorVisible = true; + Console.ResetColor(); } - private static void RenderTab(string tab, ushort bufferHeight = 30) - { - tabs.Current = tab; - Console.Clear(); - Console.SetWindowSize(80, 30); - // Console.SetBufferSize(80, bufferHeight); - ColoredConsole.Write("w", tab); - } - - private static string ReadString(ushort x, ushort y, ushort maxlength) - { - var output = ""; - tabs.Current = tabs.Current.Remove(y * 80 + x, maxlength).Insert(y * 80 + x, " ".Multiply(maxlength)); - while (true) - { - var pressedKey = Console.ReadKey(false); - switch (pressedKey.Key) - { - case ConsoleKey.Enter: - return output; - case ConsoleKey.Backspace: - if (output.Length > 0) - { - output = output.Remove(output.Length - 1); - RenderTab(tabs.Current); - Console.SetCursorPosition(x, y); - ColoredConsole.Write("c", output); - } - - break; - case ConsoleKey.Escape: - tabs.Current = tabs.Current.Remove(y * 80 + x, maxlength) - .Insert(y * 80 + x, " ".Multiply(maxlength)); - RenderTab(tabs.Current); - return ""; - //case ConsoleKey.Spacebar: - case ConsoleKey.UpArrow: - case ConsoleKey.DownArrow: - case ConsoleKey.LeftArrow: - case ConsoleKey.RightArrow: - break; - default: - if (output.Length <= maxlength) - { - string keyC = pressedKey.KeyChar.ToString(); - string thisChar = pressedKey.Modifiers.HasFlag(ConsoleModifiers.Shift) ? keyC.ToUpper() : keyC; - output += thisChar; - } - - RenderTab(tabs.Current); - Console.SetCursorPosition(x, y); - ColoredConsole.Write("c", output); - break; - } - } - } - //minecraft player uuid explanation //https://gist.github.com/CatDany/0e71ca7cd9b42a254e49/ //java uuid generation in c# diff --git a/minecraft-launcher-client/Network.cs b/minecraft-launcher-client/Network.cs deleted file mode 100644 index 6dd7469..0000000 --- a/minecraft-launcher-client/Network.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using DTLib; -using DTLib.Dtsod; -using DTLib.Extensions; -using DTLib.Filesystem; -using DTLib.Logging; -using DTLib.Network; -using DTLib.XXHash; -using static launcher_client.Launcher; - -namespace launcher_client; - -public class Network -{ - public static Socket mainSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - public static FSP Fsp = new(mainSocket); - - // подключение серверу - public static void ConnectToLauncherServer() - { - if (mainSocket.Connected) - { - Logger.LogInfo(nameof(Network), "socket is connected already. disconnecting..."); - mainSocket.Shutdown(SocketShutdown.Both); - mainSocket.Close(); - mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - Fsp = new(mainSocket); - } - - while (true) - try - { - Logger.LogInfo(nameof(Network), $"connecting to server {Config.ServerAddress}:{Config.ServerPort}"); - var ip = Dns.GetHostAddresses(Config.ServerAddress)[0]; - mainSocket.Connect(new IPEndPoint(ip, Config.ServerPort)); - Logger.LogInfo(nameof(Network), $"connected to server {ip}"); - break; - } - catch (SocketException ex) - { - Logger.LogError(nameof(Network), ex); - Thread.Sleep(2000); - } - - mainSocket.ReceiveTimeout = 2500; - mainSocket.SendTimeout = 2500; - mainSocket.GetAnswer("requesting user name"); - mainSocket.SendPackage("minecraft-launcher"); - mainSocket.GetAnswer("minecraft-launcher OK"); - } - - public static void DownloadByManifest(IOPath dirOnServer, IOPath dirOnClient, bool overwrite = false, bool delete_excess = false) - { - var manifestPath = Path.Concat(dirOnServer, "manifest.dtsod"); - Logger.LogDebug(nameof(Network), manifestPath); - string manifestContent = Fsp.DownloadFileToMemory(manifestPath).BytesToString(); - Logger.LogDebug(nameof(Network), manifestContent); - var manifest = new DtsodV23(manifestContent); - foreach (var fileOnServerData in manifest) - { - IOPath fileOnClient = Path.Concat(dirOnClient, fileOnServerData.Key); - if (!File.Exists(fileOnClient)) - { - Logger.LogDebug(nameof(Network), $"downloading {fileOnClient}"); - Fsp.DownloadFile(Path.Concat(dirOnServer, fileOnServerData.Key), fileOnClient); - } - else if (overwrite) - { - var fileStream = File.OpenRead(fileOnClient); - ulong hash = xxHash64.ComputeHash(fileStream); - fileStream.Close(); - string hashStr = BitConverter.GetBytes(hash).HashToString(); - if (hashStr != fileOnServerData.Value) - { - Logger.LogDebug(nameof(Network), $"downloading {fileOnClient} (hash {hashStr} != {fileOnServerData.Value})"); - Fsp.DownloadFile(Path.Concat(dirOnServer, fileOnServerData.Key), fileOnClient); - } - } - } - // удаление лишних файлов - if (delete_excess) - { - foreach (var file in Directory.GetAllFiles(dirOnClient)) - { - if (!manifest.ContainsKey(file.RemoveBase(dirOnClient).Str.Replace('\\','/'))) - { - Logger.LogDebug(nameof(Network), $"deleting {file}"); - File.Delete(file); - } - } - } - } - - public static void UpdateGame() - { - //обновление файлов клиента - Logger.LogInfo(nameof(Network), "updating client..."); - DownloadByManifest("download_if_not_exist", Directory.GetCurrent()); - DownloadByManifest("sync_always", Directory.GetCurrent(), true); - var dirlistDtsod = new DtsodV23(Fsp - .DownloadFileToMemory(Path.Concat("sync_and_remove","dirlist.dtsod")) - .BytesToString()); - foreach (string dir in dirlistDtsod["dirs"]) - DownloadByManifest(Path.Concat("sync_and_remove", dir), - Path.Concat(Directory.GetCurrent(), dir), true, true); - Logger.LogInfo(nameof(Network), "client updated"); - } -} \ No newline at end of file diff --git a/minecraft-launcher-client/NetworkManager.cs b/minecraft-launcher-client/NetworkManager.cs new file mode 100644 index 0000000..13ebec0 --- /dev/null +++ b/minecraft-launcher-client/NetworkManager.cs @@ -0,0 +1,160 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Threading; +using DTLib.Dtsod; +using DTLib.Extensions; +using DTLib.Filesystem; +using DTLib.Logging; +using DTLib.Network; +using DTLib.XXHash; + +namespace launcher_client; + +public class NetworkManager +{ + public Socket mainSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + public FSP Fsp; + private ILogger Logger; + private IPEndPoint ServerEndpoint; + + public NetworkManager(ILogger logger, IPEndPoint serverEndpoint) + { + Fsp = new FSP(mainSocket); + Logger = logger; + ServerEndpoint = serverEndpoint; + } + public NetworkManager(ILogger logger, string serverAddress, int serverPort) + : this(logger, new IPEndPoint(IPAddress.Parse(serverAddress), serverPort)) + {} + + // подключение серверу + public void ConnectToLauncherServer() + { + if (mainSocket.Connected) + { + Logger.LogInfo(nameof(NetworkManager), "socket is connected already. disconnecting..."); + DisconnectFromLauncherServer(); + } + + while (true) + try + { + Logger.LogInfo(nameof(NetworkManager), $"connecting to server {ServerEndpoint.Address}:{ServerEndpoint.Port}"); + mainSocket.Connect(ServerEndpoint); + Logger.LogInfo(nameof(NetworkManager), "connection established"); + break; + } + catch (SocketException ex) + { + Logger.LogError(nameof(NetworkManager), ex); + Thread.Sleep(2000); + } + + mainSocket.ReceiveTimeout = 2500; + mainSocket.SendTimeout = 2500; + mainSocket.GetAnswer("requesting user name"); + mainSocket.SendPackage("minecraft-launcher"); + mainSocket.GetAnswer("minecraft-launcher OK"); + } + + public void DisconnectFromLauncherServer() + { + mainSocket.Shutdown(SocketShutdown.Both); + mainSocket.Close(); + mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + Fsp = new(mainSocket); + } + + /// + /// returns true if new update was downloaded + /// + public bool TryDownloadLauncherUpdate(IOPath localFilePath) + { + try + { + Logger.LogDebug(nameof(NetworkManager), "requesting latest launcher version"); + mainSocket.SendPackage("requesting latest launcher version"); + var answer = mainSocket.GetPackage().BytesToString(); + if (!answer.StartsWith("latest launcher version is ") || + Version.TryParse(answer.Substring(27), out Version latestVersion)) + throw new Exception($"unexpected server answer: {answer}"); + + Version currentVersion = Assembly.GetExecutingAssembly().GetName().Version; + Logger.LogDebug(nameof(NetworkManager), + $"current version: {currentVersion}; latest version: {latestVersion}"); + if (currentVersion < latestVersion) + { + Logger.LogInfo(nameof(NetworkManager), $"downloading launcher update to {localFilePath}"); + mainSocket.SendPackage("requesting launcher update"); + Fsp.DownloadFile(localFilePath); + Logger.LogInfo(nameof(NetworkManager), "launcher update downloaded"); + return true; + } + } + catch (Exception ex) + { + Logger.LogError(nameof(NetworkManager), ex); + } + + return false; + } + + public void DownloadByManifest(IOPath dirOnServer, IOPath dirOnClient, bool overwrite, bool delete_excess) + { + var manifestPath = Path.Concat(dirOnServer, "manifest.dtsod"); + Logger.LogDebug(nameof(NetworkManager), manifestPath); + string manifestContent = Fsp.DownloadFileToMemory(manifestPath).BytesToString(); + Logger.LogDebug(nameof(NetworkManager), manifestContent); + var manifest = new DtsodV23(manifestContent); + foreach (var fileOnServerData in manifest) + { + IOPath fileOnClient = Path.Concat(dirOnClient, fileOnServerData.Key); + if (!File.Exists(fileOnClient)) + { + Logger.LogDebug(nameof(NetworkManager), $"downloading {fileOnClient}"); + Fsp.DownloadFile(Path.Concat(dirOnServer, fileOnServerData.Key), fileOnClient); + } + else if (overwrite) + { + var fileStream = File.OpenRead(fileOnClient); + ulong hash = xxHash64.ComputeHash(fileStream); + fileStream.Close(); + string hashStr = BitConverter.GetBytes(hash).HashToString(); + if (hashStr != fileOnServerData.Value) + { + Logger.LogDebug(nameof(NetworkManager), $"downloading {fileOnClient} (hash {hashStr} != {fileOnServerData.Value})"); + Fsp.DownloadFile(Path.Concat(dirOnServer, fileOnServerData.Key), fileOnClient); + } + } + } + // удаление лишних файлов + if (delete_excess) + { + foreach (var file in Directory.GetAllFiles(dirOnClient)) + { + if (!manifest.ContainsKey(file.RemoveBase(dirOnClient).Str.Replace('\\','/'))) + { + Logger.LogDebug(nameof(NetworkManager), $"deleting {file}"); + File.Delete(file); + } + } + } + } + + public void UpdateGame() + { + //обновление файлов клиента + Logger.LogInfo(nameof(NetworkManager), "updating client..."); + DownloadByManifest("download_if_not_exist", Directory.GetCurrent(), false, false); + DownloadByManifest("sync_always", Directory.GetCurrent(), true, false); + var dirlistDtsod = new DtsodV23(Fsp + .DownloadFileToMemory(Path.Concat("sync_and_remove","dirlist.dtsod")) + .BytesToString()); + foreach (string dir in dirlistDtsod["dirs"]) + DownloadByManifest(Path.Concat("sync_and_remove", dir), + Path.Concat(Directory.GetCurrent(), dir), true, true); + Logger.LogInfo(nameof(NetworkManager), "client updated"); + } +} \ No newline at end of file diff --git a/minecraft-launcher-client/gui/exit.gui b/minecraft-launcher-client/gui/exit.gui deleted file mode 100644 index d8232ad..0000000 --- a/minecraft-launcher-client/gui/exit.gui +++ /dev/null @@ -1,29 +0,0 @@ -┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ -┃ [F1] login ┃ [F2] log ┃ [F3] settings ┃ [F4] EXIT ┃ [F5] refresh ┃ -┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┫ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ -┃ ┃ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ ┃ -┃ ┃ ┃ ┃ ┃ ┃ -┃ ┃ ┃ press [ENTER] to exit ┃ ┃ ┃ -┃ ┃ ┃ ┃ ┃ ┃ -┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃ -┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ \ No newline at end of file diff --git a/minecraft-launcher-client/gui/login.gui b/minecraft-launcher-client/gui/login.gui deleted file mode 100644 index 6345030..0000000 --- a/minecraft-launcher-client/gui/login.gui +++ /dev/null @@ -1,29 +0,0 @@ -┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ -┃ [F1] LOGIN ┃ [F2] log ┃ [F3] settings ┃ [F4] exit ┃ [F5] refresh ┃ -┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┫ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┌──────────────────┐ ┃ -┃ [N] nickname:│ │ ┃ -┃ └──────────────────┘ ┃ -┃ ┃ -┃ ┏━━━━━━━━━━━━━━━━┓ ┃ -┃ ┃ [L] login ┃ ┃ -┃ ┗━━━━━━━━━━━━━━━━┛ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ \ No newline at end of file diff --git a/minecraft-launcher-client/gui/settings.gui b/minecraft-launcher-client/gui/settings.gui deleted file mode 100644 index 90b06e7..0000000 --- a/minecraft-launcher-client/gui/settings.gui +++ /dev/null @@ -1,29 +0,0 @@ -┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ -┃ [F1] login ┃ [F2] log ┃ [F3] SETTINGS ┃ [F4] exit ┃ [F5] refresh ┃ -┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┫ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ я ещё не добавил настройки ┃ -┃ ┃ -┃ приходите позже ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ■ ■ ■ ■ ┃ -┃ ■ ■ ■ ■ ┃ -┃ ■ ■ ■ ■ ■ ■ ■ ┃ -┃ ■ ■ ■ ■ ■ ■ ■ ┃ -┃ ■ ■ ■ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┃ ┃ -┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ \ No newline at end of file diff --git a/minecraft-launcher-client/gui/test_label.colormap b/minecraft-launcher-client/gui/test_label.colormap deleted file mode 100644 index e69de29..0000000 diff --git a/minecraft-launcher-client/gui/test_label.textmap b/minecraft-launcher-client/gui/test_label.textmap deleted file mode 100644 index 9d1c046..0000000 --- a/minecraft-launcher-client/gui/test_label.textmap +++ /dev/null @@ -1,3 +0,0 @@ -┏━━━━━━━━━┓ -┃ ejejeje ┃ -┗━━━━━━━━━┛ diff --git a/minecraft-launcher-client/gui/window.dtsod b/minecraft-launcher-client/gui/window.dtsod deleted file mode 100644 index 6585c34..0000000 --- a/minecraft-launcher-client/gui/window.dtsod +++ /dev/null @@ -1,16 +0,0 @@ -window: -{ - type: "container"; - anchor: [0us, 0us]; - width: 90us; - height: 30us; - children: - { - test_label: - { - type: "label"; - anchor: [0us, 0us]; - resdir: "gui"; - }; - }; -}; \ No newline at end of file diff --git a/minecraft-launcher-client/launcher-client.csproj b/minecraft-launcher-client/launcher-client.csproj index 49b8cee..4cc4ec1 100644 --- a/minecraft-launcher-client/launcher-client.csproj +++ b/minecraft-launcher-client/launcher-client.csproj @@ -11,21 +11,22 @@ launcher.ico embedded true - False + false - - - - - all - + + + + + Always + + \ No newline at end of file diff --git a/minecraft-launcher-client/launcher_version.txt b/minecraft-launcher-client/launcher_version.txt new file mode 100644 index 0000000..f21c2ca --- /dev/null +++ b/minecraft-launcher-client/launcher_version.txt @@ -0,0 +1 @@ +2.0.0 \ No newline at end of file diff --git a/minecraft-launcher-client/publish.bat b/minecraft-launcher-client/publish.bat index 90b87f1..a0eff2c 100644 --- a/minecraft-launcher-client/publish.bat +++ b/minecraft-launcher-client/publish.bat @@ -1,2 +1,3 @@ -dotnet publish -c Release -o bin\publish +set /p version=