2using System.Collections.Generic;
5using System.Threading.Tasks;
7using Microsoft.EntityFrameworkCore;
8using Microsoft.Extensions.Logging;
9using Microsoft.Extensions.Options;
40 readonly ILogger<PortAllocator>
logger;
64 IOptions<SwarmConfiguration> swarmConfigurationOptions,
65 ILogger<PortAllocator>
logger)
70 swarmConfiguration = swarmConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(swarmConfigurationOptions));
71 this.logger =
logger ??
throw new ArgumentNullException(nameof(
logger));
80 public async ValueTask<ushort?>
GetAvailablePort(ushort basePort,
bool checkOne, CancellationToken cancellationToken)
82 ushort? result =
null;
85 async databaseContext => result = await
GetAvailablePort(databaseContext, basePort, checkOne, cancellationToken));
99 logger.LogTrace(
"Port allocation >= {basePort} requested...", basePort);
100 var ddPorts = await databaseContext
106 Port = x.Port!.Value,
109 .ToListAsync(cancellationToken);
111 var dmPorts = await databaseContext
117 ApiValidationPort = x.ApiValidationPort!.Value,
120 .ToListAsync(cancellationToken);
122 var exceptions =
new List<Exception>();
126 for (port = basePort; port < ushort.MaxValue; ++port)
128 if (checkOne && port != basePort)
133 logger.LogWarning(
"Cannot allocate port {port} as it is the TGS API port!", port);
137 var reservedGamePortData = ddPorts.Where(data => data.Port == port).ToList();
138 if (reservedGamePortData.Count > 0)
141 "Cannot allocate port {port} as it in use by the game server of instance(s): {instanceId}!",
145 reservedGamePortData.Select(data => data.InstanceId)));
149 var reservedApiValidationPortData = dmPorts.Where(data => data.ApiValidationPort == port).ToList();
150 if (reservedApiValidationPortData.Count > 0)
153 "Cannot allocate port {port} as it in use by the API validation server of instance(s): {instanceId}!",
157 reservedApiValidationPortData.Select(data => data.InstanceId)));
172 logger.LogInformation(
"Allocated port {port}", port);
176 logger.LogWarning(
"Unable to allocate port >= {basePort}!", basePort);
181 if (port != basePort)
184 exceptions.Count == 1
186 :
new AggregateException(exceptions),
187 "Failed to allocate ports {basePort}-{lastCheckedPort}!",
string? Identifier
The server's identifier.
Configuration for the server swarm system.
Extension methods for the Socket class.
static void BindTest(IPlatformIdentifier platformIdentifier, ushort port, bool includeIPv6, bool udp)
Attempt to exclusively bind to a given port .
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the PortAllocator.
async ValueTask< ushort?> GetAvailablePort(ushort basePort, bool checkOne, CancellationToken cancellationToken)
Gets a port not currently in use by TGS.A ValueTask<TResult> resulting in the first available port on...
readonly ILogger< PortAllocator > logger
The ILogger for the PortAllocator.
readonly SwarmConfiguration swarmConfiguration
The SwarmConfiguration for the PortAllocator.
async ValueTask< ushort?> GetAvailablePort(IDatabaseContext databaseContext, ushort basePort, bool checkOne, CancellationToken cancellationToken)
Gets a port not currently in use by TGS.
readonly IServerPortProvider serverPortProvider
The IServerPortProvider for the PortAllocator.
PortAllocator(IServerPortProvider serverPortProvider, IDatabaseContextFactory databaseContextFactory, IPlatformIdentifier platformIdentifier, IOptions< SwarmConfiguration > swarmConfigurationOptions, ILogger< PortAllocator > logger)
Initializes a new instance of the PortAllocator class.
readonly SemaphoreSlim allocatorLock
The SemaphoreSlim used to serialized port requisition requests.
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContext for the PortAllocator.
Async lock context helper.
static async ValueTask< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken, ILogger? logger=null)
Asyncronously locks a semaphore .
Provides access to the server's HttpApiPort.
ushort HttpApiPort
The port the server listens on.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components.
ValueTask UseContext(Func< IDatabaseContext, ValueTask > operation)
Run an operation in the scope of an IDatabaseContext.
IDatabaseCollection< DreamDaemonSettings > DreamDaemonSettings
The Models.DreamDaemonSettings in the IDatabaseContext.
IDatabaseCollection< DreamMakerSettings > DreamMakerSettings
The Models.DreamMakerSettings in the IDatabaseContext.
Gets unassigned ports for use by TGS.