diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 61bdb1605..3586876e1 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -14,7 +14,7 @@ jobs:
Game: [Ares,TS,YR]
steps:
- - uses: actions/checkout@v3.2.0
+ - uses: actions/checkout@v3.3.0
with:
fetch-depth: 0
@@ -27,7 +27,7 @@ jobs:
run: ./BuildScripts/Build-${{matrix.Game}}.ps1
shell: pwsh
- - uses: actions/upload-artifact@v3.1.1
+ - uses: actions/upload-artifact@v3.1.2
name: Upload Artifacts
with:
name: artifacts-${{matrix.Game}}
diff --git a/ClientCore/ClientCore.csproj b/ClientCore/ClientCore.csproj
index 236043a77..f17c7fb08 100644
--- a/ClientCore/ClientCore.csproj
+++ b/ClientCore/ClientCore.csproj
@@ -46,12 +46,12 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/ClientGUI/ClientGUI.csproj b/ClientGUI/ClientGUI.csproj
index ff2edf7ec..b84a222d9 100644
--- a/ClientGUI/ClientGUI.csproj
+++ b/ClientGUI/ClientGUI.csproj
@@ -17,7 +17,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/DTAConfig/DTAConfig.csproj b/DTAConfig/DTAConfig.csproj
index f1e633bfb..6a4ec55fc 100644
--- a/DTAConfig/DTAConfig.csproj
+++ b/DTAConfig/DTAConfig.csproj
@@ -15,7 +15,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/DXMainClient/DXMainClient.csproj b/DXMainClient/DXMainClient.csproj
index 0ce19f848..86c02304b 100644
--- a/DXMainClient/DXMainClient.csproj
+++ b/DXMainClient/DXMainClient.csproj
@@ -37,7 +37,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/DXMainClient/Domain/Multiplayer/CnCNet/CnCNetTunnel.cs b/DXMainClient/Domain/Multiplayer/CnCNet/CnCNetTunnel.cs
index 901f3442c..cc1d9c219 100644
--- a/DXMainClient/Domain/Multiplayer/CnCNet/CnCNetTunnel.cs
+++ b/DXMainClient/Domain/Multiplayer/CnCNet/CnCNetTunnel.cs
@@ -61,10 +61,8 @@ public static CnCNetTunnel Parse(string str, bool hasIPv6Internet, bool hasIPv4I
}
else
{
- Logger.Log($"""
- No supported IP address/connection found ({nameof(NetworkHelper.HasIPv6Internet)}={hasIPv6Internet},
- {nameof(NetworkHelper.HasIPv4Internet)}={hasIPv4Internet}) for {primaryIpAddress} - {secondaryIpAddress}.
- """);
+ Logger.Log($"No supported IP address/connection found ({nameof(NetworkHelper.HasIPv6Internet)}={hasIPv6Internet}, "
+ + $"{nameof(NetworkHelper.HasIPv4Internet)}={hasIPv4Internet}) for {primaryIpAddress} - {secondaryIpAddress}.");
return null;
}
diff --git a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/InternetGatewayDevice.cs b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/InternetGatewayDevice.cs
index a847b2488..cf18dfa79 100644
--- a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/InternetGatewayDevice.cs
+++ b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/InternetGatewayDevice.cs
@@ -21,13 +21,8 @@ namespace DTAClient.Domain.Multiplayer.CnCNet.UPNP;
internal sealed record InternetGatewayDevice(
IEnumerable Locations,
string Server,
- string CacheControl,
- string Ext,
- string SearchTarget,
- string UniqueServiceName,
UPnPDescription UPnPDescription,
- Uri PreferredLocation,
- IReadOnlyCollection LocalIpAddresses)
+ Uri PreferredLocation)
{
private const uint IpLeaseTimeInSeconds = 4 * 60 * 60;
private const ushort IanaUdpProtocolNumber = 17;
@@ -135,8 +130,9 @@ public async Task GetExternalIpV4AddressAsync(CancellationToken cance
Logger.Log($"P2P: Received external IPv4 address.");
#endif
}
- catch
+ catch (Exception ex)
{
+ ProgramConstants.LogException(ex);
}
return ipAddress;
@@ -172,8 +168,9 @@ public async Task GetExternalIpV4AddressAsync(CancellationToken cance
Logger.Log($"P2P: Received NAT status {natEnabled}.");
}
- catch
+ catch (Exception ex)
{
+ ProgramConstants.LogException(ex);
}
return natEnabled;
@@ -194,6 +191,8 @@ public async Task GetExternalIpV4AddressAsync(CancellationToken cance
}
catch (Exception ex) when (ex is not OperationCanceledException || !cancellationToken.IsCancellationRequested)
{
+ ProgramConstants.LogException(ex);
+
return (null, null);
}
}
@@ -238,9 +237,7 @@ private async ValueTask DoSoapActionAsync(
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
- ProgramConstants.LogException(ex, $"P2P: {action} error/not supported using {addressFamily}.");
-
- throw;
+ throw new($"P2P: {action} error/not supported using {addressFamily}.", ex);
}
}
@@ -321,7 +318,9 @@ private static async ValueTask ExecuteSoapAction
{
AddressFamily.InterNetwork when Locations.Any(q => q.HostNameType is UriHostNameType.IPv4) =>
Locations.FirstOrDefault(q => q.HostNameType is UriHostNameType.IPv4),
- AddressFamily.InterNetworkV6 when Locations.Any(q => q.HostNameType is UriHostNameType.IPv6) =>
+ AddressFamily.InterNetworkV6 when Locations.Any(q => q.HostNameType is UriHostNameType.IPv6 && !NetworkHelper.IsPrivateIpAddress(IPAddress.Parse(q.IdnHost))) =>
+ Locations.FirstOrDefault(q => q.HostNameType is UriHostNameType.IPv6),
+ AddressFamily.InterNetworkV6 when Locations.Any(q => q.HostNameType is UriHostNameType.IPv6 && NetworkHelper.IsPrivateIpAddress(IPAddress.Parse(q.IdnHost))) =>
Locations.FirstOrDefault(q => q.HostNameType is UriHostNameType.IPv6),
_ => PreferredLocation
};
@@ -330,7 +329,7 @@ AddressFamily.InterNetworkV6 when Locations.Any(q => q.HostNameType is UriHostNa
Device wanConnectionDevice = wanDevice.DeviceList.Single(q => q.DeviceType.Equals($"{UPnPConstants.UPnPWanConnectionDevice}:{uPnPVersion}", StringComparison.OrdinalIgnoreCase));
string serviceType = $"{UPnPConstants.UPnPServiceNamespace}:{wanConnectionDeviceService}";
ServiceListItem wanIpConnectionService = wanConnectionDevice.ServiceList.Single(q => q.ServiceType.Equals(serviceType, StringComparison.OrdinalIgnoreCase));
- var serviceUri = new Uri(FormattableString.Invariant($"{location.Scheme}://{(location.HostNameType is UriHostNameType.IPv6 ? '[' : null)}{location.IdnHost}{(location.HostNameType is UriHostNameType.IPv6 ? ']' : null)}:{location.Port}{wanIpConnectionService.ControlUrl}"));
+ Uri serviceUri = NetworkHelper.FormatUri(location.Scheme, location, (ushort)location.Port, wanIpConnectionService.ControlUrl);
return new(wanIpConnectionService, serviceUri, serviceType);
}
diff --git a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/AddressType.cs b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/AddressType.cs
deleted file mode 100644
index 50d0e5181..000000000
--- a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/AddressType.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace DTAClient.Domain.Multiplayer.CnCNet.UPNP;
-
-internal enum AddressType
-{
- Unknown,
-
- IpV4SiteLocal,
-
- IpV6LinkLocal,
-
- IpV6SiteLocal
-}
\ No newline at end of file
diff --git a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/InternetGatewayDeviceResponse.cs b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/InternetGatewayDeviceResponse.cs
index f0c62e257..e1701ea98 100644
--- a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/InternetGatewayDeviceResponse.cs
+++ b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/Models/InternetGatewayDeviceResponse.cs
@@ -3,4 +3,4 @@
namespace DTAClient.Domain.Multiplayer.CnCNet.UPNP;
-internal readonly record struct InternetGatewayDeviceResponse(Uri Location, string Server, string CacheControl, string Ext, string SearchTarget, string Usn, IPAddress LocalIpAddress);
\ No newline at end of file
+internal readonly record struct InternetGatewayDeviceResponse(Uri Location, string Server, string Usn, IPAddress LocalIpAddress);
\ No newline at end of file
diff --git a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/UPnPHandler.cs b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/UPnPHandler.cs
index 8bc06da08..a120b7e07 100644
--- a/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/UPnPHandler.cs
+++ b/DXMainClient/Domain/Multiplayer/CnCNet/UPNP/UPnPHandler.cs
@@ -70,14 +70,6 @@ internal static class UPnPHandler
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher
};
- private static IReadOnlyDictionary SsdpMultiCastAddresses
- => new Dictionary
- {
- [AddressType.IpV4SiteLocal] = IPAddress.Parse("239.255.255.250"),
- [AddressType.IpV6LinkLocal] = IPAddress.Parse("[FF02::C]"),
- [AddressType.IpV6SiteLocal] = IPAddress.Parse("[FF05::C]")
- }.AsReadOnly();
-
public static async ValueTask<(
InternetGatewayDevice InternetGatewayDevice,
List<(ushort InternalPort, ushort ExternalPort)> IpV6P2PPorts,
@@ -113,10 +105,8 @@ private static async Task GetInternetGatewayDeviceAsync(C
foreach (InternetGatewayDevice internetGatewayDevice in internetGatewayDevices)
{
- Logger.Log($"""
- P2P: Found gateway device v{internetGatewayDevice.UPnPDescription.Device.DeviceType.Split(':').LastOrDefault()}
- {internetGatewayDevice.UPnPDescription.Device.FriendlyName} ({internetGatewayDevice.Server}).
- """);
+ Logger.Log($"P2P: Found gateway device v{internetGatewayDevice.UPnPDescription.Device.DeviceType.Split(':').LastOrDefault()}"
+ + $"{internetGatewayDevice.UPnPDescription.Device.FriendlyName} ({internetGatewayDevice.Server}).");
}
InternetGatewayDevice selectedInternetGatewayDevice = GetInternetGatewayDeviceByVersion(internetGatewayDevices, 2);
@@ -125,10 +115,8 @@ private static async Task GetInternetGatewayDeviceAsync(C
if (selectedInternetGatewayDevice is not null)
{
- Logger.Log($"""
- P2P: Selected gateway device v{selectedInternetGatewayDevice.UPnPDescription.Device.DeviceType.Split(':').LastOrDefault()}
- {selectedInternetGatewayDevice.UPnPDescription.Device.FriendlyName} ({selectedInternetGatewayDevice.Server}).
- """);
+ Logger.Log($"P2P: Selected gateway device v{selectedInternetGatewayDevice.UPnPDescription.Device.DeviceType.Split(':').LastOrDefault()}"
+ + $"{selectedInternetGatewayDevice.UPnPDescription.Device.FriendlyName} ({selectedInternetGatewayDevice.Server}).");
}
else
{
@@ -295,11 +283,15 @@ private static async ValueTask> GetDevicesAsy
private static IEnumerable> GetGroupedDeviceResponses(
IEnumerable<(IPAddress LocalIpAddress, IEnumerable> Responses)> formattedDeviceResponses)
=> formattedDeviceResponses
- .SelectMany(q => q.Responses.Select(r => new InternetGatewayDeviceResponse(new(r["LOCATION"]), r["SERVER"], r["CACHE-CONTROL"], r["EXT"], r["ST"], r["USN"], q.LocalIpAddress)))
+ .SelectMany(q => q.Responses.Select(r => new InternetGatewayDeviceResponse(new(r["LOCATION"]), r["SERVER"], r["USN"], q.LocalIpAddress)))
.GroupBy(q => q.Usn);
private static Uri GetPreferredLocation(IReadOnlyCollection locations)
- => locations.FirstOrDefault(q => q.HostNameType is UriHostNameType.IPv6) ?? locations.First(q => q.HostNameType is UriHostNameType.IPv4);
+ {
+ return locations.FirstOrDefault(q => q.HostNameType is UriHostNameType.IPv6 && !NetworkHelper.IsPrivateIpAddress(IPAddress.Parse(q.IdnHost)))
+ ?? locations.FirstOrDefault(q => q.HostNameType is UriHostNameType.IPv6 && NetworkHelper.IsPrivateIpAddress(IPAddress.Parse(q.IdnHost)))
+ ?? locations.First(q => q.HostNameType is UriHostNameType.IPv4);
+ }
private static IEnumerable> GetFormattedDeviceResponses(IEnumerable responses)
{
@@ -317,23 +309,18 @@ private static IEnumerable> GetFormattedDeviceRespons
StringComparer.OrdinalIgnoreCase));
}
- private static async Task<(IPAddress LocalIpAddress, IEnumerable Responses)> SearchDevicesAsync(IPAddress localAddress, CancellationToken cancellationToken)
+ private static async Task<(IPAddress LocalIpAddress, IEnumerable Responses)> SearchDevicesAsync(IPAddress localAddress, IPAddress multicastAddress, CancellationToken cancellationToken)
{
var responses = new List();
- AddressType addressType = GetAddressType(localAddress);
-
- if (addressType is AddressType.Unknown)
- return new(localAddress, responses);
-
- using var socket = new Socket(localAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
+ using var socket = new Socket(SocketType.Dgram, ProtocolType.Udp);
var localEndPoint = new IPEndPoint(localAddress, 0);
- var multiCastIpEndPoint = new IPEndPoint(SsdpMultiCastAddresses[addressType], UPnPConstants.UPnPMultiCastPort);
+ var multiCastIpEndPoint = new IPEndPoint(multicastAddress, UPnPConstants.UPnPMultiCastPort);
try
{
socket.Bind(localEndPoint);
- string request = FormattableString.Invariant($"M-SEARCH * HTTP/1.1\r\nHOST: {multiCastIpEndPoint}\r\nST: {UPnPConstants.UPnPRootDevice}\r\nMAN: \"ssdp:discover\"\r\nMX: {ReceiveTimeoutInSeconds}\r\n\r\n");
+ string request = FormattableString.Invariant($"M-SEARCH * HTTP/1.1\r\nHOST: {NetworkHelper.FormatUri(multiCastIpEndPoint).Authority}\r\nST: {UPnPConstants.UPnPRootDevice}\r\nMAN: \"ssdp:discover\"\r\nMX: {ReceiveTimeoutInSeconds}\r\n\r\n");
const int charSize = sizeof(char);
int bufferSize = request.Length * charSize;
using IMemoryOwner memoryOwner = MemoryPool.Shared.Rent(bufferSize);
@@ -356,20 +343,6 @@ private static IEnumerable> GetFormattedDeviceRespons
return new(localAddress, responses);
}
- private static AddressType GetAddressType(IPAddress localAddress)
- {
- if (localAddress.AddressFamily is AddressFamily.InterNetwork)
- return AddressType.IpV4SiteLocal;
-
- if (localAddress.IsIPv6LinkLocal)
- return AddressType.IpV6LinkLocal;
-
- if (localAddress.IsIPv6SiteLocal)
- return AddressType.IpV6SiteLocal;
-
- return AddressType.Unknown;
- }
-
private static async ValueTask ReceiveAsync(Socket socket, ICollection responses, CancellationToken cancellationToken)
{
using IMemoryOwner memoryOwner = MemoryPool.Shared.Rent(4096);
@@ -406,9 +379,10 @@ private static async ValueTask GetDescriptionAsync(Uri uri, Can
private static async ValueTask Responses)>> DetectDevicesAsync(CancellationToken cancellationToken)
{
- IEnumerable localAddresses = NetworkHelper.GetLocalAddresses();
+ IEnumerable unicastAddresses = NetworkHelper.GetLocalAddresses();
+ IEnumerable multicastAddresses = NetworkHelper.GetMulticastAddresses();
(IPAddress LocalIpAddress, IEnumerable Responses)[] localAddressesDeviceResponses = await ClientCore.Extensions.TaskExtensions.WhenAllSafe(
- localAddresses.Select(q => SearchDevicesAsync(q, cancellationToken))).ConfigureAwait(false);
+ multicastAddresses.SelectMany(q => unicastAddresses.Where(r => r.AddressFamily == q.AddressFamily).Select(r => SearchDevicesAsync(r, q, cancellationToken)))).ConfigureAwait(false);
return localAddressesDeviceResponses.Where(q => q.Responses.Any(r => r.Any())).Select(q => (q.LocalIpAddress, q.Responses)).Distinct();
}
@@ -447,13 +421,8 @@ private static async Task ParseDeviceAsync(
return new(
locations,
internetGatewayDeviceResponses.Select(r => r.Server).Distinct().Single(),
- internetGatewayDeviceResponses.Select(r => r.CacheControl).Distinct().Single(),
- internetGatewayDeviceResponses.Select(r => r.Ext).Distinct().Single(),
- internetGatewayDeviceResponses.Select(r => r.SearchTarget).Distinct().Single(),
- internetGatewayDeviceResponses.Key,
uPnPDescription,
- preferredLocation,
- internetGatewayDeviceResponses.Select(r => r.LocalIpAddress).Distinct().ToList().AsReadOnly());
+ preferredLocation);
}
catch (Exception ex)
{
@@ -465,18 +434,9 @@ private static async Task ParseDeviceAsync(
private static Uri ParseLocation((IPAddress LocalIpAddress, Uri Location) location)
{
- if (location.Location.HostNameType is not UriHostNameType.IPv6 || !IPAddress.TryParse(location.Location.Host, out IPAddress ipAddress)
- || !NetworkHelper.IsPrivateIpAddress(ipAddress))
- {
+ if (location.Location.HostNameType is not UriHostNameType.IPv6 || !IPAddress.TryParse(location.Location.IdnHost, out IPAddress ipAddress) || !NetworkHelper.IsPrivateIpAddress(ipAddress))
return location.Location;
- }
-
- var uriBuilder = new UriBuilder(location.Location);
-
- uriBuilder.Host = FormattableString.Invariant($"[{uriBuilder.Host
- .Replace("[", null, StringComparison.OrdinalIgnoreCase)
- .Replace("]", null, StringComparison.OrdinalIgnoreCase)}%{location.LocalIpAddress.ScopeId}]");
- return uriBuilder.Uri;
+ return NetworkHelper.FormatUri(new(IPAddress.Parse(FormattableString.Invariant($"{location.Location.IdnHost}%{location.LocalIpAddress.ScopeId}")), location.Location.Port), location.Location.Scheme, location.Location.PathAndQuery);
}
}
\ No newline at end of file
diff --git a/DXMainClient/Domain/Multiplayer/NetworkHelper.cs b/DXMainClient/Domain/Multiplayer/NetworkHelper.cs
index d7ea8ecb1..8bb27a7de 100644
--- a/DXMainClient/Domain/Multiplayer/NetworkHelper.cs
+++ b/DXMainClient/Domain/Multiplayer/NetworkHelper.cs
@@ -34,7 +34,7 @@ public static bool HasIPv4Internet()
public static IEnumerable GetLocalAddresses()
=> GetUniCastIpAddresses()
- .Select(q => q.UnicastIPAddressInformation.Address);
+ .Select(q => q.Address);
public static IEnumerable GetPublicIpAddresses()
=> GetLocalAddresses()
@@ -54,12 +54,38 @@ public static IEnumerable GetLanUniCastIpAddresses(
.SelectMany(q => q.UnicastAddresses)
.Where(q => SupportedAddressFamilies.Contains(q.Address.AddressFamily));
- private static IEnumerable<(UnicastIPAddressInformation UnicastIPAddressInformation, GatewayIPAddressInformation GatewayIPAddressInformation)> GetUniCastIpAddresses()
+ public static IEnumerable GetMulticastAddresses()
+ => GetIpInterfaces()
+ .SelectMany(q => q.MulticastAddresses.Select(r => r.Address))
+ .Where(q => SupportedAddressFamilies.Contains(q.AddressFamily));
+
+ public static Uri FormatUri(string scheme, Uri uri, ushort port, string path)
+ {
+ string[] pathAndQuery = path.Split('?');
+ var uriBuilder = new UriBuilder(uri)
+ {
+ Scheme = scheme,
+ Host = uri.IdnHost,
+ Port = port,
+ Path = pathAndQuery.First(),
+ Query = pathAndQuery.Skip(1).SingleOrDefault()
+ };
+
+ return uriBuilder.Uri;
+ }
+
+ public static Uri FormatUri(IPEndPoint ipEndPoint, string scheme = null, string path = null)
+ {
+ var uriBuilder = new UriBuilder(scheme ?? Uri.UriSchemeHttps, ipEndPoint.Address.ToString(), ipEndPoint.Port, path);
+
+ return uriBuilder.Uri;
+ }
+
+ private static IEnumerable GetUniCastIpAddresses()
=> GetIpInterfaces()
.Where(q => q.GatewayAddresses.Any())
- .SelectMany(q => q.UnicastAddresses.Select(
- r => (UnicastIPAddressInformation: r, GatewayIPAddressInformation: q.GatewayAddresses.FirstOrDefault(s => s.Address.AddressFamily == r.Address.AddressFamily))))
- .Where(q => SupportedAddressFamilies.Contains(q.UnicastIPAddressInformation.Address.AddressFamily));
+ .SelectMany(q => q.UnicastAddresses)
+ .Where(q => SupportedAddressFamilies.Contains(q.Address.AddressFamily));
private static IEnumerable GetIpInterfaces()
=> NetworkInterface.GetAllNetworkInterfaces()
@@ -69,8 +95,8 @@ private static IEnumerable GetIpInterfaces()
[SupportedOSPlatform("windows")]
private static IEnumerable<(IPAddress IpAddress, PrefixOrigin PrefixOrigin, SuffixOrigin SuffixOrigin)> GetWindowsPublicIpAddresses()
=> GetUniCastIpAddresses()
- .Where(q => !IsPrivateIpAddress(q.UnicastIPAddressInformation.Address))
- .Select(q => (q.UnicastIPAddressInformation.Address, q.UnicastIPAddressInformation.PrefixOrigin, q.UnicastIPAddressInformation.SuffixOrigin));
+ .Where(q => !IsPrivateIpAddress(q.Address))
+ .Select(q => (q.Address, q.PrefixOrigin, q.SuffixOrigin));
public static IPAddress GetIpV4BroadcastAddress(UnicastIPAddressInformation unicastIpAddressInformation)
{
@@ -257,7 +283,6 @@ public static bool IsPrivateIpAddress(IPAddress ipAddress)
|| ipAddress.IsIPv6UniqueLocal
|| ipAddress.IsIPv6LinkLocal,
AddressFamily.InterNetwork => IsInRange("10.0.0.0", "10.255.255.255", ipAddress)
- || IsInRange("172.16.0.0", "172.31.255.255", ipAddress)
|| IsInRange("172.16.0.0", "172.31.255.255", ipAddress)
|| IsInRange("192.168.0.0", "192.168.255.255", ipAddress)
|| IsInRange("169.254.0.0", "169.254.255.255", ipAddress)
diff --git a/Localization/Localization.csproj b/Localization/Localization.csproj
index cdc40c544..314407ed6 100644
--- a/Localization/Localization.csproj
+++ b/Localization/Localization.csproj
@@ -14,7 +14,7 @@
Localization
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive