diff --git a/App.cs b/App.cs index 8d66076..ca31aa2 100644 --- a/App.cs +++ b/App.cs @@ -1,17 +1,24 @@ -using LeaguePatchCollection; +using EmbedIO; +using LeaguePatchCollection; +using System; +using System.IO.Compression; +using System.Text; using System.Text.Json; using System.Text.Json.Nodes; class App { - static (string, object)[] PublicConfigValues = { + static (string, object)[] VanguardConfigFlags = { ("anticheat.vanguard.backgroundInstall", false), ("anticheat.vanguard.enabled", false), ("keystone.client.feature_flags.restart_required.disabled", true), ("keystone.client.feature_flags.vanguardLaunch.disabled", true), ("lol.client_settings.vanguard.enabled", false), ("lol.client_settings.vanguard.enabled_mac", false), - ("lol.client_settings.vanguard.url", ""), + ("lol.client_settings.vanguard.url", "") + }; + + static (string, object)[] OptimizeClientConfigPublic = { ("keystone.age_restriction.enabled", false), ("keystone.client.feature_flags.lifecycle.backgroundRunning.enabled", false), ("keystone.client.feature_flags.arcane_event.enabled", false), @@ -47,6 +54,7 @@ class App ("keystone.client_config.diagnostics_enabled", false), ("games_library.special_events.enabled", false), //("keystone.player-affinity.playerAffinityServiceURL", "http://127.0.0.1:29150"), + ("keystone.telemetry.heartbeat_custom_metrics", false), ("keystone.riotgamesapi.telemetry.heartbeat_products", false), ("keystone.riotgamesapi.telemetry.heartbeat_voice_chat_metrics", false), ("keystone.riotgamesapi.telemetry.newrelic_events_v2_enabled", false), @@ -58,6 +66,8 @@ class App ("keystone.rso-mobile-ui.accountCreationTosAgreement", false), ("keystone.telemetry.heartbeat_products", false), ("keystone.telemetry.heartbeat_voice_chat_metrics", false), + ("keystone.telemetry.send_error_telemetry_metrics", false), + ("keystone.telemetry.send_product_session_start_metrics", false), ("keystone.telemetry.singular_v1_enabled", false), ("lol.client_settings.clash.eosCelebrationEnabled", false), ("lol.client_settings.missions.upsell_opens_event_hub", false), @@ -81,7 +91,7 @@ class App ("rms.allow_bad_cert.enabled", true) }; - static (string, object)[] PlayerConfigValues = { + static (string, object)[] OptimizeClientConfigPlayer = { ("chat.allow_bad_cert.enabled", true), ("chat.disable_chat_restriction_muted_system_message", true), ("chat.force_filter.enabled", false), @@ -99,65 +109,60 @@ class App ("lol.client_settings.metrics.enabled", false), ("lol.client_settings.player_behavior.display_v1_ban_notifications", true), ("lol.client_settings.player_behavior.use_reform_card_v2", false), - ("lol.game_client_settings.logging.enable_http_public_logs", false) + ("lol.game_client_settings.logging.enable_http_public_logs", false), + ("lol.game_client_settings.logging.enable_rms_public_logs", false) }; public static async Task Main(string[] args) { var leagueProxy = new LeagueProxy(); - leagueProxy.Events.OnProcessConfigPublic += (string content) => + leagueProxy.Events.OnProcessConfigPublic += (string content, IHttpRequest request) => { var configObject = JsonSerializer.Deserialize(content); - SetConfigValues(configObject, PublicConfigValues); - - SetConfig(configObject, "lol.client_settings.honor", "CeremonyV3Enabled", false); - SetConfig(configObject, "lol.client_settings.honor", "Enabled", true); - SetConfig(configObject, "lol.client_settings.honor", "HonorEndpointsV2Enabled", false); - SetConfig(configObject, "lol.client_settings.honor", "HonorSuggestionsEnabled", true); - SetConfig(configObject, "lol.client_settings.honor", "HonorVisibilityEnabled", true); - SetConfig(configObject, "lol.client_settings.honor", "SecondsToVote", 90); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "applicationID", ""); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "clientToken", ""); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "isEnabled", false); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "service", ""); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "sessionReplaySampleRate", 0); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "sessionSampleRate", 0); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "site", ""); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "telemetrySampleRate", 0); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "traceSampleRate", 0); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "trackLongTasks", false); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "trackResources", false); - SetConfig(configObject, "lol.client_settings.datadog_rum_config", "trackUserInteractions", false); - SetConfig(configObject, "lol.client_settings.sentry_config", "isEnabled", false); - SetConfig(configObject, "lol.client_settings.sentry_config", "sampleRate", 0); - SetConfig(configObject, "lol.client_settings.sentry_config", "dsn", ""); - - RemoveVanguardDependencies(configObject, "keystone.products.league_of_legends.patchlines.live"); - RemoveVanguardDependencies(configObject, "keystone.products.league_of_legends.patchlines.pbe"); - RemoveVanguardDependencies(configObject, "keystone.products.valorant.patchlines.live"); + DisableVanguard(configObject); + LegacyHonor(configObject); + OptimizePublicConfig(configObject); return JsonSerializer.Serialize(configObject); }; - leagueProxy.Events.OnProcessConfigPlayer += (string content) => + leagueProxy.Events.OnProcessConfigPlayer += (string content, IHttpRequest request) => { var configObject = JsonSerializer.Deserialize(content); - SetConfigValues(configObject, PlayerConfigValues); - - NoLoyalty(configObject); - SetConfig(configObject, "lol.client_settings.deepLinks", "launchLorEnabled", false); + OptimizePlayerConfig(configObject); return JsonSerializer.Serialize(configObject); }; - leagueProxy.Events.OnProcessLedge += (string content) => + leagueProxy.Events.OnProcessLedge += (string content, IHttpRequest request) => { - var configObject = JsonSerializer.Deserialize(content); - return JsonSerializer.Serialize(configObject); + if (request.Url.LocalPath == "/leaverbuster-ledge/restrictionInfo") + { + Console.WriteLine("Processing request to /leaverbuster-ledge/restrictionInfo"); + + var configObject = JsonSerializer.Deserialize(content); + + if (configObject["rankedRestrictionEntryDto"] != null) + { + configObject["rankedRestrictionEntryDto"]["rankedRestrictionAckNeeded"] = false; + } + + content = JsonSerializer.Serialize(configObject); + + Console.WriteLine("Modified content: " + content); + } + + if (request.Url.LocalPath == "/sipt/v1/sipt/token") + { + Console.WriteLine("Processing request to sipt"); + Console.WriteLine(content); + } + + return content; }; var process = leagueProxy.StartAndLaunchRCS(args); @@ -173,6 +178,17 @@ public static async Task Main(string[] args) await process.WaitForExitAsync(); leagueProxy.Stop(); } + private static void SetEmptyArrayForConfig(JsonNode? configObject, string configKey) + { + if (configObject?[configKey] is JsonArray) + { + configObject[configKey] = new JsonArray(); // Set to empty array + } + else if (configObject?[configKey] is JsonObject jsonObject) + { + jsonObject[configKey] = new JsonArray(); + } + } static void SetConfig(JsonNode? configObject, string parentKey, string childKey, bool value) { @@ -183,7 +199,6 @@ static void SetConfig(JsonNode? configObject, string parentKey, string childKey, parentNode[childKey] = value; } } - private static void SetConfigValues(JsonNode? configObject, (string, object)[] configValues) { foreach (var (key, value) in configValues) @@ -191,7 +206,6 @@ private static void SetConfigValues(JsonNode? configObject, (string, object)[] c SetConfig(configObject, key, value); } } - private static void SetConfig(JsonNode? configObject, string key, object value) { if (configObject?[key] is not null) @@ -215,7 +229,6 @@ private static void SetConfig(JsonNode? configObject, string key, object value) } } } - private static void SetConfig(JsonNode? configObject, string parentKey, string childKey, object value) { if (configObject?[parentKey] is JsonNode parentNode) @@ -239,7 +252,51 @@ private static void SetConfig(JsonNode? configObject, string parentKey, string c } } } + public static void DisableVanguard(JsonNode? configObject) + { + SetConfigValues(configObject, VanguardConfigFlags); + RemoveVanguardDependencies(configObject, "keystone.products.league_of_legends.patchlines.live"); + RemoveVanguardDependencies(configObject, "keystone.products.league_of_legends.patchlines.pbe"); + RemoveVanguardDependencies(configObject, "keystone.products.valorant.patchlines.live"); + } + public static void LegacyHonor(JsonNode? configObject) + { + SetConfig(configObject, "lol.client_settings.honor", "CeremonyV3Enabled", false); + SetConfig(configObject, "lol.client_settings.honor", "Enabled", true); + SetConfig(configObject, "lol.client_settings.honor", "HonorEndpointsV2Enabled", false); + SetConfig(configObject, "lol.client_settings.honor", "HonorSuggestionsEnabled", true); + SetConfig(configObject, "lol.client_settings.honor", "HonorVisibilityEnabled", true); + SetConfig(configObject, "lol.client_settings.honor", "SecondsToVote", 90); + } + public static void OptimizePublicConfig(JsonNode? configObject) + { + SetConfigValues(configObject, OptimizeClientConfigPublic); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "applicationID", ""); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "clientToken", ""); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "isEnabled", false); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "service", ""); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "sessionReplaySampleRate", 0); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "sessionSampleRate", 0); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "site", ""); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "telemetrySampleRate", 0); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "traceSampleRate", 0); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "trackLongTasks", false); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "trackResources", false); + SetConfig(configObject, "lol.client_settings.datadog_rum_config", "trackUserInteractions", false); + SetConfig(configObject, "lol.client_settings.sentry_config", "isEnabled", false); + SetConfig(configObject, "lol.client_settings.sentry_config", "sampleRate", 0); + SetConfig(configObject, "lol.client_settings.sentry_config", "dsn", ""); + } + public static void OptimizePlayerConfig(JsonNode? configObject) + { + SetConfigValues(configObject, OptimizeClientConfigPlayer); + NoLoyalty(configObject); + SetConfig(configObject, "lol.client_settings.deepLinks", "launchLorEnabled", false); + SetEmptyArrayForConfig(configObject, "chat.xmpp_stanza_response_telemetry_allowed_codes"); + SetEmptyArrayForConfig(configObject, "chat.xmpp_stanza_response_telemetry_allowed_iqids"); + + } static void NoLoyalty(JsonNode? configObject) { if (configObject?["keystone.loyalty.config"] is JsonObject loyaltyConfig) @@ -253,8 +310,6 @@ static void NoLoyalty(JsonNode? configObject) } } } - - static void RemoveVanguardDependencies(JsonNode configObject, string path) { var productNode = configObject?[path]; diff --git a/LeagueProxyController.cs b/LeagueProxyController.cs index eb6c59c..6b8334b 100644 --- a/LeagueProxyController.cs +++ b/LeagueProxyController.cs @@ -1,17 +1,23 @@ using System.Text; using System.Net.Http; using System.Threading.Tasks; +using System.Net.Http.Headers; using EmbedIO; using EmbedIO.Routing; using EmbedIO.WebApi; using System.Diagnostics; using EmbedIO.Utilities; +using System.Text.Json.Nodes; +using System.Text.Json; +using Swan.Logging; +using System.Net; +using System.IO.Compression; namespace LeaguePatchCollection; public sealed class LeagueProxyEvents { - public delegate string ProcessBasicEndpoint(string content); + public delegate string ProcessBasicEndpoint(string content, IHttpRequest request); public event ProcessBasicEndpoint? OnProcessConfigPublic; public event ProcessBasicEndpoint? OnProcessConfigPlayer; @@ -35,14 +41,14 @@ private LeagueProxyEvents() OnProcessLedge = null; } - private string InvokeProcessBasicEndpoint(ProcessBasicEndpoint? @event, string content) + private string InvokeProcessBasicEndpoint(ProcessBasicEndpoint? @event, string content, IHttpRequest? request) { if (@event is null) return content; foreach (var i in @event.GetInvocationList()) { - var result = i.DynamicInvoke(content); + var result = i.DynamicInvoke(content, request); // Pass 'content' and 'request' if (result is not string resultString) throw new Exception("Return value of an event is not string!"); @@ -52,15 +58,14 @@ private string InvokeProcessBasicEndpoint(ProcessBasicEndpoint? @event, string c return content; } - internal string InvokeProcessConfigPublic(string content) => InvokeProcessBasicEndpoint(OnProcessConfigPublic, content); - internal string InvokeProcessConfigPlayer(string content) => InvokeProcessBasicEndpoint(OnProcessConfigPlayer, content); - internal string InvokeProcessLedge(string content) => InvokeProcessBasicEndpoint(OnProcessLedge, content); - + internal string InvokeProcessConfigPublic(string content, IHttpRequest request) => InvokeProcessBasicEndpoint(OnProcessConfigPublic, content, request); + internal string InvokeProcessConfigPlayer(string content, IHttpRequest request) => InvokeProcessBasicEndpoint(OnProcessConfigPlayer, content, request); + internal string InvokeProcessLedge(string content, IHttpRequest request) => InvokeProcessBasicEndpoint(OnProcessLedge, content, request); } internal sealed class ConfigController : WebApiController { - private static HttpClient _Client = new HttpClient(); + private static HttpClient _Client = new(new HttpClientHandler { UseCookies = false }); private const string BASE_URL = "https://clientconfig.rpg.riotgames.com"; private static LeagueProxyEvents _Events => LeagueProxyEvents.Instance; @@ -71,7 +76,7 @@ public async Task GetConfigPublic() var response = await ClientConfig(HttpContext.Request); var content = await response.Content.ReadAsStringAsync(); - content = _Events.InvokeProcessConfigPublic(content); + content = _Events.InvokeProcessConfigPublic(content, HttpContext.Request); await SendResponse(response, content); } @@ -82,7 +87,7 @@ public async Task GetConfigPlayer() var response = await ClientConfig(HttpContext.Request); var content = await response.Content.ReadAsStringAsync(); - content = _Events.InvokeProcessConfigPlayer(content); + content = _Events.InvokeProcessConfigPlayer(content, HttpContext.Request); await SendResponse(response, content); } @@ -92,7 +97,9 @@ private async Task ClientConfig(IHttpRequest request) var url = BASE_URL + request.RawUrl; using var message = new HttpRequestMessage(HttpMethod.Get, url); - message.Headers.TryAddWithoutValidation("User-Agent", request.Headers["user-agent"]); + + message.Headers.TryAddWithoutValidation("user-agent", request.Headers["user-agent"]); + //message.Headers.TryAddWithoutValidation("Accept-Encoding", "gzip"); if (request.Headers["x-riot-entitlements-jwt"] is not null) message.Headers.TryAddWithoutValidation("X-Riot-Entitlements-JWT", request.Headers["x-riot-entitlements-jwt"]); @@ -100,6 +107,17 @@ private async Task ClientConfig(IHttpRequest request) if (request.Headers["authorization"] is not null) message.Headers.TryAddWithoutValidation("Authorization", request.Headers["authorization"]); + if (request.Headers["x-riot-rso-identity-jwt"] is not null) + message.Headers.TryAddWithoutValidation("X-Riot-RSO-Identity-JWT", request.Headers["x-riot-rso-identity-jwt"]); + + if (request.Headers["baggage"] is not null) + message.Headers.TryAddWithoutValidation("baggage", request.Headers["baggage"]); + + if (request.Headers["traceparent"] is not null) + message.Headers.TryAddWithoutValidation("traceparent", request.Headers["traceparent"]); + + message.Headers.TryAddWithoutValidation("Accept", "application/json"); + return await _Client.SendAsync(message); } @@ -119,7 +137,7 @@ private async Task SendResponse(HttpResponseMessage response, string content) internal sealed class LedgeController : WebApiController { - private static HttpClient _Client = new HttpClient(); + private static HttpClient _Client = new(new HttpClientHandler { UseCookies = false }); private const string LEDGE_URL = "https://na-red.lol.sgp.pvp.net"; private static LeagueProxyEvents _Events => LeagueProxyEvents.Instance; @@ -127,10 +145,16 @@ internal sealed class LedgeController : WebApiController [Route(HttpVerbs.Get, "/", true)] public async Task GetLedge() { + + if (HttpContext.Request.Url.LocalPath == "/leagues-ledge/v2/notifications") + { + return; + } + var response = await GetLedge(HttpContext.Request); var content = await response.Content.ReadAsStringAsync(); - content = _Events.InvokeProcessLedge(content); + content = _Events.InvokeProcessLedge(content, HttpContext.Request); await SendResponse(response, content); } @@ -138,36 +162,90 @@ public async Task GetLedge() [Route(HttpVerbs.Post, "/", true)] public async Task PostLedge() { - var response = await PostLedge(HttpContext.Request); + string requestBody; + using (var reader = new StreamReader(HttpContext.OpenRequestStream())) + { + requestBody = await reader.ReadToEndAsync(); + } + + var response = await PostLedge(HttpContext.Request, requestBody); var content = await response.Content.ReadAsStringAsync(); - content = _Events.InvokeProcessLedge(content); + content = _Events.InvokeProcessLedge(content, HttpContext.Request); await SendResponse(response, content); } - private async Task PostLedge(IHttpRequest request) + + [Route(HttpVerbs.Put, "/", true)] + public async Task PutLedge() + { + string requestBody; + using (var reader = new StreamReader(HttpContext.OpenRequestStream())) + { + requestBody = await reader.ReadToEndAsync(); + } + + var response = await PutLedge(HttpContext.Request, requestBody); + var content = await response.Content.ReadAsStringAsync(); + + content = _Events.InvokeProcessLedge(content, HttpContext.Request); + + await SendResponse(response, content); + } + + private async Task PutLedge(IHttpRequest request, string body) { var url = LEDGE_URL + request.RawUrl; - using var message = new HttpRequestMessage(HttpMethod.Post, url); + using var message = new HttpRequestMessage(HttpMethod.Put, url); + + message.Headers.TryAddWithoutValidation("user-agent", request.Headers["user-agent"]); + + if (request.Headers["content-encoding"] is not null) + message.Headers.TryAddWithoutValidation("Content-Encoding", request.Headers["content-encoding"]); + + if (request.Headers["content-type"] is not null) + message.Content = new StringContent(body, Encoding.UTF8, request.Headers["content-type"]); + + if (request.Headers["authorization"] is not null) + message.Headers.TryAddWithoutValidation("Authorization", request.Headers["authorization"]); - message.Headers.TryAddWithoutValidation("User-Agent", request.Headers["user-agent"]); message.Headers.TryAddWithoutValidation("Accept", "application/json"); - if (request.Headers["x-riot-entitlements-jwt"] is not null) - message.Headers.TryAddWithoutValidation("X-Riot-Entitlements-JWT", request.Headers["x-riot-entitlements-jwt"]); + if (!string.IsNullOrEmpty(body)) + message.Content = new StringContent(body, Encoding.UTF8, "application/json"); + + if (request.Headers["content-length"] is not null) + { + if (long.TryParse(request.Headers["content-length"], out var contentLength)) + message.Content.Headers.ContentLength = contentLength; + } + + return await _Client.SendAsync(message); + } + + private async Task PostLedge(IHttpRequest request, string body) + { + var url = LEDGE_URL + request.RawUrl; + + using var message = new HttpRequestMessage(HttpMethod.Post, url); + + message.Headers.TryAddWithoutValidation("user-agent", request.Headers["user-agent"]); if (request.Headers["authorization"] is not null) message.Headers.TryAddWithoutValidation("Authorization", request.Headers["authorization"]); - if (request.Headers.ContainsKey("Content-Length")) + if (request.Headers["content-type"] is not null) { - message.Headers.TryAddWithoutValidation("Content-Length", request.Headers["Content-Length"]); + message.Content = new StringContent(body, null, request.Headers["content-type"]); } - if (request.Headers.ContainsKey("payload")) + message.Headers.TryAddWithoutValidation("Accept", "application/json"); + + if (request.Headers["content-length"] is not null) { - message.Headers.TryAddWithoutValidation("payload", request.Headers["payload"]); + if (long.TryParse(request.Headers["content-length"], out var contentLength)) + message.Content.Headers.ContentLength = contentLength; } return await _Client.SendAsync(message); @@ -179,28 +257,30 @@ private async Task GetLedge(IHttpRequest request) using var message = new HttpRequestMessage(HttpMethod.Get, url); - message.Headers.TryAddWithoutValidation("User-Agent", request.Headers["user-agent"]); - message.Headers.TryAddWithoutValidation("Accept", "application/json"); - - if (request.Headers["x-riot-entitlements-jwt"] is not null) - message.Headers.TryAddWithoutValidation("X-Riot-Entitlements-JWT", request.Headers["x-riot-entitlements-jwt"]); + message.Headers.TryAddWithoutValidation("Accept-Encoding", "deflate, gzip, zstd"); + message.Headers.TryAddWithoutValidation("user-agent", request.Headers["user-agent"]); if (request.Headers["authorization"] is not null) message.Headers.TryAddWithoutValidation("Authorization", request.Headers["authorization"]); + message.Headers.TryAddWithoutValidation("Accept", "application/json"); + return await _Client.SendAsync(message); } private async Task SendResponse(HttpResponseMessage response, string content) { - var responseBuffer = Encoding.UTF8.GetBytes(content); - HttpContext.Response.SendChunked = false; HttpContext.Response.ContentType = "application/json"; - HttpContext.Response.ContentLength64 = responseBuffer.Length; + HttpContext.Response.ContentLength64 = response.Content.Headers.ContentLength ?? 0; HttpContext.Response.StatusCode = (int)response.StatusCode; - await HttpContext.Response.OutputStream.WriteAsync(responseBuffer, 0, responseBuffer.Length); + if (response.Content.Headers.ContentEncoding.Contains("gzip")) + { + HttpContext.Response.Headers.Add("Content-Encoding", "gzip"); + } + + await response.Content.CopyToAsync(HttpContext.Response.OutputStream); HttpContext.Response.OutputStream.Close(); } } @@ -234,6 +314,7 @@ public Task RunAsync(CancellationToken cancellationToken = default) return _WebServer.RunAsync(cancellationToken); } } + public class LeagueProxy { private ProxyServer _ConfigServer; @@ -290,6 +371,7 @@ public void Start(out string configServerUrl, out string ledgeServerUrl) TerminateRiotServices(); + Logger.UnregisterLogger(); _ServerCTS = new CancellationTokenSource(); _ConfigServer.Start(_ServerCTS.Token); diff --git a/RiotClient.cs b/RiotClient.cs index 4c6661d..efcb68b 100644 --- a/RiotClient.cs +++ b/RiotClient.cs @@ -17,7 +17,7 @@ public RiotClient() if (path is null) return null; - IEnumerable allArgs = [$"--client-config-url={configServerUrl}", "--launch-product=league_of_legends", "--launch-patchline=live", .. args ?? []]; + IEnumerable allArgs = [$"--client-config-url={configServerUrl}", "--launch-product=league_of_legends", .. args ?? []]; return Process.Start(path, allArgs); }