commit 9da952ece37e85ec8afab63365b3378a85e5322b Author: Timerix22 Date: Sat Jan 6 19:43:51 2024 +0600 added project files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62faaac --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Build results +[Bb]in/ +.bin/ +[Dd]ebug/ +[Rr]elease/ +[Rr]eleases/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ +[Pp]ublish/ + +# IDE files +.vs/ +.vscode/ +.vshistory/ +.idea/ +.editorconfig +*.user + +#backups +.old*/ \ No newline at end of file diff --git a/minecraft-launcher-client/Launcher.LaunchGame.cs b/minecraft-launcher-client/Launcher.LaunchGame.cs new file mode 100644 index 0000000..9f8243b --- /dev/null +++ b/minecraft-launcher-client/Launcher.LaunchGame.cs @@ -0,0 +1,97 @@ +namespace launcher_client; + +internal static partial class Launcher +{ + static string ConstructGameLaunchArgs(string username, string uuid, int maxmemory, int width, int height, + string gameDir) + => "-XX:+UnlockExperimentalVMOptions " + + "-XX:+UseG1GC " + + "-XX:G1NewSizePercent=20 " + + "-XX:G1ReservePercent=20 " + + "-XX:MaxGCPauseMillis=50 " + + "-XX:G1HeapRegionSize=32M " + + "-XX:+DisableExplicitGC " + + "-XX:+AlwaysPreTouch " + + "-XX:+ParallelRefProcEnabled " + + "-Xms512M " + + $"-Xmx{maxmemory}M " + + "-Dfile.encoding=UTF-8 " + + "-Dfml.ignoreInvalidMinecraftCertificates=true " + + "-Dfml.ignorePatchDiscrepancies=true " + + "-Djava.net.useSystemProxies=true " + + "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump " + + "\"-Dos.name=Windows 10\" " + + "-Dos.version=10.0 " + + @"-Djava.library.path=versions\1.12.2-forge-14.23.5.2860\natives " + + "-Dminecraft.launcher.brand=java-minecraft-launcher " + + "-Dminecraft.launcher.version=1.6.84-j " + + @"-Dminecraft.client.jar=versions\1.12.2-forge-14.23.5.2860\1.12.2-forge-14.23.5.2860.jar " + + "-cp " + + @"libraries\com\turikhay\ca-fixer\1.0\ca-fixer-1.0.jar;" + + @"libraries\net\minecraftforge\forge\1.12.2-14.23.5.2860\forge-1.12.2-14.23.5.2860.jar;" + + @"libraries\org\ow2\asm\asm-debug-all\5.2\asm-debug-all-5.2.jar;" + + @"libraries\net\minecraft\launchwrapper\1.12\launchwrapper-1.12.jar;" + + @"libraries\org\jline\jline\3.5.1\jline-3.5.1.jar;" + + @"libraries\com\typesafe\akka\akka-actor_2.11\2.3.3\akka-actor_2.11-2.3.3.jar;" + + @"libraries\com\typesafe\config\1.2.1\config-1.2.1.jar;" + + @"libraries\org\scala-lang\scala-actors-migration_2.11\1.1.0\scala-actors-migration_2.11-1.1.0.jar;" + + @"libraries\org\scala-lang\scala-compiler\2.11.1\scala-compiler-2.11.1.jar;" + + @"libraries\org\scala-lang\plugins\scala-continuations-library_2.11\1.0.2_mc\scala-continuations-library_2.11-1.0.2_mc.jar;" + + @"libraries\org\scala-lang\plugins\scala-continuations-plugin_2.11.1\1.0.2_mc\scala-continuations-plugin_2.11.1-1.0.2_mc.jar;" + + @"libraries\org\scala-lang\scala-library\2.11.1\scala-library-2.11.1.jar;" + + @"libraries\org\scala-lang\scala-parser-combinators_2.11\1.0.1\scala-parser-combinators_2.11-1.0.1.jar;" + + @"libraries\org\scala-lang\scala-reflect\2.11.1\scala-reflect-2.11.1.jar;" + + @"libraries\org\scala-lang\scala-swing_2.11\1.0.1\scala-swing_2.11-1.0.1.jar;" + + @"libraries\org\scala-lang\scala-xml_2.11\1.0.2\scala-xml_2.11-1.0.2.jar;" + + @"libraries\lzma\lzma\0.0.1\lzma-0.0.1.jar;" + + @"libraries\java3d\vecmath\1.5.2\vecmath-1.5.2.jar;" + + @"libraries\net\sf\trove4j\trove4j\3.0.3\trove4j-3.0.3.jar;" + + @"libraries\org\apache\maven\maven-artifact\3.5.3\maven-artifact-3.5.3.jar;" + + @"libraries\net\sf\jopt-simple\jopt-simple\5.0.3\jopt-simple-5.0.3.jar;" + + @"libraries\org\apache\logging\log4j\log4j-api\2.15.0\log4j-api-2.15.0.jar;" + + @"libraries\org\apache\logging\log4j\log4j-core\2.15.0\log4j-core-2.15.0.jar;" + + @"libraries\ru\tlauncher\patchy\1.0.0\patchy-1.0.0.jar;" + + @"libraries\oshi-project\oshi-core\1.1\oshi-core-1.1.jar;" + + @"libraries\net\java\dev\jna\jna\4.4.0\jna-4.4.0.jar;" + + @"libraries\net\java\dev\jna\platform\3.4.0\platform-3.4.0.jar;" + + @"libraries\com\ibm\icu\icu4j-core-mojang\51.2\icu4j-core-mojang-51.2.jar;" + + @"libraries\com\paulscode\codecjorbis\20101023\codecjorbis-20101023.jar;" + + @"libraries\com\paulscode\codecwav\20101023\codecwav-20101023.jar;" + + @"libraries\com\paulscode\libraryjavasound\20101123\libraryjavasound-20101123.jar;" + + @"libraries\com\paulscode\librarylwjglopenal\20100824\librarylwjglopenal-20100824.jar;" + + @"libraries\com\paulscode\soundsystem\20120107\soundsystem-20120107.jar;" + + @"libraries\io\netty\netty-all\4.1.9.Final\netty-all-4.1.9.Final.jar;" + + @"libraries\com\google\guava\guava\21.0\guava-21.0.jar;" + + @"libraries\org\apache\commons\commons-lang3\3.5\commons-lang3-3.5.jar;" + + @"libraries\commons-io\commons-io\2.5\commons-io-2.5.jar;" + + @"libraries\commons-codec\commons-codec\1.10\commons-codec-1.10.jar;" + + @"libraries\net\java\jinput\jinput\2.0.5\jinput-2.0.5.jar;" + + @"libraries\net\java\jutils\jutils\1.0.0\jutils-1.0.0.jar;" + + @"libraries\com\google\code\gson\gson\2.8.0\gson-2.8.0.jar;" + + @"libraries\by\ely\authlib\3.11.49.2\authlib-3.11.49.2.jar;" + + @"libraries\com\mojang\realms\1.10.22\realms-1.10.22.jar;" + + @"libraries\org\apache\commons\commons-compress\1.8.1\commons-compress-1.8.1.jar;" + + @"libraries\org\apache\httpcomponents\httpclient\4.3.3\httpclient-4.3.3.jar;" + + @"libraries\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar;" + + @"libraries\org\apache\httpcomponents\httpcore\4.3.2\httpcore-4.3.2.jar;" + + @"libraries\it\unimi\dsi\fastutil\7.1.0\fastutil-7.1.0.jar;" + + @"libraries\org\apache\logging\log4j\log4j-api\2.8.1\log4j-api-2.8.1.jar;" + + @"libraries\org\apache\logging\log4j\log4j-core\2.8.1\log4j-core-2.8.1.jar;" + + @"libraries\org\lwjgl\lwjgl\lwjgl\2.9.4-nightly-20150209\lwjgl-2.9.4-nightly-20150209.jar;" + + @"libraries\org\lwjgl\lwjgl\lwjgl_util\2.9.4-nightly-20150209\lwjgl_util-2.9.4-nightly-20150209.jar;" + + @"libraries\com\mojang\text2speech\1.10.3\text2speech-1.10.3.jar;" + + @"versions\1.12.2-forge-14.23.5.2860\1.12.2-forge-14.23.5.2860.jar " + + "-Xss2M net.minecraft.launchwrapper.Launch " + + $"--username {username} " + + "--version 1.12.2-forge-14.23.5.2860 " + + $"--gameDir {gameDir} " + + "--assetsDir assets " + + "--assetIndex 1.12 " + + $"--uuid {uuid} " + + "--accessToken null " + + "--userType mojang " + + "--tweakClass net.minecraftforge.fml.common.launcher.FMLTweaker " + + "--versionType Forge " + + $"--width {width} " + + $"--height {height}"; +} \ No newline at end of file diff --git a/minecraft-launcher-client/Launcher.cs b/minecraft-launcher-client/Launcher.cs new file mode 100644 index 0000000..b35e571 --- /dev/null +++ b/minecraft-launcher-client/Launcher.cs @@ -0,0 +1,254 @@ +using System; +using System.Diagnostics; +using System.Dynamic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +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(); + + private static void Main(string[] args) + { + 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 = !File.Exists(LauncherConfig.ConfigFilePath) + ? LauncherConfig.CreateDefault() + : LauncherConfig.LoadFromFile(); + + Logger.DebugLogEnabled = debug; + Logger.LogInfo("Main", "launcher is starting"); + + if(File.Exists("minecraft-launcher.exe_old")) + File.Delete("minecraft-launcher.exe_old"); + + // обновление лаунчера + 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; + } + + // если уже обновлён + 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); + + while (true) try + // ReSharper disable once BadChildStatementIndent + { + var pressedKey = Console.ReadKey(true); // Считывание ввода + switch (pressedKey.Key) + { + 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"); + + // обновление клиента + if (!offline) + { + ConnectToLauncherServer(); + UpdateGame(); + } + + // запуск майнкрафта + 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); + 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; + } + } + catch (Exception ex) + { + Logger.LogError("Main", ex); + } + } + catch (Exception ex) + { + Logger.LogError("Main", ex); + ColoredConsole.Write("gray", "press any key to close..."); + Console.ReadKey(); + } + Console.CursorVisible = true; + } + + 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# + //https://stackoverflow.com/questions/18021808/uuid-interop-with-c-sharp-code + public static string NameUUIDFromString(string input) + => NameUUIDFromBytes(Encoding.UTF8.GetBytes(input)); + + public static string NameUUIDFromBytes(byte[] input) + { + byte[] hash = MD5.HashData(input); + hash[6] &= 0x0f; + hash[6] |= 0x30; + hash[8] &= 0x3f; + hash[8] |= 0x80; + string hex = BitConverter.ToString(hash).Replace("-", string.Empty).ToLower(); + return hex.Insert(8, "-").Insert(13, "-").Insert(18, "-").Insert(23, "-"); + } +} \ No newline at end of file diff --git a/minecraft-launcher-client/LauncherConfig.cs b/minecraft-launcher-client/LauncherConfig.cs new file mode 100644 index 0000000..0663817 --- /dev/null +++ b/minecraft-launcher-client/LauncherConfig.cs @@ -0,0 +1,56 @@ +using DTLib.Dtsod; +using DTLib.Filesystem; + +namespace launcher_client; + +public class LauncherConfig +{ + public static IOPath ConfigFilePath = "minecraft-launcher.dtsod"; + + public int GameMemory = 3000; + public int GameWindowHeight = 500; + public int GameWindowWidth = 900; + public IOPath JavaPath = "jre/bin/java.exe"; + public string ServerAddress = "127.0.0.1"; + public int ServerPort = 25000; + public string Username = ""; + + private LauncherConfig(){} + + private LauncherConfig(DtsodV23 dtsod) + { + GameMemory = dtsod["gameMemory"]; + GameWindowHeight = dtsod["gameWindowHeight"]; + GameWindowWidth = dtsod["gameWindowWidth"]; + JavaPath = dtsod["javaPath"]; + ServerAddress = dtsod["serverAddress"]; + ServerPort = dtsod["serverPort"]; + Username = dtsod["username"]; + } + + public static LauncherConfig LoadFromFile() => new(new DtsodV23(File.ReadAllText(ConfigFilePath))); + + public DtsodV23 ToDtsod() => + new() + { + { "gameMemory", GameMemory }, + { "gameWindowHeight", GameWindowHeight }, + { "gameWindowWidth", GameWindowWidth }, + { "javaPath", JavaPath.Str }, + { "serverAddress", ServerAddress }, + { "serverPort", ServerPort }, + { "username", Username }, + }; + + public void Save() + { + File.WriteAllText(ConfigFilePath, ToDtsod().ToString()); + } + + public static LauncherConfig CreateDefault() + { + var c = new LauncherConfig(); + c.Save(); + return c; + } +} \ No newline at end of file diff --git a/minecraft-launcher-client/Network.cs b/minecraft-launcher-client/Network.cs new file mode 100644 index 0000000..8f4d686 --- /dev/null +++ b/minecraft-launcher-client/Network.cs @@ -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"); + } +} \ No newline at end of file diff --git a/minecraft-launcher-client/gui/exit.gui b/minecraft-launcher-client/gui/exit.gui new file mode 100644 index 0000000..d8232ad --- /dev/null +++ b/minecraft-launcher-client/gui/exit.gui @@ -0,0 +1,29 @@ +┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ +┃ [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 new file mode 100644 index 0000000..6345030 --- /dev/null +++ b/minecraft-launcher-client/gui/login.gui @@ -0,0 +1,29 @@ +┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ +┃ [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 new file mode 100644 index 0000000..90b06e7 --- /dev/null +++ b/minecraft-launcher-client/gui/settings.gui @@ -0,0 +1,29 @@ +┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ +┃ [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 new file mode 100644 index 0000000..e69de29 diff --git a/minecraft-launcher-client/gui/test_label.textmap b/minecraft-launcher-client/gui/test_label.textmap new file mode 100644 index 0000000..9d1c046 --- /dev/null +++ b/minecraft-launcher-client/gui/test_label.textmap @@ -0,0 +1,3 @@ +┏━━━━━━━━━┓ +┃ ejejeje ┃ +┗━━━━━━━━━┛ diff --git a/minecraft-launcher-client/gui/window.dtsod b/minecraft-launcher-client/gui/window.dtsod new file mode 100644 index 0000000..6585c34 --- /dev/null +++ b/minecraft-launcher-client/gui/window.dtsod @@ -0,0 +1,16 @@ +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 new file mode 100644 index 0000000..157c210 --- /dev/null +++ b/minecraft-launcher-client/launcher-client.csproj @@ -0,0 +1,20 @@ + + + net8.0 + disable + enable + Exe + launcher_client + minecraft-launcher + launcher.ico + + + + + + + + + + + \ No newline at end of file diff --git a/minecraft-launcher-client/launcher.ico b/minecraft-launcher-client/launcher.ico new file mode 100644 index 0000000..9b68a7e Binary files /dev/null and b/minecraft-launcher-client/launcher.ico differ diff --git a/minecraft-launcher-client/publish.sh b/minecraft-launcher-client/publish.sh new file mode 100644 index 0000000..e024786 --- /dev/null +++ b/minecraft-launcher-client/publish.sh @@ -0,0 +1,10 @@ +dotnet publish -c release -o bin/publish \ + --self-contained \ + --use-current-runtime \ + -p:PublishSingleFile=true \ + -p:PublishTrimmed=true \ + -p:TrimMode=partial \ + -p:EnableCompressionInSingleFile=true \ + -p:OptimizationPreference=Size \ + -p:InvariantGlobalization=true \ + -p:DebugType=none diff --git a/minecraft-launcher-server/Manifests.cs b/minecraft-launcher-server/Manifests.cs new file mode 100644 index 0000000..47fbfa5 --- /dev/null +++ b/minecraft-launcher-server/Manifests.cs @@ -0,0 +1,69 @@ +using System.Linq; +using System.Text; +using DTLib; +using DTLib.Extensions; +using DTLib.Filesystem; +using static launcher_server.Server; + +namespace launcher_server; + +public static class Manifests +{ + static object manifestLocker = new(); + + public static void CreateManifest(IOPath dir) + { + if(!Directory.Exists(dir)) + { + Directory.Create(dir); + return; + } + + StringBuilder manifestBuilder = new(); + Hasher hasher = new(); + var manifestPath = Path.Concat(dir, "manifest.dtsod"); + if (Directory.GetFiles(dir).Contains(manifestPath)) + File.Delete(manifestPath); + foreach (var fileInDir in Directory.GetAllFiles(dir)) + { + var fileRelative = fileInDir.RemoveBase(dir); + manifestBuilder.Append(fileRelative); + manifestBuilder.Append(": \""); + byte[] hash = hasher.HashFile(Path.Concat(fileInDir)); + manifestBuilder.Append(hash.HashToString()); + manifestBuilder.Append("\";\n"); + } + File.WriteAllText(manifestPath, manifestBuilder.ToString().Replace('\\','/')); + } + + public static void CreateAllManifests() + { + lock (manifestLocker) + { + var sync_and_remove_dir = Path.Concat(shared_dir, "sync_and_remove"); + CreateManifest(Path.Concat(shared_dir, "download_if_not_exist")); + CreateManifest(Path.Concat(shared_dir, "sync_always")); + if (!Directory.Exists(sync_and_remove_dir)) + Directory.Create(sync_and_remove_dir); + else foreach (var dir in Directory.GetDirectories(sync_and_remove_dir)) + CreateManifest(dir); + StringBuilder dirlist_content_builder = new("dirs: [\n"); + + var dirs = Directory.GetDirectories(sync_and_remove_dir); + for (var i = 0; i < dirs.Length-1; i++) + { + dirlist_content_builder + .Append("\t\"") + .Append(dirs[i].RemoveBase(sync_and_remove_dir).Str.Replace('\\','/')) + .Append("\",\n"); + } + dirlist_content_builder + .Append("\t\"") + .Append(dirs[dirs.Length-1].RemoveBase(sync_and_remove_dir).Str.Replace('\\','/')) + .Append("\"\n"); + + dirlist_content_builder.Append("];"); + File.WriteAllText(Path.Concat(sync_and_remove_dir, "dirlist.dtsod"), dirlist_content_builder.ToString()); + } + } +} \ No newline at end of file diff --git a/minecraft-launcher-server/Server.cs b/minecraft-launcher-server/Server.cs new file mode 100644 index 0000000..83f047d --- /dev/null +++ b/minecraft-launcher-server/Server.cs @@ -0,0 +1,118 @@ +using System; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using DTLib.Dtsod; +using DTLib.Extensions; +using DTLib.Filesystem; +using DTLib.Logging; +using DTLib.Network; + +namespace launcher_server; + +static class Server +{ + 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 DtsodV23 config = null!; + public static readonly IOPath shared_dir = "public"; + + + static void Main(string[] args) + { + try + { + Console.Title = "minecraft_launcher_server"; + Console.InputEncoding = Encoding.Unicode; + Console.OutputEncoding = Encoding.Unicode; + + config = new DtsodV23(File.ReadAllText("minecraft-launcher-server.dtsod")); + + logger.LogInfo("Main", $"local address: {config["local_ip"]}"); + logger.LogInfo("Main", $"public address: {Functions.GetPublicIP()}"); + logger.LogInfo("Main", $"port: {config["local_port"]}"); + mainSocket.Bind(new IPEndPoint(IPAddress.Parse(config["local_ip"]), config["local_port"])); + mainSocket.Listen(1000); + Manifests.CreateAllManifests(); + logger.LogInfo("Main", "server started succesfully"); + // запуск отдельного потока для каждого юзера + logger.LogInfo("Main", "waiting for users"); + while (true) + { + var userSocket = mainSocket.Accept(); + var userThread = new Thread(obj => HandleUser((Socket)obj!)); + userThread.Start(userSocket); + } + } + catch (Exception ex) + { + logger.LogError("Main", ex); + mainSocket.Close(); + } + logger.LogInfo("Main", ""); + } + + // запускается для каждого юзера в отдельном потоке + static void HandleUser(Socket handlerSocket) + { + logger.LogInfo(nameof(HandleUser), "user connecting... "); + try + { + // тут запрос пароля заменён запросом заглушки + handlerSocket.SendPackage("requesting user name"); + string connectionString = handlerSocket.GetPackage().BytesToString(); + FSP fsp = new(handlerSocket); + + // запрос от апдейтера + if (connectionString == "minecraft-launcher") + { + logger.LogInfo(nameof(HandleUser), "incoming connection from minecraft-launcher"); + handlerSocket.SendPackage("minecraft-launcher OK"); + // обработка запросов + while (true) + { + if (handlerSocket.Available >= 2) + { + string request = handlerSocket.GetPackage().BytesToString(); + switch (request) + { + case "requesting 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 file = handlerSocket.GetPackage().BytesToString(); + logger.LogInfo(nameof(HandleUser), $"updater requested file {file}"); + // ReSharper disable once InconsistentlySynchronizedField + fsp.UploadFile(Path.Concat(shared_dir, file)); + break; + default: + throw new Exception("unknown request: " + request); + } + } + else Thread.Sleep(50); + } + } + // неизвестный юзер + + logger.LogWarn(nameof(HandleUser),$"invalid connection string: '{connectionString}'"); + handlerSocket.SendPackage("invalid connection string"); + } + catch (Exception ex) + { + logger.LogWarn(nameof(HandleUser), ex); + } + finally + { + if (handlerSocket.Connected) handlerSocket.Shutdown(SocketShutdown.Both); + handlerSocket.Close(); + logger.LogInfo(nameof(HandleUser), "user disconnected"); + } + } + + +} \ No newline at end of file diff --git a/minecraft-launcher-server/launcher-server.csproj b/minecraft-launcher-server/launcher-server.csproj new file mode 100644 index 0000000..2428d2c --- /dev/null +++ b/minecraft-launcher-server/launcher-server.csproj @@ -0,0 +1,16 @@ + + + net8.0 + disable + enable + Exe + launcher_server + minecraft-launcher-server + launcher.ico + + + + + + + \ No newline at end of file diff --git a/minecraft-launcher-server/launcher.ico b/minecraft-launcher-server/launcher.ico new file mode 100644 index 0000000..9b68a7e Binary files /dev/null and b/minecraft-launcher-server/launcher.ico differ diff --git a/minecraft-launcher-server/minecraft-launcher-server.dtsod.default b/minecraft-launcher-server/minecraft-launcher-server.dtsod.default new file mode 100644 index 0000000..7ed1467 --- /dev/null +++ b/minecraft-launcher-server/minecraft-launcher-server.dtsod.default @@ -0,0 +1,2 @@ +local_ip: "127.0.0.1"; +local_port: 25000; diff --git a/minecraft-launcher-server/minecraft-launcher-server.service b/minecraft-launcher-server/minecraft-launcher-server.service new file mode 100644 index 0000000..4732bfc --- /dev/null +++ b/minecraft-launcher-server/minecraft-launcher-server.service @@ -0,0 +1,11 @@ +# put this file in /etc/systemd/system/ +[Unit] +Description=minecraft launcher backend in c# + +[Service] +WorkingDirectory=/opt/minecraft-launcher/minecraft-launcher-server/bin/publish +ExecStart=/opt/minecraft-launcher/minecraft-launcher-server/bin/publish/minecraft-launcher-server +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/minecraft-launcher-server/publish.sh b/minecraft-launcher-server/publish.sh new file mode 100644 index 0000000..e024786 --- /dev/null +++ b/minecraft-launcher-server/publish.sh @@ -0,0 +1,10 @@ +dotnet publish -c release -o bin/publish \ + --self-contained \ + --use-current-runtime \ + -p:PublishSingleFile=true \ + -p:PublishTrimmed=true \ + -p:TrimMode=partial \ + -p:EnableCompressionInSingleFile=true \ + -p:OptimizationPreference=Size \ + -p:InvariantGlobalization=true \ + -p:DebugType=none diff --git a/minecraft-launcher.sln b/minecraft-launcher.sln new file mode 100644 index 0000000..2597576 --- /dev/null +++ b/minecraft-launcher.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32104.313 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "launcher-client", "minecraft-launcher-client\launcher-client.csproj", "{49ADEFCE-DA46-4229-997C-3D43DD600627}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "launcher-server", "minecraft-launcher-server\launcher-server.csproj", "{1DC6892C-5DC8-4C1C-94C1-CE695BD2DBC2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49ADEFCE-DA46-4229-997C-3D43DD600627}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49ADEFCE-DA46-4229-997C-3D43DD600627}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49ADEFCE-DA46-4229-997C-3D43DD600627}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49ADEFCE-DA46-4229-997C-3D43DD600627}.Release|Any CPU.Build.0 = Release|Any CPU + {1DC6892C-5DC8-4C1C-94C1-CE695BD2DBC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1DC6892C-5DC8-4C1C-94C1-CE695BD2DBC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1DC6892C-5DC8-4C1C-94C1-CE695BD2DBC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1DC6892C-5DC8-4C1C-94C1-CE695BD2DBC2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5D358070-7ABE-4BD6-9A87-6A5BE8CB6BC9} + EndGlobalSection +EndGlobal