Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make network servers configurable #487

Merged
merged 8 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions ClientCore/ClientConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ public class ClientConfiguration
private const string CLIENT_SETTINGS = "DTACnCNetClient.ini";
private const string GAME_OPTIONS = "GameOptions.ini";
private const string CLIENT_DEFS = "ClientDefinitions.ini";
private const string NETWORK_DEFS = "NetworkDefinitions.ini";

private static ClientConfiguration _instance;

private IniFile gameOptions_ini;
private IniFile DTACnCNetClient_ini;
private IniFile clientDefinitionsIni;
private IniFile networkDefinitionsIni;

protected ClientConfiguration()
{
Expand All @@ -36,14 +38,16 @@ protected ClientConfiguration()

FileInfo clientDefinitionsFile = SafePath.GetFile(baseResourceDirectory.FullName, CLIENT_DEFS);

if (clientDefinitionsFile is null)
if (!(clientDefinitionsFile?.Exists ?? false))
throw new FileNotFoundException($"Couldn't find {CLIENT_DEFS} at {baseResourceDirectory}. Please verify that you're running the client from the correct directory.");

clientDefinitionsIni = new IniFile(clientDefinitionsFile.FullName);

DTACnCNetClient_ini = new IniFile(SafePath.CombineFilePath(ProgramConstants.GetResourcePath(), CLIENT_SETTINGS));

gameOptions_ini = new IniFile(SafePath.CombineFilePath(baseResourceDirectory.FullName, GAME_OPTIONS));

networkDefinitionsIni = new IniFile(SafePath.CombineFilePath(ProgramConstants.GetResourcePath(), NETWORK_DEFS));
}

/// <summary>
Expand Down Expand Up @@ -99,7 +103,7 @@ public void RefreshSettings()

public string AltUIBackgroundColor => DTACnCNetClient_ini.GetStringValue(GENERAL, "AltUIBackgroundColor", "196,196,196");

public string WindowBorderColor => DTACnCNetClient_ini.GetStringValue(GENERAL, "WindowBorderColor", "128,128,128");
public string WindowBorderColor => DTACnCNetClient_ini.GetStringValue(GENERAL, "WindowBorderColor", "128,128,128");

public string PanelBorderColor => DTACnCNetClient_ini.GetStringValue(GENERAL, "PanelBorderColor", "255,255,255");

Expand Down Expand Up @@ -385,6 +389,37 @@ public IEnumerable<string> SupplementalMapFileExtensions

#endregion

#region Network definitions

public string CnCNetTunnelListURL => networkDefinitionsIni.GetStringValue(SETTINGS, "CnCNetTunnelListURL", "http://cncnet.org/master-list");

public string CnCNetPlayerCountURL => networkDefinitionsIni.GetStringValue(SETTINGS, "CnCNetPlayerCountURL", "http://api.cncnet.org/status");

public string CnCNetMapDBDownloadURL => networkDefinitionsIni.GetStringValue(SETTINGS, "CnCNetMapDBDownloadURL", "http://mapdb.cncnet.org");

public string CnCNetMapDBUploadURL => networkDefinitionsIni.GetStringValue(SETTINGS, "CnCNetMapDBUploadURL", "http://mapdb.cncnet.org/upload");

public bool DisableDiscordIntegration => networkDefinitionsIni.GetBooleanValue(SETTINGS, "DisableDiscordIntegration", false);

public List<string> IRCServers => GetIRCServers();

#endregion

public List<string> GetIRCServers()
{
List<string> servers = [];

IniSection serversSection = networkDefinitionsIni.GetSection("IRCServers");
if (serversSection != null)
foreach ((_, string value) in serversSection.Keys)
if (!string.IsNullOrWhiteSpace(value))
servers.Add(value);

return servers;
}

public bool DiscordIntegrationGloballyDisabled => string.IsNullOrWhiteSpace(DiscordAppId) || DisableDiscordIntegration;

public OSVersion GetOperatingSystemVersion()
{
#if NETFRAMEWORK
Expand Down
6 changes: 3 additions & 3 deletions DTAConfig/OptionPanels/CnCNetOptionsPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ private void InitOptions()
chkConnectOnStartup.Bottom + 12, 0, 0);
chkDiscordIntegration.Text = "Show detailed game info in Discord status".L10N("Client:DTAConfig:DiscordStatus");

if (String.IsNullOrEmpty(ClientConfiguration.Instance.DiscordAppId))
if (ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled)
{
chkDiscordIntegration.AllowChecking = false;
chkDiscordIntegration.Checked = false;
Expand Down Expand Up @@ -317,7 +317,7 @@ public override void Load()
chkSkipLoginWindow.Checked = IniSettings.SkipConnectDialog;
chkPersistentMode.Checked = IniSettings.PersistentMode;

chkDiscordIntegration.Checked = !String.IsNullOrEmpty(ClientConfiguration.Instance.DiscordAppId)
chkDiscordIntegration.Checked = !ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled
&& IniSettings.DiscordIntegration;

chkAllowGameInvitesFromFriendsOnly.Checked = IniSettings.AllowGameInvitesFromFriendsOnly;
Expand Down Expand Up @@ -352,7 +352,7 @@ public override bool Save()
IniSettings.SkipConnectDialog.Value = chkSkipLoginWindow.Checked;
IniSettings.PersistentMode.Value = chkPersistentMode.Checked;

if (!String.IsNullOrEmpty(ClientConfiguration.Instance.DiscordAppId))
if (!ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled)
{
IniSettings.DiscordIntegration.Value = chkDiscordIntegration.Checked;
}
Expand Down
2 changes: 1 addition & 1 deletion DXMainClient/DXGUI/Generic/MainMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ private void SettingsSaved(object sender, EventArgs e)
if (!connectionManager.IsConnected)
ProgramConstants.PLAYERNAME = UserINISettings.Instance.PlayerName;

if (UserINISettings.Instance.DiscordIntegration)
if (UserINISettings.Instance.DiscordIntegration && !ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled)
discordHandler.Connect();
else
discordHandler.Disconnect();
Expand Down
2 changes: 1 addition & 1 deletion DXMainClient/Domain/DiscordHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public RichPresence CurrentPresence
/// </summary>
public DiscordHandler()
{
if (!UserINISettings.Instance.DiscordIntegration || string.IsNullOrEmpty(ClientConfiguration.Instance.DiscordAppId))
if (!UserINISettings.Instance.DiscordIntegration || ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled)
return;

InitializeClient();
Expand Down
4 changes: 3 additions & 1 deletion DXMainClient/Domain/MainClientConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace DTAClient.Domain
{
public static class MainClientConstants
{
public const string CNCNET_TUNNEL_LIST_URL = "http://cncnet.org/master-list";
public static string CNCNET_TUNNEL_LIST_URL = "http://cncnet.org/master-list";

public static string GAME_NAME_LONG = "CnCNet Client";
public static string GAME_NAME_SHORT = "CnCNet";
Expand Down Expand Up @@ -92,6 +92,8 @@ public static void Initialize()

CREDITS_URL = clientConfiguration.CreditsURL;

CNCNET_TUNNEL_LIST_URL = clientConfiguration.CnCNetTunnelListURL;

USE_ISOMETRIC_CELLS = clientConfiguration.UseIsometricCells;
TDRA_WAYPOINT_COEFFICIENT = clientConfiguration.WaypointCoefficient;
MAP_CELL_SIZE_X = clientConfiguration.MapCellSizeX;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,15 @@
{
try
{
// Don't fetch the player count if it is explicitly disabled
// For example, the official CnCNet server might be unavailable/unstable in a country with Internet censorship,
// which causes lags in the splash screen. In the worst case, say if packets are dropped, it waits until timeouts --- 30 seconds
if (string.IsNullOrWhiteSpace(ClientConfiguration.Instance.CnCNetPlayerCountURL))
return -1;

WebClient client = new WebClient();

Check warning on line 59 in DXMainClient/Domain/Multiplayer/CnCNet/CnCNetPlayerCountTask.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
Stream data = client.OpenRead(ClientConfiguration.Instance.CnCNetPlayerCountURL);

Stream data = client.OpenRead("http://api.cncnet.org/status");

string info = string.Empty;

using (StreamReader reader = new StreamReader(data))
Expand Down
27 changes: 21 additions & 6 deletions DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@

private static readonly object locker = new object();

private const string MAPDB_URL = "http://mapdb.cncnet.org/upload";

/// <summary>
/// Adds a map into the CnCNet map upload queue.
/// </summary>
Expand Down Expand Up @@ -78,8 +76,14 @@

Logger.Log("MapSharer: Starting upload of " + map.BaseFilePath);

bool success = false;
string message = MapUpload(MAPDB_URL, map, myGameId, out success);
if (string.IsNullOrWhiteSpace(ClientConfiguration.Instance.CnCNetMapDBUploadURL))
{
Logger.Log("MapSharer: Upload URL is not configured.");
MapUploadFailed?.Invoke(null, new MapEventArgs(map));
return;
}

string message = MapUpload(ClientConfiguration.Instance.CnCNetMapDBUploadURL, map, myGameId, out bool success);

if (success)
{
Expand Down Expand Up @@ -206,7 +210,7 @@

private static byte[] UploadFiles(string address, List<FileToUpload> files, NameValueCollection values)
{
WebRequest request = WebRequest.Create(address);

Check warning on line 213 in DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

'WebRequest.Create(string)' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 213 in DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

'WebRequest.Create(string)' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 213 in DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

'WebRequest.Create(string)' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
request.Method = "POST";
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
request.ContentType = "multipart/form-data; boundary=" + boundary;
Expand Down Expand Up @@ -380,12 +384,22 @@

using (TWebClient webClient = new TWebClient())
{
// TODO enable proxy support for some users
webClient.Proxy = null;

if (string.IsNullOrWhiteSpace(ClientConfiguration.Instance.CnCNetMapDBDownloadURL))
{
success = false;
Logger.Log("MapSharer: Download URL is not configured.");
return null;
}

string url = string.Format(CultureInfo.InvariantCulture, "{0}/{1}/{2}.zip", ClientConfiguration.Instance.CnCNetMapDBDownloadURL, myGame, sha1);

try
{
Logger.Log("MapSharer: Downloading URL: " + "http://mapdb.cncnet.org/" + myGame + "/" + sha1 + ".zip");
webClient.DownloadFile("http://mapdb.cncnet.org/" + myGame + "/" + sha1 + ".zip", destinationFile.FullName);
Logger.Log($"MapSharer: Downloading URL: {url}");
webClient.DownloadFile(url, destinationFile.FullName);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -447,8 +461,9 @@
{
private int Timeout = 10000;

public TWebClient()

Check warning on line 464 in DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 464 in DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 464 in DXMainClient/Domain/Multiplayer/CnCNet/MapSharer.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
{
// TODO enable proxy support for some users
this.Proxy = null;
}

Expand Down
87 changes: 59 additions & 28 deletions DXMainClient/Domain/Multiplayer/CnCNet/TunnelHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,48 +143,78 @@
});
}

/// <summary>
/// Downloads and parses the list of CnCNet tunnels.
/// </summary>
/// <returns>A list of tunnel servers.</returns>
private List<CnCNetTunnel> RefreshTunnels()
{
FileInfo tunnelCacheFile = SafePath.GetFile(ProgramConstants.ClientUserFilesPath, "tunnel_cache");

List<CnCNetTunnel> returnValue = new List<CnCNetTunnel>();
private bool OnlineTunnelDataAvailable => !string.IsNullOrWhiteSpace(MainClientConstants.CNCNET_TUNNEL_LIST_URL);
private bool OfflineTunnelDataAvailable => SafePath.GetFile(ProgramConstants.ClientUserFilesPath, "tunnel_cache").Exists;

private byte[] GetRawTunnelDataOnline()
{
WebClient client = new WebClient();

Check warning on line 151 in DXMainClient/Domain/Multiplayer/CnCNet/TunnelHandler.cs

View workflow job for this annotation

GitHub Actions / build-clients (Ares)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 151 in DXMainClient/Domain/Multiplayer/CnCNet/TunnelHandler.cs

View workflow job for this annotation

GitHub Actions / build-clients (TS)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 151 in DXMainClient/Domain/Multiplayer/CnCNet/TunnelHandler.cs

View workflow job for this annotation

GitHub Actions / build-clients (YR)

'WebClient.WebClient()' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
return client.DownloadData(MainClientConstants.CNCNET_TUNNEL_LIST_URL);
}

byte[] data;
private byte[] GetRawTunnelDataOffline()
{
FileInfo tunnelCacheFile = SafePath.GetFile(ProgramConstants.ClientUserFilesPath, "tunnel_cache");
return File.ReadAllBytes(tunnelCacheFile.FullName);
}

private byte[] GetRawTunnelData(int retryCount = 2)
{
Logger.Log("Fetching tunnel server info.");

try
{
data = client.DownloadData(MainClientConstants.CNCNET_TUNNEL_LIST_URL);
}
catch (Exception ex)
if (OnlineTunnelDataAvailable)
{
Logger.Log("Error when downloading tunnel server info: " + ex.ToString());
Logger.Log("Retrying.");
try
for (int i = 0; i < retryCount; i++)
{
data = client.DownloadData(MainClientConstants.CNCNET_TUNNEL_LIST_URL);
}
catch
{
if (!tunnelCacheFile.Exists)
try
{
Logger.Log("Tunnel cache file doesn't exist!");
return returnValue;
byte[] data = GetRawTunnelDataOnline();
return data;
}
else
catch (Exception ex)
{
Logger.Log("Fetching tunnel server list failed. Using cached tunnel data.");
data = File.ReadAllBytes(tunnelCacheFile.FullName);
Logger.Log("Error when downloading tunnel server info: " + ex.Message);
if (i < retryCount - 1)
Logger.Log("Retrying.");
else
Logger.Log("Fetching tunnel server list failed.");
}
}
}
else
{
// Don't fetch the latest tunnel list if it is explicitly disabled
// For example, the official CnCNet server might be unavailable/unstable in a country with Internet censorship,
// where players might either establish a substitute server or manually distribute the tunnel cache file
Logger.Log("Fetching tunnel server list online is disabled.");
}

if (OfflineTunnelDataAvailable)
{
Logger.Log("Using cached tunnel data.");
byte[] data = GetRawTunnelDataOffline();
return data;
}
else
Logger.Log("Tunnel cache file doesn't exist!");

return null;
}


/// <summary>
/// Downloads and parses the list of CnCNet tunnels.
/// </summary>
/// <returns>A list of tunnel servers.</returns>
private List<CnCNetTunnel> RefreshTunnels()
{
List<CnCNetTunnel> returnValue = new List<CnCNetTunnel>();

FileInfo tunnelCacheFile = SafePath.GetFile(ProgramConstants.ClientUserFilesPath, "tunnel_cache");

byte[] data = GetRawTunnelData();
if (data is null)
return returnValue;

string convertedData = Encoding.Default.GetString(data);

Expand Down Expand Up @@ -234,6 +264,7 @@
}
}

Logger.Log($"Successfully refreshed tunnel cache with {returnValue.Count} servers.");
return returnValue;
}

Expand Down
40 changes: 22 additions & 18 deletions DXMainClient/Online/Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,32 @@ public Connection(IConnectionManager connectionManager)

IConnectionManager connectionManager;

private static IList<Server> _servers = null;
/// <summary>
/// The list of CnCNet / GameSurge IRC servers to connect to.
/// </summary>
private static readonly IList<Server> Servers = new List<Server>
private static IList<Server> Servers
{
new Server("Burstfire.UK.EU.GameSurge.net", "GameSurge London, UK", new int[3] { 6667, 6668, 7000 }),
new Server("ColoCrossing.IL.US.GameSurge.net", "GameSurge Chicago, IL", new int[5] { 6660, 6666, 6667, 6668, 6669 }),
new Server("Gameservers.NJ.US.GameSurge.net", "GameSurge Newark, NJ", new int[7] { 6665, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("Krypt.CA.US.GameSurge.net", "GameSurge Santa Ana, CA", new int[4] { 6666, 6667, 6668, 6669 }),
new Server("NuclearFallout.WA.US.GameSurge.net", "GameSurge Seattle, WA", new int[2] { 6667, 5960 }),
new Server("Portlane.SE.EU.GameSurge.net", "GameSurge Stockholm, Sweden", new int[5] { 6660, 6666, 6667, 6668, 6669 }),
new Server("Prothid.NY.US.GameSurge.Net", "GameSurge NYC, NY", new int[7] { 5960, 6660, 6666, 6667, 6668, 6669, 6697 }),
new Server("TAL.DE.EU.GameSurge.net", "GameSurge Wuppertal, Germany", new int[5] { 6660, 6666, 6667, 6668, 6669 }),
new Server("208.167.237.120", "GameSurge IP 208.167.237.120", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("192.223.27.109", "GameSurge IP 192.223.27.109", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("108.174.48.100", "GameSurge IP 108.174.48.100", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("208.146.35.105", "GameSurge IP 208.146.35.105", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("195.8.250.180", "GameSurge IP 195.8.250.180", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("91.217.189.76", "GameSurge IP 91.217.189.76", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("195.68.206.250", "GameSurge IP 195.68.206.250", new int[7] { 6660, 6666, 6667, 6668, 6669, 7000, 8080 }),
new Server("irc.gamesurge.net", "GameSurge", new int[1] { 6667 }),
}.AsReadOnly();
get
{
if (_servers is not null)
return _servers;

IEnumerable<string> serversList;
if (ClientConfiguration.Instance.IRCServers.Count > 0)
serversList = ClientConfiguration.Instance.IRCServers;
else
{
// fallback to the hardcoded servers list
serversList = [
"irc.gamesurge.net|GameSurge|6667,6660,6666,6668,6669",
Metadorius marked this conversation as resolved.
Show resolved Hide resolved
];
}

_servers = serversList.Select(Server.Deserialize).ToList();
return _servers;
}
}

bool _isConnected = false;
public bool IsConnected
Expand Down
Loading
Loading