diff --git a/MinecraftClient/ChatBots/WebSocketBot.cs b/MinecraftClient/ChatBots/WebSocketBot.cs index 6ba4395e06..1aa6caa9f0 100644 --- a/MinecraftClient/ChatBots/WebSocketBot.cs +++ b/MinecraftClient/ChatBots/WebSocketBot.cs @@ -287,13 +287,21 @@ public class Configs [TomlInlineComment("$ChatBot.WebSocketBot.DebugMode$")] public bool DebugMode = false; + + [TomlInlineComment("$ChatBot.WebSocketBot.AllowIpAlias$")] + public bool AllowIpAlias = false; } public WebSocketBot() { + _password = Config.Password; + _authenticatedSessions = new(); + _waitingEvents = new(); + var match = Regex.Match(Config.Ip!, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"); - if (!match.Success) + // If AllowIpAlias is set to true in the config, then always ignore this check + if (!match.Success & !Config.AllowIpAlias!) { LogToConsole(Translations.bot_WebSocketBot_failed_to_start_ip); return; @@ -307,9 +315,6 @@ public WebSocketBot() _ip = Config.Ip; _port = Config.Port; - _password = Config.Password; - _authenticatedSessions = new(); - _waitingEvents = new(); } public override void Initialize() @@ -420,6 +425,9 @@ private bool ProcessWebsocketCommand(string sessionId, string password, string m _authenticatedSessions.Add(newId); } + // Update the responder to the new session id + responder = new WsCommandResponder(this, newId, cmd.Command, cmd.RequestId); + responder.SendSuccessResponse( responder.Quote("The session ID was successfully changed to: '" + newId + "'"), true); LogToConsole(string.Format(Translations.bot_WebSocketBot_session_id_changed, sessionId, newId)); diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index f9bd554663..c7296d52a3 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -3438,6 +3438,16 @@ public void OnUpdateScore(string entityname, int action, string objectivename, i { DispatchBotEvent(bot => bot.OnUpdateScore(entityname, action, objectivename, value)); } + + /// + /// Called when the client received the Tab Header and Footer + /// + /// Header + /// Footer + public void OnTabListHeaderAndFooter(string header, string footer) + { + DispatchBotEvent(bot => bot.OnTabListHeaderAndFooter(header, footer)); + } /// /// Called when the health of an entity changed diff --git a/MinecraftClient/Program.cs b/MinecraftClient/Program.cs index 445996bdb0..f0d8f0b864 100644 --- a/MinecraftClient/Program.cs +++ b/MinecraftClient/Program.cs @@ -416,7 +416,7 @@ private static void InitializeClient() else { // Validate cached session or login new session. - if (Config.Main.Advanced.SessionCache != CacheType.none && SessionCache.Contains(loginLower)) + if (Config.Main.Advanced.SessionCache != CacheType.none && SessionCache.Contains(loginLower) && Config.Main.General.AccountType != LoginType.yggdrasil) { session = SessionCache.Get(loginLower); result = ProtocolHandler.GetTokenValidation(session); @@ -447,7 +447,7 @@ private static void InitializeClient() if (result != ProtocolHandler.LoginResult.Success) { - ConsoleIO.WriteLine(string.Format(Translations.mcc_connecting, Config.Main.General.AccountType == LoginType.mojang ? "Minecraft.net" : "Microsoft")); + ConsoleIO.WriteLine(string.Format(Translations.mcc_connecting, Config.Main.General.AccountType == LoginType.mojang ? "Minecraft.net" : (Config.Main.General.AccountType == LoginType.microsoft ? "Microsoft" : Config.Main.General.AuthServer.Host))); result = ProtocolHandler.GetLogin(InternalConfig.Account.Login, InternalConfig.Account.Password, Config.Main.General.AccountType, out session); } @@ -455,7 +455,7 @@ private static void InitializeClient() SessionCache.Store(loginLower, session); if (result == ProtocolHandler.LoginResult.Success) - session.SessionPreCheckTask = Task.Factory.StartNew(() => session.SessionPreCheck()); + session.SessionPreCheckTask = Task.Factory.StartNew(() => session.SessionPreCheck(Config.Main.General.AccountType)); } if (result == ProtocolHandler.LoginResult.Success) @@ -649,6 +649,7 @@ private static void InitializeClient() ProtocolHandler.LoginResult.OtherError => Translations.error_login_network, ProtocolHandler.LoginResult.SSLError => Translations.error_login_ssl, ProtocolHandler.LoginResult.UserCancel => Translations.error_login_cancel, + ProtocolHandler.LoginResult.WrongSelection => Translations.error_login_blocked, _ => Translations.error_login_unknown, #pragma warning restore format // @formatter:on }; diff --git a/MinecraftClient/Protocol/Handlers/Protocol16.cs b/MinecraftClient/Protocol/Handlers/Protocol16.cs index 53813e1402..7910cf56e6 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol16.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol16.cs @@ -15,6 +15,7 @@ using MinecraftClient.Proxy; using MinecraftClient.Scripting; using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; namespace MinecraftClient.Protocol.Handlers { @@ -504,7 +505,7 @@ private bool Handshake(string uuid, string username, string sessionID, string ho else if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_handshake, serverID)); - return StartEncryption(uuid, username, sessionID, token, serverID, PublicServerkey, session); + return StartEncryption(uuid, username, sessionID, Config.Main.General.AccountType, token, serverID, PublicServerkey, session); } else { @@ -513,7 +514,7 @@ private bool Handshake(string uuid, string username, string sessionID, string ho } } - private bool StartEncryption(string uuid, string username, string sessionID, byte[] token, string serverIDhash, byte[] serverPublicKey, SessionToken session) + private bool StartEncryption(string uuid, string username, string sessionID, LoginType type, byte[] token, string serverIDhash, byte[] serverPublicKey, SessionToken session) { RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!; byte[] secretKey = CryptoHandler.ClientAESPrivateKey ?? CryptoHandler.GenerateAESPrivateKey(); @@ -537,7 +538,7 @@ private bool StartEncryption(string uuid, string username, string sessionID, byt if (needCheckSession) { - if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash)) + if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash, type)) { session.ServerIDhash = serverIDhash; session.ServerPublicKey = serverPublicKey; diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index 4c90678447..3c6101939a 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -25,6 +25,7 @@ using MinecraftClient.Proxy; using MinecraftClient.Scripting; using static MinecraftClient.Settings; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; namespace MinecraftClient.Protocol.Handlers { @@ -337,7 +338,7 @@ internal Tuple> ReadNextPacket() { var toDecompress = packetData.ToArray(); var uncompressed = ZlibUtils.Decompress(toDecompress, sizeUncompressed); - packetData = new(uncompressed); + packetData = new Queue(uncompressed); } } @@ -912,8 +913,7 @@ private bool HandlePlayPackets(int packetId, Queue packetData) .StringValue; string? senderTeamName = null; if (targetName != null && - (messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING || - messageTypeEnum == ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING)) + messageTypeEnum is ChatParser.MessageType.TEAM_MSG_COMMAND_INCOMING or ChatParser.MessageType.TEAM_MSG_COMMAND_OUTGOING) senderTeamName = Json.ParseJson(targetName).Properties["with"].DataArray[0] .Properties["text"].StringValue; @@ -1311,7 +1311,7 @@ private bool HandlePlayPackets(int packetId, Queue packetData) else { var chunksContinuous = dataTypes.ReadNextBool(packetData); - if (protocolVersion >= MC_1_16_Version && protocolVersion <= MC_1_16_1_Version) + if (protocolVersion is >= MC_1_16_Version and <= MC_1_16_1_Version) dataTypes.ReadNextBool(packetData); // Ignore old data - 1.16 to 1.16.1 only var chunkMask = protocolVersion >= MC_1_9_Version ? (ushort)dataTypes.ReadNextVarInt(packetData) @@ -2758,7 +2758,8 @@ public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) var serverId = dataTypes.ReadNextString(packetData); var serverPublicKey = dataTypes.ReadNextByteArray(packetData); var token = dataTypes.ReadNextByteArray(packetData); - return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), token, serverId, + return StartEncryption(handler.GetUserUuidStr(), handler.GetSessionID(), + Config.Main.General.AccountType, token, serverId, serverPublicKey, playerKeyPair, session); } @@ -2793,7 +2794,7 @@ public bool Login(PlayerKeyPair? playerKeyPair, SessionToken session) /// Start network encryption. Automatically called by Login() if the server requests encryption. /// /// True if encryption was successful - private bool StartEncryption(string uuid, string sessionID, byte[] token, string serverIDhash, + private bool StartEncryption(string uuid, string sessionID, LoginType type, byte[] token, string serverIDhash, byte[] serverPublicKey, PlayerKeyPair? playerKeyPair, SessionToken session) { var RSAService = CryptoHandler.DecodeRSAPublicKey(serverPublicKey)!; @@ -2818,8 +2819,7 @@ private bool StartEncryption(string uuid, string sessionID, byte[] token, string if (needCheckSession) { var serverHash = CryptoHandler.GetServerHash(serverIDhash, serverPublicKey, secretKey); - - if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash)) + if (ProtocolHandler.SessionCheck(uuid, sessionID, serverHash, type)) { session.ServerIDhash = serverIDhash; session.ServerPublicKey = serverPublicKey; @@ -4142,9 +4142,11 @@ public bool ClickContainerButton(int windowId, int buttonId) { try { - List packet = new(); - packet.Add((byte)windowId); - packet.Add((byte)buttonId); + var packet = new List + { + (byte)windowId, + (byte)buttonId + }; SendPacket(PacketTypesOut.ClickWindowButton, packet); return true; } @@ -4271,32 +4273,27 @@ public bool SendUpdateSign(Location sign, string line1, string line2, string lin public bool UpdateCommandBlock(Location location, string command, CommandBlockMode mode, CommandBlockFlags flags) { - if (protocolVersion <= MC_1_13_Version) + if (protocolVersion > MC_1_13_Version) return false; + + try { - try - { - List packet = new(); - packet.AddRange(dataTypes.GetLocation(location)); - packet.AddRange(dataTypes.GetString(command)); - packet.AddRange(DataTypes.GetVarInt((int)mode)); - packet.Add((byte)flags); - SendPacket(PacketTypesOut.UpdateSign, packet); - return true; - } - catch (SocketException) - { - return false; - } - catch (System.IO.IOException) - { - return false; - } - catch (ObjectDisposedException) - { - return false; - } + List packet = new(); + packet.AddRange(dataTypes.GetLocation(location)); + packet.AddRange(dataTypes.GetString(command)); + packet.AddRange(DataTypes.GetVarInt((int)mode)); + packet.Add((byte)flags); + SendPacket(PacketTypesOut.UpdateSign, packet); + return true; } - else + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) { return false; } @@ -4306,8 +4303,7 @@ public bool SendWindowConfirmation(byte windowID, short actionID, bool accepted) { try { - List packet = new(); - packet.Add(windowID); + var packet = new List() { windowID }; packet.AddRange(dataTypes.GetShort(actionID)); packet.Add(accepted ? (byte)1 : (byte)0); SendPacket(PacketTypesOut.WindowConfirmation, packet); @@ -4330,60 +4326,50 @@ public bool SendWindowConfirmation(byte windowID, short actionID, bool accepted) public bool SelectTrade(int selectedSlot) { // MC 1.13 or greater - if (protocolVersion >= MC_1_13_Version) + if (protocolVersion < MC_1_13_Version) return false; + + try { - try - { - List packet = new(); - packet.AddRange(DataTypes.GetVarInt(selectedSlot)); - SendPacket(PacketTypesOut.SelectTrade, packet); - return true; - } - catch (SocketException) - { - return false; - } - catch (System.IO.IOException) - { - return false; - } - catch (ObjectDisposedException) - { - return false; - } + List packet = new(); + packet.AddRange(DataTypes.GetVarInt(selectedSlot)); + SendPacket(PacketTypesOut.SelectTrade, packet); + return true; } - else + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) { return false; } } - public bool SendSpectate(Guid UUID) + public bool SendSpectate(Guid uuid) { // MC 1.8 or greater - if (protocolVersion >= MC_1_8_Version) + if (protocolVersion < MC_1_8_Version) return false; + + try { - try - { - List packet = new(); - packet.AddRange(DataTypes.GetUUID(UUID)); - SendPacket(PacketTypesOut.Spectate, packet); - return true; - } - catch (SocketException) - { - return false; - } - catch (System.IO.IOException) - { - return false; - } - catch (ObjectDisposedException) - { - return false; - } + List packet = new(); + packet.AddRange(DataTypes.GetUUID(uuid)); + SendPacket(PacketTypesOut.Spectate, packet); + return true; } - else + catch (SocketException) + { + return false; + } + catch (System.IO.IOException) + { + return false; + } + catch (ObjectDisposedException) { return false; } @@ -4462,7 +4448,7 @@ private byte[] GenerateSalt() return salt; } - public static long GetNanos() + private static long GetNanos() { var nano = 10000L * Stopwatch.GetTimestamp(); nano /= TimeSpan.TicksPerMillisecond; diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index e2a5be5cf0..dcc991a523 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -447,6 +447,13 @@ public interface IMinecraftComHandler /// he score to be displayed next to the entry. Only sent when Action does not equal 1. void OnUpdateScore(string entityname, int action, string objectivename, int value); + /// + /// Called when the client received the Tab Header and Footer + /// + /// Header + /// Footer + void OnTabListHeaderAndFooter(string header, string footer); + /// /// Called when tradeList is received from server /// diff --git a/MinecraftClient/Protocol/ProtocolHandler.cs b/MinecraftClient/Protocol/ProtocolHandler.cs index 86fd8ce704..92e53ce25d 100644 --- a/MinecraftClient/Protocol/ProtocolHandler.cs +++ b/MinecraftClient/Protocol/ProtocolHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data.Odbc; using System.Globalization; using System.Linq; using System.Net.Security; @@ -11,6 +12,7 @@ using MinecraftClient.Protocol.Handlers.Forge; using MinecraftClient.Protocol.Session; using MinecraftClient.Proxy; +using static MinecraftClient.Protocol.Microsoft; using static MinecraftClient.Settings; using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; @@ -41,39 +43,32 @@ public static bool MinecraftServiceLookup(ref string domain, ref ushort port) if (!String.IsNullOrEmpty(domain) && domain.Any(c => char.IsLetter(c))) { AutoTimeout.Perform(() => + { + try { - try - { - ConsoleIO.WriteLine(string.Format(Translations.mcc_resolve, domainVal)); - var lookupClient = new LookupClient(); - var response = - lookupClient.Query(new DnsQuestion($"_minecraft._tcp.{domainVal}", QueryType.SRV)); - if (response.HasError != true && response.Answers.SrvRecords().Any()) - { - //Order SRV records by priority and weight, then randomly - var result = response.Answers.SrvRecords() - .OrderBy(record => record.Priority) - .ThenByDescending(record => record.Weight) - .ThenBy(record => Guid.NewGuid()) - .First(); - string target = result.Target.Value.Trim('.'); - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_found, target, - result.Port, domainVal)); - domainVal = target; - portVal = result.Port; - foundService = true; - } - } - catch (Exception e) + ConsoleIO.WriteLine(string.Format(Translations.mcc_resolve, domainVal)); + var lookupClient = new LookupClient(); + var response = lookupClient.Query(new DnsQuestion($"_minecraft._tcp.{domainVal}", QueryType.SRV)); + if (response.HasError != true && response.Answers.SrvRecords().Any()) { - ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_not_found, domainVal, - e.GetType().FullName, e.Message)); + //Order SRV records by priority and weight, then randomly + var result = response.Answers.SrvRecords() + .OrderBy(record => record.Priority) + .ThenByDescending(record => record.Weight) + .ThenBy(record => Guid.NewGuid()) + .First(); + string target = result.Target.Value.Trim('.'); + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_found, target, result.Port, domainVal)); + domainVal = target; + portVal = result.Port; + foundService = true; } - }, - TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == - MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast - ? 10 - : 30)); + } + catch (Exception e) + { + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.mcc_not_found, domainVal, e.GetType().FullName, e.Message)); + } + }, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30)); } domain = domainVal; @@ -88,34 +83,28 @@ public static bool MinecraftServiceLookup(ref string domain, ref ushort port) /// Server Port to ping /// Will contain protocol version, if ping successful /// TRUE if ping was successful - public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, - ref ForgeInfo? forgeInfo) + public static bool GetServerInfo(string serverIP, ushort serverPort, ref int protocolversion, ref ForgeInfo? forgeInfo) { bool success = false; int protocolversionTmp = 0; ForgeInfo? forgeInfoTmp = null; if (AutoTimeout.Perform(() => + { + try + { + if (Protocol18Handler.DoPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp) + || Protocol16Handler.DoPing(serverIP, serverPort, ref protocolversionTmp)) { - try - { - if (Protocol18Handler.DoPing(serverIP, serverPort, ref protocolversionTmp, ref forgeInfoTmp) - || Protocol16Handler.DoPing(serverIP, serverPort, ref protocolversionTmp)) - { - success = true; - } - else - ConsoleIO.WriteLineFormatted("§8" + Translations.error_unexpect_response, - acceptnewlines: true); - } - catch (Exception e) - { - ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); - } - }, - TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == - MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast - ? 10 - : 30))) + success = true; + } + else + ConsoleIO.WriteLineFormatted("§8" + Translations.error_unexpect_response, acceptnewlines: true); + } + catch (Exception e) + { + ConsoleIO.WriteLineFormatted(String.Format("§8{0}: {1}", e.GetType().FullName, e.Message)); + } + }, TimeSpan.FromSeconds(Config.Main.Advanced.ResolveSrvRecords == MainConfigHealper.MainConfig.AdvancedConfig.ResolveSrvRecordType.fast ? 10 : 30))) { if (protocolversion != 0 && protocolversion != protocolversionTmp) ConsoleIO.WriteLineFormatted("§8" + Translations.error_version_different, acceptnewlines: true); @@ -140,19 +129,14 @@ public static bool GetServerInfo(string serverIP, ushort serverPort, ref int pro /// Protocol version to handle /// Handler with the appropriate callbacks /// - public static IMinecraftCom GetProtocolHandler(TcpClient Client, int ProtocolVersion, ForgeInfo? forgeInfo, - IMinecraftComHandler Handler) + public static IMinecraftCom GetProtocolHandler(TcpClient Client, int ProtocolVersion, ForgeInfo? forgeInfo, IMinecraftComHandler Handler) { int[] supportedVersions_Protocol16 = { 51, 60, 61, 72, 73, 74, 78 }; if (Array.IndexOf(supportedVersions_Protocol16, ProtocolVersion) > -1) return new Protocol16Handler(Client, ProtocolVersion, Handler); - int[] supportedVersions_Protocol18 = - { - 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, - 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764 - }; + int[] supportedVersions_Protocol18 = { 4, 5, 47, 107, 108, 109, 110, 210, 315, 316, 335, 338, 340, 393, 401, 404, 477, 480, 485, 490, 498, 573, 575, 578, 735, 736, 751, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764}; if (Array.IndexOf(supportedVersions_Protocol18, ProtocolVersion) > -1) return new Protocol18Handler(Client, ProtocolVersion, Handler, forgeInfo); @@ -444,27 +428,8 @@ public static ForgeInfo ProtocolForceForge(int protocol) return Protocol18Forge.ServerForceForge(protocol); } - public enum LoginResult - { - OtherError, - ServiceUnavailable, - SSLError, - Success, - WrongPassword, - AccountMigrated, - NotPremium, - LoginRequired, - InvalidToken, - InvalidResponse, - NullError, - UserCancel - }; - - public enum AccountType - { - Mojang, - Microsoft - }; + public enum LoginResult { OtherError, ServiceUnavailable, SSLError, Success, WrongPassword, AccountMigrated, NotPremium, LoginRequired, InvalidToken, InvalidResponse, NullError, UserCancel, WrongSelection }; + public enum AccountType { Mojang, Microsoft }; /// /// Allows to login to a premium Minecraft account using the Yggdrasil authentication scheme. @@ -486,7 +451,11 @@ public static LoginResult GetLogin(string user, string pass, LoginType type, out { return MojangLogin(user, pass, out session); } - else throw new InvalidOperationException("Account type must be Mojang or Microsoft"); + else if (type == LoginType.yggdrasil) + { + return YggdrasiLogin(user, pass, out session); + } + else throw new InvalidOperationException("Account type must be Mojang or Microsoft or valid authlib 3rd Servers!"); } /// @@ -503,10 +472,8 @@ private static LoginResult MojangLogin(string user, string pass, out SessionToke try { string result = ""; - string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + - JsonEncode(user) + "\", \"password\": \"" + JsonEncode(pass) + - "\", \"clientToken\": \"" + JsonEncode(session.ClientID) + "\" }"; - int code = DoHTTPSPost("authserver.mojang.com", "/authenticate", json_request, ref result); + string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + JsonEncode(user) + "\", \"password\": \"" + JsonEncode(pass) + "\", \"clientToken\": \"" + JsonEncode(session.ClientID) + "\" }"; + int code = DoHTTPSPost("authserver.mojang.com",443, "/authenticate", json_request, ref result); if (code == 200) { if (result.Contains("availableProfiles\":[]}")) @@ -523,8 +490,7 @@ private static LoginResult MojangLogin(string user, string pass, out SessionToke { session.ID = loginResponse.Properties["accessToken"].StringValue; session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; - session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"] - .StringValue; + session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"].StringValue; return LoginResult.Success; } else return LoginResult.InvalidResponse; @@ -554,7 +520,6 @@ private static LoginResult MojangLogin(string user, string pass, out SessionToke { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } - return LoginResult.SSLError; } catch (System.IO.IOException e) @@ -563,7 +528,6 @@ private static LoginResult MojangLogin(string user, string pass, out SessionToke { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } - if (e.Message.Contains("authentication")) { return LoginResult.SSLError; @@ -576,11 +540,119 @@ private static LoginResult MojangLogin(string user, string pass, out SessionToke { ConsoleIO.WriteLineFormatted("§8" + e.ToString()); } - return LoginResult.OtherError; } } + private static LoginResult YggdrasiLogin(string user, string pass, out SessionToken session) + { + session = new SessionToken() { ClientID = Guid.NewGuid().ToString().Replace("-", "") }; + try + { + string result = ""; + string json_request = "{\"agent\": { \"name\": \"Minecraft\", \"version\": 1 }, \"username\": \"" + JsonEncode(user) + "\", \"password\": \"" + JsonEncode(pass) + "\", \"clientToken\": \"" + JsonEncode(session.ClientID) + "\" }"; + int code = DoHTTPSPost(Config.Main.General.AuthServer.Host,Config.Main.General.AuthServer.Port, "/api/yggdrasil/authserver/authenticate", json_request, ref result); + if (code == 200) + { + if (result.Contains("availableProfiles\":[]}")) + { + return LoginResult.NotPremium; + } + else + { + Json.JSONData loginResponse = Json.ParseJson(result); + if (loginResponse.Properties.ContainsKey("accessToken")) + { + session.ID = loginResponse.Properties["accessToken"].StringValue; + if (loginResponse.Properties.ContainsKey("selectedProfile") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + { + session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; + session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"].StringValue; + return LoginResult.Success; + } + else + { + string availableProfiles = ""; + foreach (Json.JSONData profile in loginResponse.Properties["availableProfiles"].DataArray) + { + availableProfiles += " " + profile.Properties["name"].StringValue; + } + ConsoleIO.WriteLine(Translations.mcc_avaliable_profiles + availableProfiles); + + ConsoleIO.WriteLine(Translations.mcc_select_profile); + string selectedProfileName = ConsoleIO.ReadLine(); + ConsoleIO.WriteLine(Translations.mcc_selected_profile + " " + selectedProfileName); + Json.JSONData? selectedProfile = null; + foreach (Json.JSONData profile in loginResponse.Properties["availableProfiles"].DataArray) + { + selectedProfile = profile.Properties["name"].StringValue == selectedProfileName ? profile : selectedProfile; + } + + if (selectedProfile != null) + { + session.PlayerID = selectedProfile.Properties["id"].StringValue; + session.PlayerName = selectedProfile.Properties["name"].StringValue; + SessionToken currentsession = session; + return GetNewYggdrasilToken(currentsession, out session); + } + else + { + return LoginResult.WrongSelection; + } + } + } + else return LoginResult.InvalidResponse; + } + } + else if (code == 403) + { + if (result.Contains("UserMigratedException")) + { + return LoginResult.AccountMigrated; + } + else return LoginResult.WrongPassword; + } + else if (code == 503) + { + return LoginResult.ServiceUnavailable; + } + else + { + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.error_http_code, code)); + return LoginResult.OtherError; + } + } + catch (System.Security.Authentication.AuthenticationException e) + { + if (Settings.Config.Logging.DebugMessages) + { + ConsoleIO.WriteLineFormatted("§8" + e.ToString()); + } + return LoginResult.SSLError; + } + catch (System.IO.IOException e) + { + if (Settings.Config.Logging.DebugMessages) + { + ConsoleIO.WriteLineFormatted("§8" + e.ToString()); + } + if (e.Message.Contains("authentication")) + { + return LoginResult.SSLError; + } + else return LoginResult.OtherError; + } + catch (Exception e) + { + if (Settings.Config.Logging.DebugMessages) + { + ConsoleIO.WriteLineFormatted("§8" + e.ToString()); + } + return LoginResult.OtherError; + } + } /// /// Sign-in to Microsoft Account without using browser. Only works if 2FA is disabled. /// Might not work well in some rare cases. @@ -606,7 +678,6 @@ private static LoginResult MicrosoftMCCLogin(string email, string password, out { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } - return LoginResult.WrongPassword; // Might not always be wrong password } } @@ -677,7 +748,6 @@ private static LoginResult MicrosoftLogin(Microsoft.LoginResponse msaResponse, o { ConsoleIO.WriteLineFormatted("§c" + e.StackTrace); } - return LoginResult.WrongPassword; // Might not always be wrong password } } @@ -691,15 +761,13 @@ public static LoginResult GetTokenValidation(SessionToken session) { var payload = JwtPayloadDecode.GetPayload(session.ID); var json = Json.ParseJson(payload); - var expTimestamp = long.Parse(json.Properties["exp"].StringValue, NumberStyles.Any, - CultureInfo.CurrentCulture); + var expTimestamp = long.Parse(json.Properties["exp"].StringValue, NumberStyles.Any, CultureInfo.CurrentCulture); var now = DateTime.Now; var tokenExp = UnixTimeStampToDateTime(expTimestamp); if (Settings.Config.Logging.DebugMessages) { ConsoleIO.WriteLine("Access token expiration time is " + tokenExp.ToString()); } - if (now < tokenExp) { // Still valid @@ -724,11 +792,8 @@ public static LoginResult GetNewToken(SessionToken currentsession, out SessionTo try { string result = ""; - string json_request = "{ \"accessToken\": \"" + JsonEncode(currentsession.ID) + - "\", \"clientToken\": \"" + JsonEncode(currentsession.ClientID) + - "\", \"selectedProfile\": { \"id\": \"" + JsonEncode(currentsession.PlayerID) + - "\", \"name\": \"" + JsonEncode(currentsession.PlayerName) + "\" } }"; - int code = DoHTTPSPost("authserver.mojang.com", "/refresh", json_request, ref result); + string json_request = "{ \"accessToken\": \"" + JsonEncode(currentsession.ID) + "\", \"clientToken\": \"" + JsonEncode(currentsession.ClientID) + "\", \"selectedProfile\": { \"id\": \"" + JsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + JsonEncode(currentsession.PlayerName) + "\" } }"; + int code = DoHTTPSPost("authserver.mojang.com",443, "/refresh", json_request, ref result); if (code == 200) { if (result == null) @@ -745,8 +810,53 @@ public static LoginResult GetNewToken(SessionToken currentsession, out SessionTo { session.ID = loginResponse.Properties["accessToken"].StringValue; session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; - session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"] - .StringValue; + session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"].StringValue; + return LoginResult.Success; + } + else return LoginResult.InvalidResponse; + } + } + else if (code == 403 && result.Contains("InvalidToken")) + { + return LoginResult.InvalidToken; + } + else + { + ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.error_auth, code)); + return LoginResult.OtherError; + } + } + catch + { + return LoginResult.OtherError; + } + } + + public static LoginResult GetNewYggdrasilToken(SessionToken currentsession, out SessionToken session) + { + session = new SessionToken(); + try + { + string result = ""; + string json_request = "{ \"accessToken\": \"" + JsonEncode(currentsession.ID) + "\", \"clientToken\": \"" + JsonEncode(currentsession.ClientID) + "\", \"selectedProfile\": { \"id\": \"" + JsonEncode(currentsession.PlayerID) + "\", \"name\": \"" + JsonEncode(currentsession.PlayerName) + "\" } }"; + int code = DoHTTPSPost(Config.Main.General.AuthServer.Host, Config.Main.General.AuthServer.Port, "/api/yggdrasil/authserver/refresh", json_request, ref result); + if (code == 200) + { + if (result == null) + { + return LoginResult.NullError; + } + else + { + Json.JSONData loginResponse = Json.ParseJson(result); + if (loginResponse.Properties.ContainsKey("accessToken") + && loginResponse.Properties.ContainsKey("selectedProfile") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("id") + && loginResponse.Properties["selectedProfile"].Properties.ContainsKey("name")) + { + session.ID = loginResponse.Properties["accessToken"].StringValue; + session.PlayerID = loginResponse.Properties["selectedProfile"].Properties["id"].StringValue; + session.PlayerName = loginResponse.Properties["selectedProfile"].Properties["name"].StringValue; return LoginResult.Success; } else return LoginResult.InvalidResponse; @@ -774,21 +884,22 @@ public static LoginResult GetNewToken(SessionToken currentsession, out SessionTo /// Username /// Session ID /// Server ID + /// LoginType /// TRUE if session was successfully checked - public static bool SessionCheck(string uuid, string accesstoken, string serverhash) + public static bool SessionCheck(string uuid, string accesstoken, string serverhash, LoginType type) { try { string result = ""; - string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + - "\",\"serverId\":\"" + serverhash + "\"}"; - int code = DoHTTPSPost("sessionserver.mojang.com", "/session/minecraft/join", json_request, ref result); + string json_request = "{\"accessToken\":\"" + accesstoken + "\",\"selectedProfile\":\"" + uuid + "\",\"serverId\":\"" + serverhash + "\"}"; + string host = type == LoginType.yggdrasil ? Config.Main.General.AuthServer.Host : "sessionserver.mojang.com"; + int port = type == LoginType.yggdrasil ? Config.Main.General.AuthServer.Port : 443; + string endpoint = type == LoginType.yggdrasil ? "/api/yggdrasil/sessionserver/session/minecraft/join" : "/session/minecraft/join"; + + int code = DoHTTPSPost(host, port, endpoint, json_request, ref result); return (code >= 200 && code < 300); } - catch - { - return false; - } + catch { return false; } } /// @@ -804,9 +915,8 @@ public static List RealmsListWorlds(string username, string uuid, string try { string result = ""; - string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, - Program.MCHighestVersion); - DoHTTPSGet("pc.realms.minecraft.net", "/worlds", cookies, ref result); + string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, Program.MCHighestVersion); + DoHTTPSGet("pc.realms.minecraft.net", 443,"/worlds", cookies, ref result); Json.JSONData realmsWorlds = Json.ParseJson(result); if (realmsWorlds.Properties.ContainsKey("servers") && realmsWorlds.Properties["servers"].Type == Json.JSONData.DataType.Array @@ -832,7 +942,6 @@ public static List RealmsListWorlds(string username, string uuid, string } } } - if (availableWorlds.Count > 0) { ConsoleIO.WriteLine(Translations.mcc_realms_available); @@ -841,6 +950,7 @@ public static List RealmsListWorlds(string username, string uuid, string ConsoleIO.WriteLine(Translations.mcc_realms_join); } } + } catch (Exception e) { @@ -850,7 +960,6 @@ public static List RealmsListWorlds(string username, string uuid, string ConsoleIO.WriteLineFormatted("§8" + e.StackTrace); } } - return realmsWorldsResult; } @@ -862,16 +971,13 @@ public static List RealmsListWorlds(string username, string uuid, string /// Player UUID /// Access token /// Server address (host:port) or empty string if failure - public static string GetRealmsWorldServerAddress(string worldId, string username, string uuid, - string accesstoken) + public static string GetRealmsWorldServerAddress(string worldId, string username, string uuid, string accesstoken) { try { string result = ""; - string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, - Program.MCHighestVersion); - int statusCode = DoHTTPSGet("pc.realms.minecraft.net", "/worlds/v1/" + worldId + "/join/pc", cookies, - ref result); + string cookies = String.Format("sid=token:{0}:{1};user={2};version={3}", accesstoken, uuid, username, Program.MCHighestVersion); + int statusCode = DoHTTPSGet("pc.realms.minecraft.net",443, "/worlds/v1/" + worldId + "/join/pc", cookies, ref result); if (statusCode == 200) { Json.JSONData serverAddress = Json.ParseJson(result); @@ -896,7 +1002,6 @@ public static string GetRealmsWorldServerAddress(string worldId, string username { ConsoleIO.WriteLineFormatted("§8" + e.StackTrace); } - return ""; } } @@ -909,7 +1014,7 @@ public static string GetRealmsWorldServerAddress(string worldId, string username /// Cookies for making the request /// Request result /// HTTP Status code - private static int DoHTTPSGet(string host, string endpoint, string cookies, ref string result) + private static int DoHTTPSGet(string host,int port, string endpoint, string cookies, ref string result) { List http_request = new() { @@ -924,7 +1029,7 @@ private static int DoHTTPSGet(string host, string endpoint, string cookies, ref "", "" }; - return DoHTTPSRequest(http_request, host, ref result); + return DoHTTPSRequest(http_request, host,port, ref result); } /// @@ -935,7 +1040,7 @@ private static int DoHTTPSGet(string host, string endpoint, string cookies, ref /// Request payload /// Request result /// HTTP Status code - private static int DoHTTPSPost(string host, string endpoint, string request, ref string result) + private static int DoHTTPSPost(string host, int port, string endpoint, string request, ref string result) { List http_request = new() { @@ -948,7 +1053,7 @@ private static int DoHTTPSPost(string host, string endpoint, string request, ref "", request }; - return DoHTTPSRequest(http_request, host, ref result); + return DoHTTPSRequest(http_request, host,port, ref result); } /// @@ -959,7 +1064,7 @@ private static int DoHTTPSPost(string host, string endpoint, string request, ref /// Host to connect to /// Request result /// HTTP Status code - private static int DoHTTPSRequest(List headers, string host, ref string result) + private static int DoHTTPSRequest(List headers, string host,int port, ref string result) { string? postResult = null; int statusCode = 520; @@ -971,10 +1076,9 @@ private static int DoHTTPSRequest(List headers, string host, ref string if (Settings.Config.Logging.DebugMessages) ConsoleIO.WriteLineFormatted("§8" + string.Format(Translations.debug_request, host)); - TcpClient client = ProxyHandler.NewTcpClient(host, 443, true); + TcpClient client = ProxyHandler.NewTcpClient(host, port, true); SslStream stream = new(client.GetStream()); - stream.AuthenticateAsClient(host, null, SslProtocols.Tls12, - true); // Enable TLS 1.2. Hotfix for #1780 + stream.AuthenticateAsClient(host, null, SslProtocols.Tls12, true); // Enable TLS 1.2. Hotfix for #1780 if (Settings.Config.Logging.DebugMessages) foreach (string line in headers) @@ -993,8 +1097,15 @@ private static int DoHTTPSRequest(List headers, string host, ref string if (raw_result.StartsWith("HTTP/1.1")) { - postResult = raw_result[(raw_result.IndexOf("\r\n\r\n") + 4)..]; statusCode = int.Parse(raw_result.Split(' ')[1], NumberStyles.Any, CultureInfo.CurrentCulture); + if (statusCode != 204) + { + postResult = raw_result[(raw_result.IndexOf("\r\n\r\n") + 4)..].Split("\r\n")[1]; + } + else + { + postResult = "No Content"; + } } else statusCode = 520; //Web server is returning an unknown error } @@ -1053,4 +1164,4 @@ public static DateTime UnixTimeStampToDateTime(long unixTimeStamp) return dateTime; } } -} \ No newline at end of file +} diff --git a/MinecraftClient/Protocol/Session/SessionToken.cs b/MinecraftClient/Protocol/Session/SessionToken.cs index c3c0aee921..1df31146ac 100644 --- a/MinecraftClient/Protocol/Session/SessionToken.cs +++ b/MinecraftClient/Protocol/Session/SessionToken.cs @@ -3,6 +3,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using MinecraftClient.Scripting; +using static MinecraftClient.Settings.MainConfigHealper.MainConfig.GeneralConfig; namespace MinecraftClient.Protocol.Session { @@ -32,13 +33,13 @@ public SessionToken() ServerPublicKey = null; } - public bool SessionPreCheck() + public bool SessionPreCheck(LoginType type) { if (ID == string.Empty || PlayerID == String.Empty || ServerPublicKey == null) return false; Crypto.CryptoHandler.ClientAESPrivateKey ??= Crypto.CryptoHandler.GenerateAESPrivateKey(); string serverHash = Crypto.CryptoHandler.GetServerHash(ServerIDhash, ServerPublicKey, Crypto.CryptoHandler.ClientAESPrivateKey); - if (ProtocolHandler.SessionCheck(PlayerID, ID, serverHash)) + if (ProtocolHandler.SessionCheck(PlayerID, ID, serverHash, type)) return true; return false; } diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index eec9c25dd5..762b4d2e9e 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -799,7 +799,8 @@ internal static string ChatBot_Farmer_Delay_Between_Tasks { ///NOTE: This is an experimental feature, the bot can be slow at times, you need to walk with a normal speed and to sometimes stop for it to be able to keep up with you ///It's similar to making animals follow you when you're holding food in your hand. ///This is due to a slow pathfinding algorithm, we're working on getting a better one - ///You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite, /// [rest of string was truncated]";. + ///You can tweak the update limit and find what works best for you. (NOTE: Do not but a very low one, because you might achieve the opposite, + /// [rest of string was truncated]";. /// internal static string ChatBot_FollowPlayer { get { @@ -1128,6 +1129,15 @@ internal static string ChatBot_WebSocketBot { } } + /// + /// Looks up a localized string similar to Allow IP aliases, such as "localhost" or if using containers then the container name can be used.... + /// + internal static string ChatBot_WebSocketBot_AllowIpAlias { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.AllowIpAlias", resourceCulture); + } + } + /// /// Looks up a localized string similar to This setting is for developers who are developing a library that uses this chat bot to remotely execute procedures/commands/functions.. /// @@ -1722,6 +1732,15 @@ internal static string Main_General_account { } } + /// + /// Looks up a localized string similar to Yggdrasil authlib server domain name and port.. + /// + internal static string Main_General_AuthlibServer { + get { + return ResourceManager.GetString("Main.General.AuthlibServer", resourceCulture); + } + } + /// /// Looks up a localized string similar to The address of the game server, "Host" can be filled in with domain name or IP address. (The "Port" field can be deleted, it will be resolved automatically). /// @@ -1741,7 +1760,7 @@ internal static string Main_General_method { } /// - /// Looks up a localized string similar to Account type: "mojang" OR "microsoft". Also affects interactive login in console.. + /// Looks up a localized string similar to Account type: "mojang" OR "microsoft" OR "yggdrasil". Also affects interactive login in console.. /// internal static string Main_General_server_info { get { @@ -1986,4 +2005,4 @@ internal static string Signature_SignMessageInCommand { } } } -} +} \ No newline at end of file diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index 8ad6001f40..7e9e3b09e9 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -724,7 +724,7 @@ Usage examples: "/tell <mybot> connect Server1", "/connect Server2"Microsoft Account sign-in method: "mcc" OR "browser". If the login always fails, please try to use the "browser" once. - Account type: "mojang" OR "microsoft". Also affects interactive login in console. + Account type: "mojang" OR "microsoft" OR "yggdrasil". Also affects interactive login in console. Settings below are sent to the server and only affect server-side things like your skin. @@ -843,7 +843,13 @@ If the connection to the Minecraft game server is blocked by the firewall, set E This setting is for developers who are developing a library that uses this chat bot to remotely execute procedures/commands/functions. + + Allow IP aliases, such as "localhost" or if using containers then the container name can be used... + Ignore invalid player name + + Yggdrasil authlib server domain name and port. + \ No newline at end of file diff --git a/MinecraftClient/Resources/Translations/Translations.Designer.cs b/MinecraftClient/Resources/Translations/Translations.Designer.cs index 08a5b472a2..7d4cd17b4f 100644 --- a/MinecraftClient/Resources/Translations/Translations.Designer.cs +++ b/MinecraftClient/Resources/Translations/Translations.Designer.cs @@ -2450,6 +2450,15 @@ internal static string chatbot_reconnect { return ResourceManager.GetString("chatbot.reconnect", resourceCulture); } } + + /// + /// Looks up a localized string similar to . + /// + internal static string ChatBot_WebSocketBot_AllowIpAlias { + get { + return ResourceManager.GetString("ChatBot.WebSocketBot.AllowIpAlias", resourceCulture); + } + } /// /// Looks up a localized string similar to . @@ -5373,6 +5382,15 @@ internal static string icmd_unknown { } } + /// + /// Looks up a localized string similar to Avaliable profiles:. + /// + internal static string mcc_avaliable_profiles { + get { + return ResourceManager.GetString("mcc.avaliable_profiles", resourceCulture); + } + } + /// /// Looks up a localized string similar to The old MinecraftClient.ini has been backed up as {0}. /// @@ -5746,6 +5764,24 @@ internal static string mcc_run_with_default_settings { } } + /// + /// Looks up a localized string similar to Select a profile from available profiles:. + /// + internal static string mcc_select_profile { + get { + return ResourceManager.GetString("mcc.select_profile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Selected profile:. + /// + internal static string mcc_selected_profile { + get { + return ResourceManager.GetString("mcc.selected_profile", resourceCulture); + } + } + /// /// Looks up a localized string similar to Server is in offline mode.. /// diff --git a/MinecraftClient/Resources/Translations/Translations.resx b/MinecraftClient/Resources/Translations/Translations.resx index 414b08cdd6..abc5c08be3 100644 --- a/MinecraftClient/Resources/Translations/Translations.resx +++ b/MinecraftClient/Resources/Translations/Translations.resx @@ -2121,4 +2121,13 @@ Logging in... Shift right-clicking slot {0} in window #{1} + + Avaliable profiles: + + + Selected profile: + + + Select a profile from available profiles: + \ No newline at end of file diff --git a/MinecraftClient/Resources/en_us.json b/MinecraftClient/Resources/en_us.json index f02b7bf1d9..72aa260c24 100644 --- a/MinecraftClient/Resources/en_us.json +++ b/MinecraftClient/Resources/en_us.json @@ -4632,6 +4632,8 @@ "advancements.adventure.avoid_vibration.description": "Sneak near a Sculk Sensor or Warden to prevent it from detecting you", "advancements.adventure.bullseye.title": "Bullseye", "advancements.adventure.bullseye.description": "Hit the bullseye of a Target block from at least 30 meters away", + "advancements.adventure.craft_decorated_pot_using_only_sherds.title": "Careful Restoration", + "advancements.adventure.craft_decorated_pot_using_only_sherds.description": "Make a Decorated Pot out of 4 Pottery Sherds", "advancements.adventure.fall_from_world_height.title": "Caves & Cliffs", "advancements.adventure.fall_from_world_height.description": "Free fall from the top of the world (build limit) to the bottom of the world and survive", "advancements.adventure.kill_mob_near_sculk_catalyst.title": "It Spreads", @@ -4660,6 +4662,10 @@ "advancements.adventure.play_jukebox_in_meadows.description": "Make the Meadows come alive with the sound of music from a Jukebox", "advancements.adventure.root.title": "Adventure", "advancements.adventure.root.description": "Adventure, exploration and combat", + "advancements.adventure.read_power_of_chiseled_bookshelf.title": "The Power of Books", + "advancements.adventure.read_power_of_chiseled_bookshelf.description": "Read the power signal of a Chiseled Bookshelf using a Comparator", + "advancements.adventure.salvage_sherd.title": "Respecting the Remnants", + "advancements.adventure.salvage_sherd.description": "Brush a Suspicious block to obtain a Pottery Sherd", "advancements.adventure.shoot_arrow.title": "Take Aim", "advancements.adventure.shoot_arrow.description": "Shoot something with an Arrow", "advancements.adventure.sleep_in_bed.title": "Sweet Dreams", @@ -4674,6 +4680,10 @@ "advancements.adventure.trade.description": "Successfully trade with a Villager", "advancements.adventure.trade_at_world_height.title": "Star Trader", "advancements.adventure.trade_at_world_height.description": "Trade with a Villager at the build height limit", + "advancements.adventure.trim_with_all_exclusive_armor_patterns.title": "Smithing with Style", + "advancements.adventure.trim_with_all_exclusive_armor_patterns.description": "Apply these smithing templates at least once: Spire, Snout, Rib, Ward, Silence, Vex, Tide, Wayfinder", + "advancements.adventure.trim_with_any_armor_pattern.title": "Crafting a New Look", + "advancements.adventure.trim_with_any_armor_pattern.description": "Craft a trimmed armor at a Smithing Table", "advancements.adventure.throw_trident.title": "A Throwaway Joke", "advancements.adventure.throw_trident.description": "Throw a Trident at something.\nNote: Throwing away your only weapon is not a good idea.", "advancements.adventure.two_birds_one_arrow.title": "Two Birds, One Arrow", @@ -4730,6 +4740,12 @@ "advancements.husbandry.allay_deliver_item_to_player.description": "Have an Allay deliver items to you", "advancements.husbandry.allay_deliver_cake_to_note_block.title": "Birthday Song", "advancements.husbandry.allay_deliver_cake_to_note_block.description": "Have an Allay drop a Cake at a Note Block", + "advancements.husbandry.obtain_sniffer_egg.title": "Smells Interesting", + "advancements.husbandry.obtain_sniffer_egg.description": "Obtain a Sniffer Egg", + "advancements.husbandry.plant_any_sniffer_seed.title": "Planting the Past", + "advancements.husbandry.plant_any_sniffer_seed.description": "Plant any Sniffer seed", + "advancements.husbandry.obtain_netherite_hoe.title": "Serious Dedication", + "advancements.husbandry.obtain_netherite_hoe.description": "Use a Netherite Ingot to upgrade a Hoe, and then reevaluate your life choices", "advancements.end.dragon_breath.title": "You Need a Mint", "advancements.end.dragon_breath.description": "Collect Dragon's Breath in a Glass Bottle", "advancements.end.dragon_egg.title": "The Next Generation", diff --git a/MinecraftClient/Scripting/ChatBot.cs b/MinecraftClient/Scripting/ChatBot.cs index af5b5a430e..36c365bee4 100644 --- a/MinecraftClient/Scripting/ChatBot.cs +++ b/MinecraftClient/Scripting/ChatBot.cs @@ -347,6 +347,13 @@ public virtual void OnScoreboardObjective(string objectivename, byte mode, strin /// The score to be displayed next to the entry. Only sent when Action does not equal 1. public virtual void OnUpdateScore(string entityname, int action, string objectivename, int value) { } + /// + /// Called when the client received the Tab Header and Footer + /// + /// Header + /// Footer + public virtual void OnTabListHeaderAndFooter(string header, string footer) { } + /// /// Called when an inventory/container was updated by server /// diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index c0d510282b..7982b057fe 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -494,8 +494,11 @@ public class GeneralConfig [TomlInlineComment("$Main.General.method$")] public LoginMethod Method = LoginMethod.mcc; + [TomlInlineComment("$Main.General.AuthlibServer$")] + public AuthlibServer AuthServer = new(string.Empty); + - public enum LoginType { mojang, microsoft }; + public enum LoginType { mojang, microsoft,yggdrasil }; public enum LoginMethod { mcc, browser }; } @@ -688,6 +691,29 @@ public ServerInfoConfig(string Host, ushort Port) this.Port = Port; } } + public struct AuthlibServer + { + public string Host = string.Empty; + public int Port = 443; + + public AuthlibServer(string Host) + { + string[] sip = Host.Split(new[] { ":", ":" }, StringSplitOptions.None); + this.Host = sip[0]; + + if (sip.Length > 1) + { + try { this.Port = Convert.ToUInt16(sip[1]); } + catch (FormatException) { } + } + } + + public AuthlibServer(string Host, ushort Port) + { + this.Host = Host.Split(new[] { ":", ":" }, StringSplitOptions.None)[0]; + this.Port = Port; + } + } } }