Compare commits

..

No commits in common. "f0679403d1e76c4fee47011bfe57d0b159bc054f" and "34f298c43be7d927375629464203c8386202049a" have entirely different histories.

20 changed files with 474 additions and 671 deletions

View File

@ -1,75 +0,0 @@
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);
}
}

View File

@ -1,189 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using DTLib;
using DTLib.Extensions;
namespace launcher_client;
public class ConsoleWrapper : IConsoleWrapper
{
private List<string> _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();
}
});
}
/// <summary>
/// starts automatig gui redraw on console size change
/// </summary>
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<string> SplitStringToLines(string _s, int lineW)
{
var split = _s.Replace("\r", "").Split('\n');
if (_s.Length <= lineW)
return split;
List<string> 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();
}
}

View File

@ -1,169 +1,239 @@
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
{
public static bool Debug, Offline, Updated;
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();
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
try
{
Console.Title = "anarx_2";
Console.OutputEncoding = Encoding.UTF8;
Console.InputEncoding = Encoding.UTF8;
using ConsoleWrapper console = new ConsoleWrapper();
console.StartUpdating();
Console.CursorVisible = false;
// 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;
#if DEBUG
debug = true;
#else
if (args.Contains("debug")) debug = true;
#endif
if (args.Contains("offline")) offline = true;
if (args.Contains("updated")) updated = true;
try
{
var config = LauncherConfig.LoadOrCreateDefault();
NetworkManager networkManager = new NetworkManager(Logger, config.ServerAddress, config.ServerPort);
Config = LauncherConfig.LoadOrCreateDefault();
Logger.LogInfo(nameof(Main), "launcher started");
Logger.DebugLogEnabled = debug;
Logger.LogInfo("Main", "launcher is starting");
if(File.Exists("minecraft-launcher.exe_old"))
File.Delete("minecraft-launcher.exe_old");
// self-update
if (!Updated && !Offline)
{
Logger.LogInfo(nameof(Main), "checking for launcher update");
networkManager.ConnectToLauncherServer();
bool updateDownloaded = networkManager.TryDownloadLauncherUpdate("minecraft-launcher.exe_new");
if(updateDownloaded)
// обновление лаунчера
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;
}
networkManager.DisconnectFromLauncherServer();
}
// если уже обновлён
console.SetHeader($"username: {config.Username} | game memory: {config.GameMemory}M");
console.SetFooter("[L] launch game | [N] change username | [H] help");
console.DrawGui();
while (true)
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())
{
try
tabs.Login = tabs.Login.Remove(833, Config.Username.Length).Insert(833, Config.Username);
username = Config.Username;
}
RenderTab(tabs.Login);
while (true) try
// ReSharper disable once BadChildStatementIndent
{
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);
case ConsoleKey.F1:
RenderTab(tabs.Login);
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();
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 (config.Username.Length < 2)
throw new Exception("username is too short");
if (tabs.Current == tabs.Login)
{
RenderTab(tabs.Current);
if (username.Length < 2) throw new Exception("username is too short");
// обновление клиента
if (!Offline)
if (!offline)
{
networkManager.ConnectToLauncherServer();
networkManager.UpdateGame();
ConnectToLauncherServer();
UpdateGame();
}
// запуск майнкрафта
Logger.LogInfo(nameof(Main), "launching minecraft");
string gameOptions = ConstructGameLaunchArgs(config.Username,
NameUUIDFromString("OfflinePlayer:" + config.Username),
config.GameMemory,
config.GameWindowWidth,
config.GameWindowHeight,
Logger.LogInfo("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(nameof(Main), "minecraft closed");
Logger.LogInfo("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.");
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;
}
}
catch (Exception ex)
{
Logger.LogError(nameof(Main), ex);
}
Logger.LogError("Main", ex);
}
}
catch (Exception ex)
{
console.StopUpdating();
Logger.LogError(nameof(Main), ex);
Logger.LogError("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#

View File

@ -8,10 +8,10 @@ public class LauncherConfig
public static IOPath ConfigFilePath = "minecraft-launcher.dtsod";
public int GameMemory = 3000;
public int GameWindowHeight = 600;
public int GameWindowHeight = 500;
public int GameWindowWidth = 900;
public IOPath JavaPath = "jre/bin/javaw.exe";
public string ServerAddress = "185.117.155.226";
public IOPath JavaPath = "jre/bin/java.exe";
public string ServerAddress = "127.0.0.1";
public int ServerPort = 25000;
public string Username = "";

View File

@ -0,0 +1,91 @@
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 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();
var manifest = new DtsodV23(manifestContent);
var hasher = new Hasher();
foreach (var fileOnServerData in manifest)
{
IOPath fileOnClient = Path.Concat(dirOnClient, fileOnServerData.Key);
if (!File.Exists(fileOnClient) || (overwrite && hasher.HashFile(fileOnClient).HashToString() != 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('\\','/')))
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");
}
}

View File

@ -1,160 +0,0 @@
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);
}
/// <summary>
/// returns true if new update was downloaded
/// </summary>
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(28), 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");
}
}

View File

@ -0,0 +1,29 @@
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ [F1] login ┃ [F2] log ┃ [F3] settings ┃ [F4] EXIT ┃ [F5] refresh ┃
┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┫
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃
┃ ┃ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ ┃
┃ ┃ ┃ ┃ ┃ ┃
┃ ┃ ┃ press [ENTER] to exit ┃ ┃ ┃
┃ ┃ ┃ ┃ ┃ ┃
┃ ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃
┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

View File

@ -0,0 +1,29 @@
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ [F1] LOGIN ┃ [F2] log ┃ [F3] settings ┃ [F4] exit ┃ [F5] refresh ┃
┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┫
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┌──────────────────┐ ┃
┃ [N] nickname:│ │ ┃
┃ └──────────────────┘ ┃
┃ ┃
┃ ┏━━━━━━━━━━━━━━━━┓ ┃
┃ ┃ [L] login ┃ ┃
┃ ┗━━━━━━━━━━━━━━━━┛ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

View File

@ -0,0 +1,29 @@
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ [F1] login ┃ [F2] log ┃ [F3] SETTINGS ┃ [F4] exit ┃ [F5] refresh ┃
┣━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┫
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ я ещё не добавил настройки ┃
┃ ┃
┃ приходите позже ┃
┃ ┃
┃ ┃
┃ ┃
┃ ■ ■ ■ ■ ┃
┃ ■ ■ ■ ■ ┃
┃ ■ ■ ■ ■ ■ ■ ■ ┃
┃ ■ ■ ■ ■ ■ ■ ■ ┃
┃ ■ ■ ■ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

View File

@ -0,0 +1,3 @@
┏━━━━━━━━━┓
┃ ejejeje ┃
┗━━━━━━━━━┛

View File

@ -0,0 +1,16 @@
window:
{
type: "container";
anchor: [0us, 0us];
width: 90us;
height: 30us;
children:
{
test_label:
{
type: "label";
anchor: [0us, 0us];
resdir: "gui";
};
};
};

View File

@ -11,22 +11,20 @@
<ApplicationIcon>launcher.ico</ApplicationIcon>
<DebugType>embedded</DebugType>
<InvariantGlobalization>true</InvariantGlobalization>
<GenerateSupportedRuntime>false</GenerateSupportedRuntime>
<GenerateSupportedRuntime>False</GenerateSupportedRuntime>
</PropertyGroup>
<ItemGroup>
<None Remove="gui\exit.gui" />
<EmbeddedResource Include="gui\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Costura.Fody" Version="5.7.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="DTLib.Dtsod" Version="1.3.4" />
<PackageReference Include="DTLib.Logging" Version="1.3.5" />
<PackageReference Include="DTLib.Network" Version="1.4.2" />
<PackageReference Include="DTLib.XXHash" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="launcher_version.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<PackageReference Include="DTLib.Dtsod" Version="1.3.1" />
<PackageReference Include="DTLib.Logging" Version="1.3.1" />
<PackageReference Include="DTLib.Network" Version="1.3.3" />
</ItemGroup>
</Project>

View File

@ -1 +0,0 @@
2.0.0

View File

@ -1,3 +1,2 @@
set /p version=<launcher_version.txt
dotnet publish -c Release -o bin\publish -p:Version=%version%
dotnet publish -c Release -o bin\publish
del /f bin\publish\minecraft-launcher.exe.config

View File

@ -1,5 +1,8 @@
using System.Linq;
using DTLib.XXHash;
using System.Text;
using DTLib;
using DTLib.Extensions;
using DTLib.Filesystem;
using static launcher_server.Server;
namespace launcher_server;
@ -17,6 +20,7 @@ public static class Manifests
}
StringBuilder manifestBuilder = new();
Hasher hasher = new();
var manifestPath = Path.Concat(dir, "manifest.dtsod");
if (Directory.GetFiles(dir).Contains(manifestPath))
File.Delete(manifestPath);
@ -25,11 +29,8 @@ public static class Manifests
var fileRelative = fileInDir.RemoveBase(dir);
manifestBuilder.Append(fileRelative);
manifestBuilder.Append(": \"");
var fileStream = File.OpenRead(fileInDir);
ulong hash = xxHash64.ComputeHash(fileStream);
fileStream.Close();
string hashStr = BitConverter.GetBytes(hash).HashToString();
manifestBuilder.Append(hashStr);
byte[] hash = hasher.HashFile(Path.Concat(fileInDir));
manifestBuilder.Append(hash.HashToString());
manifestBuilder.Append("\";\n");
}
File.WriteAllText(manifestPath, manifestBuilder.ToString().Replace('\\','/'));
@ -58,7 +59,7 @@ public static class Manifests
}
dirlist_content_builder
.Append("\t\"")
.Append(dirs[^1].RemoveBase(sync_and_remove_dir).Str.Replace('\\','/'))
.Append(dirs[dirs.Length-1].RemoveBase(sync_and_remove_dir).Str.Replace('\\','/'))
.Append("\"\n");
dirlist_content_builder.Append("];");

View File

@ -10,24 +10,25 @@ global using DTLib.Extensions;
global using DTLib.Filesystem;
global using DTLib.Logging;
global using DTLib.Network;
using System.Diagnostics;
using DTLib.Ben.Demystifier;
using Timer = DTLib.Timer;
namespace launcher_server;
static class Server
{
public static ILogger Logger = new CompositeLogger(
private static ILogger logger = new CompositeLogger(
new FileLogger("logs","launcher-server"),
new ConsoleLogger());
static readonly Socket mainSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static ServerConfig Config = null!;
public static readonly IOPath shared_dir = "public";
public static readonly IOPath LatestLauncherVersionFile = "launcher_version.txt";
public static string LatestLauncherVersion = "0.0.1";
static void Main(string[] args)
{
var mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Timer? updateCheckTimer = null;
try
{
Console.Title = "minecraft_launcher_server";
@ -35,26 +36,22 @@ static class Server
Console.OutputEncoding = Encoding.Unicode;
Config = ServerConfig.LoadOrCreateDefault();
LatestLauncherVersion = File.ReadAllText(LatestLauncherVersionFile);
Updates.Check();
Logger.LogInfo(nameof(Main), "creating manifests...");
Manifests.CreateAllManifests();
Logger.LogInfo(nameof(Main), "manifests created");
CheckUpdates();
// check for updates every minute
var updateCheckTimer = new Timer(true, 60 * 1000, Updates.Check);
updateCheckTimer = new Timer(true, 60 * 1000, CheckUpdates);
updateCheckTimer.Start();
Logger.LogInfo("Main", $"local address: {Config.LocalIp}");
Logger.LogInfo("Main", $"public address: {Functions.GetPublicIP()}");
Logger.LogInfo("Main", $"port: {Config.LocalPort}");
logger.LogInfo("Main", $"local address: {Config.LocalIp}");
logger.LogInfo("Main", $"public address: {Functions.GetPublicIP()}");
logger.LogInfo("Main", $"port: {Config.LocalPort}");
mainSocket.Bind(new IPEndPoint(IPAddress.Parse(Config.LocalIp), Config.LocalPort));
mainSocket.Listen(1000);
Logger.LogInfo("Main", "server started succesfully");
logger.LogInfo("Main", "server started succesfully");
// запуск отдельного потока для каждого юзера
Logger.LogInfo("Main", "waiting for users");
logger.LogInfo("Main", "waiting for users");
while (true)
{
var userSocket = mainSocket.Accept();
@ -64,16 +61,58 @@ static class Server
}
catch (Exception ex)
{
Logger.LogError("Main", ex);
logger.LogError("Main", ex);
mainSocket.Close();
}
Console.ResetColor();
}
static void CheckUpdates()
{
logger.LogInfo(nameof(CheckUpdates), "checking for updates...");
IOPath updatesDir = "updates";
Directory.Create(updatesDir);
var updatedFiles = Directory.GetAllFiles(updatesDir);
foreach (var updatedFilePath in updatedFiles)
{
try
{
var relativeFilePath = updatedFilePath.RemoveBase(updatesDir);
if (relativeFilePath.Str is "minecraft-launcher-server" or "minecraft-launcher-server.exe")
{
logger.LogWarn(nameof(CheckUpdates), "program update found, restarting...");
string exeFile = relativeFilePath.Str;
string exeFileNew = exeFile + "_new";
File.Move(updatedFilePath, exeFileNew, true);
if(Environment.OSVersion.Platform == PlatformID.Win32NT)
Process.Start("cmd",$"/c move {exeFileNew} {exeFile} && {exeFile}");
else
File.Move(exeFileNew, exeFile, true);
Environment.Exit(0);
}
logger.LogDebug(nameof(CheckUpdates), "updating file "+relativeFilePath);
File.Move(updatedFilePath, relativeFilePath, true);
}
catch (Exception e)
{
logger.LogError(nameof(CheckUpdates), $"failed update of file '{updatedFilePath}'\n"
+ e.ToStringDemystified());
}
}
if(updatedFiles.Count != 0)
{
logger.LogInfo(nameof(CheckUpdates), "creating manifests...");
Manifests.CreateAllManifests();
logger.LogInfo(nameof(CheckUpdates), "manifests created");
}
logger.LogInfo(nameof(CheckUpdates), "update check completed");
}
// запускается для каждого юзера в отдельном потоке
static void HandleUser(Socket handlerSocket)
{
Logger.LogInfo(nameof(HandleUser), "user connecting... ");
logger.LogInfo(nameof(HandleUser), "user connecting... ");
try
{
// тут запрос пароля заменён запросом заглушки
@ -84,7 +123,7 @@ static class Server
// запрос от апдейтера
if (connectionString == "minecraft-launcher")
{
Logger.LogInfo(nameof(HandleUser), "incoming connection from minecraft-launcher");
logger.LogInfo(nameof(HandleUser), "incoming connection from minecraft-launcher");
handlerSocket.SendPackage("minecraft-launcher OK");
// обработка запросов
while (true)
@ -94,17 +133,14 @@ static class Server
string request = handlerSocket.GetPackage().BytesToString();
switch (request)
{
case "requesting latest launcher version":
handlerSocket.SendPackage("latest launcher version is "+LatestLauncherVersion);
break;
case "requesting launcher update":
Logger.LogInfo(nameof(HandleUser), "updater requested launcher update");
logger.LogInfo(nameof(HandleUser), "updater requested launcher update");
// ReSharper disable once InconsistentlySynchronizedField
fsp.UploadFile(Path.Concat(shared_dir, "minecraft-launcher.exe"));
break;
case "requesting file download":
var filePath = handlerSocket.GetPackage().BytesToString();
Logger.LogInfo(nameof(HandleUser), $"updater requested file {filePath}");
logger.LogInfo(nameof(HandleUser), $"updater requested file {filePath}");
if(filePath.EndsWith("manifest.dtsod"))
lock (Manifests.manifestLocker)
{
@ -122,18 +158,18 @@ static class Server
}
// неизвестный юзер
Logger.LogWarn(nameof(HandleUser),$"invalid connection string: '{connectionString}'");
logger.LogWarn(nameof(HandleUser),$"invalid connection string: '{connectionString}'");
handlerSocket.SendPackage("invalid connection string");
}
catch (Exception ex)
{
Logger.LogWarn(nameof(HandleUser), ex);
logger.LogWarn(nameof(HandleUser), ex);
}
finally
{
if (handlerSocket.Connected) handlerSocket.Shutdown(SocketShutdown.Both);
handlerSocket.Close();
Logger.LogInfo(nameof(HandleUser), "user disconnected");
logger.LogInfo(nameof(HandleUser), "user disconnected");
}
}

View File

@ -1,71 +0,0 @@
using System.Diagnostics;
using DTLib.Ben.Demystifier;
namespace launcher_server;
static class Updates
{
public static void Check()
{
Server.Logger.LogDebug(nameof(Check), "checking for updates...");
IOPath updatesDir = "updates";
Directory.Create(updatesDir);
var updatedFiles = Directory.GetAllFiles(updatesDir);
if(updatedFiles.Count != 0) Server.Logger.LogInfo(nameof(Check), $"updated files found in '{updatesDir}'");
foreach (var updatedFilePath in updatedFiles)
{
var relativeFilePath = updatedFilePath.RemoveBase(updatesDir);
if(relativeFilePath.Str is "minecraft-launcher-server" or "minecraft-launcher-server.exe")
SelfUpdate(updatedFilePath, relativeFilePath);
}
foreach (var updatedFilePath in updatedFiles)
{
try
{
var relativeFilePath = updatedFilePath.RemoveBase(updatesDir);
if (relativeFilePath == Path.Concat(Server.shared_dir, "launcher_version.txt"))
UpdateLauncherVersion(updatedFilePath);
else
{
Server.Logger.LogInfo(nameof(Check), "updating file " + relativeFilePath);
File.Move(updatedFilePath, relativeFilePath, true);
}
}
catch (Exception e)
{
Server.Logger.LogError(nameof(Check), $"failed update of file '{updatedFilePath}'\n"
+ e.ToStringDemystified());
}
}
if(updatedFiles.Count != 0)
{
Server.Logger.LogInfo(nameof(Check), "creating manifests...");
Manifests.CreateAllManifests();
Server.Logger.LogInfo(nameof(Check), "manifests created");
}
Server.Logger.LogDebug(nameof(Check), "update check completed");
}
private static void UpdateLauncherVersion(IOPath updatedFilePath)
{
Server.LatestLauncherVersion = File.ReadAllText(updatedFilePath);
Server.Logger.LogInfo(nameof(Check), "new launcher version: " + Server.LatestLauncherVersion);
File.Move(updatedFilePath, Server.LatestLauncherVersionFile, true);
}
public static void SelfUpdate(IOPath updatedFile, IOPath exeFile)
{
Server.Logger.LogWarn(nameof(SelfUpdate), "program update found, restarting...");
IOPath exeFileNew = exeFile + "_new";
File.Move(updatedFile, exeFileNew, true);
if(Environment.OSVersion.Platform == PlatformID.Win32NT)
Process.Start("cmd",$"/c move {exeFileNew} {exeFile} && {exeFile}");
else
File.Move(exeFileNew, exeFile, true);
Environment.Exit(0);
}
}

View File

@ -12,9 +12,8 @@
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DTLib.Dtsod" Version="1.3.4" />
<PackageReference Include="DTLib.Logging" Version="1.3.4" />
<PackageReference Include="DTLib.Network" Version="1.4.2" />
<PackageReference Include="DTLib.XXHash" Version="1.0.0" />
<PackageReference Include="DTLib.Dtsod" Version="1.3.1" />
<PackageReference Include="DTLib.Logging" Version="1.3.1" />
<PackageReference Include="DTLib.Network" Version="1.3.3" />
</ItemGroup>
</Project>

View File

@ -1,4 +1,3 @@
#!/bin/bash
dotnet publish -c release -o bin/publish \
--self-contained \
--use-current-runtime \