diff --git a/DepotDownloader/CDNClientPool.cs b/DepotDownloader/CDNClientPool.cs index 2f85d69c..adccd7e7 100644 --- a/DepotDownloader/CDNClientPool.cs +++ b/DepotDownloader/CDNClientPool.cs @@ -5,91 +5,46 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using SteamKit2; -using SteamKit2.CDN; +using SteamPrefill.Handlers; -namespace DepotDownloader +namespace LancachePrefill.Common { - /// - /// CDNClientPool provides a pool of connections to CDN endpoints, requesting CDN tokens as needed - /// - class CDNClientPool + public class CDNClientPool { private const int ServerEndpointMinimumSize = 8; - private readonly Steam3Session steamSession; - private readonly uint appId; - public Client CDNClient { get; } - public Server ProxyServer { get; private set; } - - private readonly ConcurrentStack activeConnectionPool = new ConcurrentStack(); - private readonly BlockingCollection availableServerEndpoints = new BlockingCollection(); - - private readonly AutoResetEvent populatePoolEvent = new AutoResetEvent(true); - private readonly Task monitorTask; - private readonly CancellationTokenSource shutdownToken = new CancellationTokenSource(); + private readonly IAnsiConsole _ansiConsole; + private readonly string _cdnUrl; + private readonly ConcurrentStack _activeConnectionPool = new ConcurrentStack(); + private readonly BlockingCollection _availableServerEndpoints = new BlockingCollection(); + private readonly AutoResetEvent _populatePoolEvent = new AutoResetEvent(true); + private readonly Task _monitorTask; + private readonly CancellationTokenSource _shutdownToken = new CancellationTokenSource(); public CancellationTokenSource ExhaustedToken { get; set; } - public CDNClientPool(Steam3Session steamSession, uint appId) + public CDNClientPool(IAnsiConsole ansiConsole, string cdnUrl) { - this.steamSession = steamSession; - this.appId = appId; - CDNClient = new Client(steamSession.steamClient); + _ansiConsole = ansiConsole; + _cdnUrl = cdnUrl; - monitorTask = Task.Factory.StartNew(ConnectionPoolMonitorAsync, TaskCreationOptions.LongRunning); + _monitorTask = Task.Factory.StartNew(ConnectionPoolMonitorAsync, TaskCreationOptions.LongRunning); } public void Shutdown() { - shutdownToken.Cancel(); - monitorTask.Wait(); - } - - private async Task> FetchBootstrapServerListAsync() - { - try - { - var cdnServers = await steamSession.steamContent.GetServersForSteamPipe().ConfigureAwait(false); - return cdnServers; - } - catch (Exception ex) - { - Console.WriteLine("Failed to retrieve content server list: {0}", ex.Message); - return null; - } - } - - private async Task ResolveLancacheIpAsync(string hostname) - { - try - { - var hostEntry = await Dns.GetHostEntryAsync(hostname).ConfigureAwait(false); - return hostEntry.AddressList.FirstOrDefault(ip => IsPrivateIp(ip)); - } - catch (Exception ex) - { - Console.WriteLine("Failed to resolve Lancache IP: {0}", ex.Message); - return null; - } - } - - private bool IsPrivateIp(IPAddress ip) - { - byte[] bytes = ip.GetAddressBytes(); - return bytes[0] == 10 || - (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || - (bytes[0] == 192 && bytes[1] == 168); + _shutdownToken.Cancel(); + _monitorTask.Wait(); } private async Task ConnectionPoolMonitorAsync() { var didPopulate = false; - while (!shutdownToken.IsCancellationRequested) + while (!_shutdownToken.IsCancellationRequested) { - populatePoolEvent.WaitOne(TimeSpan.FromSeconds(1)); + _populatePoolEvent.WaitOne(TimeSpan.FromSeconds(1)); - if (availableServerEndpoints.Count < ServerEndpointMinimumSize && steamSession.steamClient.IsConnected) + if (_availableServerEndpoints.Count < ServerEndpointMinimumSize) { var servers = await FetchBootstrapServerListAsync().ConfigureAwait(false); @@ -99,45 +54,29 @@ namespace DepotDownloader return; } - ProxyServer = servers.FirstOrDefault(x => x.UseAsProxy); - foreach (var server in servers) { - if (server.Type == "SteamCache") + var resolvedIp = await ResolveLancacheIpAsync(server.Host).ConfigureAwait(false); + if (resolvedIp != null && IsPrivateIp(resolvedIp)) { - var lancacheIp = await ResolveLancacheIpAsync(server.Host).ConfigureAwait(false); - if (lancacheIp != null && IsPrivateIp(lancacheIp)) + var lancacheServer = new Server { - var lancacheServer = new Server - { - Host = lancacheIp.ToString(), - Type = server.Type, - NumEntries = server.NumEntries, - WeightedLoad = server.WeightedLoad, - AllowedAppIds = server.AllowedAppIds.ToArray(), - Protocol = Server.ConnectionProtocol.HTTP // Downgrade to HTTP - }; - - Console.WriteLine($"Found Lancache Server: {lancacheServer.Host}. Downgrading connection to HTTP."); - availableServerEndpoints.Add(lancacheServer); - } - } - else - { - var isEligibleForApp = server.AllowedAppIds.Length == 0 || server.AllowedAppIds.Contains(appId); - if (isEligibleForApp && (server.Type == "SteamCache" || server.Type == "CDN")) - { - for (var i = 0; i < server.NumEntries; i++) - { - availableServerEndpoints.Add(server); - } - } + Host = resolvedIp.ToString(), + Type = server.Type, + NumEntries = server.NumEntries, + WeightedLoad = server.WeightedLoad, + AllowedAppIds = server.AllowedAppIds.ToArray(), + Protocol = Server.ConnectionProtocol.HTTP // Downgrade to HTTP + }; + + _ansiConsole.MarkupLine($"Found Lancache Server: {_cdnUrl}. Downgrading connection to HTTP."); + _availableServerEndpoints.Add(lancacheServer); } } didPopulate = true; } - else if (availableServerEndpoints.Count == 0 && !steamSession.steamClient.IsConnected && didPopulate) + else if (_availableServerEndpoints.Count == 0 && didPopulate) { ExhaustedToken?.Cancel(); return; @@ -145,39 +84,39 @@ namespace DepotDownloader } } - private Server BuildConnection(CancellationToken token) + private async Task> FetchBootstrapServerListAsync() { - if (availableServerEndpoints.Count < ServerEndpointMinimumSize) - { - populatePoolEvent.Set(); - } - - return availableServerEndpoints.Take(token); + // Implement logic to fetch the CDN server list + return null; } - public async Task DownloadDepotChunkAsync(uint depotId, DepotManifest.ChunkData chunkData, Server connection, byte[] depotKey) + private async Task ResolveLancacheIpAsync(string hostname) { try { - // Call the DownloadDepotChunkAsync method of CDNClient - var chunk = await CDNClient.DownloadDepotChunkAsync(depotId, chunkData, connection, depotKey); - - // Return the downloaded chunk - return chunk; + var hostEntry = await Dns.GetHostEntryAsync(hostname).ConfigureAwait(false); + return hostEntry.AddressList.FirstOrDefault(ip => IsPrivateIp(ip)); } catch (Exception ex) { - // Handle any exceptions that occur during chunk download - Console.WriteLine($"Error downloading chunk: {ex.Message}"); + _ansiConsole.MarkupLine($"Failed to resolve Lancache IP: {ex.Message}"); return null; } } + private bool IsPrivateIp(IPAddress ip) + { + byte[] bytes = ip.GetAddressBytes(); + return bytes[0] == 10 || + (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31) || + (bytes[0] == 192 && bytes[1] == 168); + } + public Server GetConnection(CancellationToken token) { - if (!activeConnectionPool.TryPop(out var connection)) + if (!_activeConnectionPool.TryPop(out var connection)) { - connection = BuildConnection(token); + connection = _availableServerEndpoints.Take(token); } return connection; @@ -187,14 +126,12 @@ namespace DepotDownloader { if (server == null) return; - activeConnectionPool.Push(server); + _activeConnectionPool.Push(server); } public void ReturnBrokenConnection(Server server) { - if (server == null) return; - - // Broken connections are not returned to the pool + // Implement logic to handle broken connections } } } diff --git a/DepotDownloader/DepotDownloader.csproj b/DepotDownloader/DepotDownloader.csproj index 85fe1a3d..275a540c 100644 --- a/DepotDownloader/DepotDownloader.csproj +++ b/DepotDownloader/DepotDownloader.csproj @@ -16,11 +16,6 @@ - - - - - ..\..\SteamKit\SteamKit2\SteamKit2\bin\Debug\net8.0\SteamKit2.dll - +