Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
66c9063
Bump cncnet protocol version
11EJDE11 Oct 5, 2025
ac9a9b3
Add UseLegacyTunnels and UseDynamicTunnels settings
11EJDE11 Oct 5, 2025
94a006b
Add check for tunnelling method when receiving game broadcasts
11EJDE11 Oct 5, 2025
732152e
Allow displaying and selecting V3 tunnels in TunnelListBox
11EJDE11 Oct 5, 2025
787eec3
Update ping text in GameInformationPanel when dynamic tunnels are ena…
11EJDE11 Oct 5, 2025
9127d7f
Allow lobbies to be created with V3 tunnels
11EJDE11 Oct 5, 2025
8f9e69d
Fix null ping crash when hosted game uses dynamic tunnels
11EJDE11 Oct 5, 2025
0f85089
Add V3PlayerInfo
11EJDE11 Oct 8, 2025
bff149e
Add tunnel version to /tunnelinfo chatbox command
11EJDE11 Oct 9, 2025
515a38a
Separate out negotiation status information into new file
11EJDE11 Oct 9, 2025
ab92b94
Update lobby ping indicators
11EJDE11 Oct 9, 2025
10628a0
Merge NEGSTAT and NEGPNG CTCP messages > NEGINFO
11EJDE11 Oct 9, 2025
17a7f88
Reset pings on TunnelModeChange
11EJDE11 Oct 9, 2025
b455845
Change to ping distinct IPs and update others
11EJDE11 Oct 9, 2025
5733cf1
Remove debug call
11EJDE11 Oct 9, 2025
eddf60d
Rename files
11EJDE11 Oct 9, 2025
c2da73d
Add final ping to the tunnel choice packet send to non-decider
11EJDE11 Oct 9, 2025
f7e442a
Rearrange some functions
11EJDE11 Oct 10, 2025
fa4b6e7
Prevent duplicate complete messages
11EJDE11 Oct 11, 2025
34c49f6
Increase timeout
11EJDE11 Oct 11, 2025
3f4269b
Merge branch 'develop' into new-v3-tunnels
11EJDE11 Nov 22, 2025
caf9f1e
Add nullable to various files
11EJDE11 Nov 22, 2025
8cb2735
Fix ping check
11EJDE11 Nov 22, 2025
288f91e
Remove unnecessary if statement
11EJDE11 Nov 22, 2025
8ff6d06
Fix nullable issues
11EJDE11 Nov 22, 2025
1723234
Fix endianness
11EJDE11 Nov 22, 2025
80f4769
More nullable fixups
11EJDE11 Nov 22, 2025
aba4aad
More nullable fixups
11EJDE11 Nov 22, 2025
7752275
Store pings as PingValue record instead of integers
11EJDE11 Dec 3, 2025
a577951
Update TunnelInfo lobby command
11EJDE11 Dec 3, 2025
65f168f
Merge remote-tracking branch 'origin/develop' into new-v3-tunnels
11EJDE11 Dec 6, 2025
24366a9
Merge remote-tracking branch 'origin/develop' into new-v3-tunnels
11EJDE11 Dec 26, 2025
c849e5a
Fix negotiator leak
11EJDE11 Dec 26, 2025
43a0339
Update early selection threshold
11EJDE11 Dec 26, 2025
bae1fdd
Fix comment
11EJDE11 Dec 26, 2025
1e9333f
Temp test
11EJDE11 Dec 31, 2025
5ea8ac0
Undo temp change
11EJDE11 Dec 31, 2025
dd5f5e5
Merge with develop
11EJDE11 Jan 24, 2026
98442ba
Merge branch 'develop' into new-v3-tunnels
11EJDE11 Jan 24, 2026
c8f8d0f
Fix header alignment in negotiation status panel
11EJDE11 Jan 24, 2026
9dd924e
Update protocol revision to shave off 4 bytes
11EJDE11 Jan 24, 2026
0133a03
Add summary comments
11EJDE11 Jan 24, 2026
5b9f62b
Add exception when requesting negotiation status between same player
11EJDE11 Jan 24, 2026
8508ca0
Change FromMS to conditional expression
11EJDE11 Jan 24, 2026
45b19b5
Avoid unnecessary task creation
11EJDE11 Jan 24, 2026
df9e132
Fix formatting, spelling, and grammar
11EJDE11 Jan 24, 2026
f051920
Use icons for negotiation status instead of chat messages
11EJDE11 Jan 24, 2026
d27fd35
-Move PACKET_LOSS_WEIGHT constant to top of class before properties
11EJDE11 Mar 14, 2026
b8990d4
-Convert _isDecider comments to XML summary doc
11EJDE11 Mar 14, 2026
df4d285
-UnregisterHandler now logs based on TryRemove return value
11EJDE11 Mar 14, 2026
8b4ed5f
Update magic to CNCNET
11EJDE11 Mar 14, 2026
e1c2766
Merge origin/develop into new-v3-tunnels
11EJDE11 Mar 14, 2026
b210cb6
Fix LAN lobby issue with GameInformationPanel
11EJDE11 Mar 14, 2026
fb42ef8
Make TunnelChosenEventArgs properties nullable
11EJDE11 Apr 6, 2026
636ad00
Make UpdatePlayerPingIndicator tooltipText nullable
11EJDE11 Apr 6, 2026
f74fc6e
Remove duplicate CHANGE_TUNNEL_SERVER_MESSAGE handler
11EJDE11 Apr 6, 2026
2b46409
Add packet length validation in V3GameTunnelBridge
11EJDE11 Apr 6, 2026
df06074
Move NegotiationStatus enum to Domain layer
11EJDE11 Apr 6, 2026
0cd0e33
Fix NegotiateAsync to return actual negotiation result
11EJDE11 Apr 6, 2026
172921b
Fix negotiation cleanup and completion
11EJDE11 Apr 6, 2026
02907b3
Merge develop into new-v3-tunnels
11EJDE11 Apr 15, 2026
7b7b926
Fix null reference on CurrentTunnel.Version in StartGame and WriteSpa…
11EJDE11 Apr 15, 2026
3c11968
Fix null reference in PreparePlayerGameData when V3PlayerInfo is missing
11EJDE11 Apr 15, 2026
758f04a
Fix null reference in HandlePlayerTunnelMessage when tunnel is not in…
11EJDE11 Apr 15, 2026
3095f82
Fix decider firing NegotiationResult twice when ack retries are exhau…
11EJDE11 Apr 15, 2026
c3ebc8b
Fix non-decider timeout: raise NegotiationResult, prevent double Nego…
11EJDE11 Apr 15, 2026
c426dbd
Prevent double event handler subscription when negotiation is already…
11EJDE11 Apr 15, 2026
486db6f
Show all-negotiations-complete notice and high-ping warnings to all p…
11EJDE11 Apr 15, 2026
bf7c943
Add Shutdown and AddTunnels to V3TunnelCommunicator; update endpoints…
11EJDE11 Apr 15, 2026
7f34ecc
Show actionable error when no V3 tunnels are available for negotiation
11EJDE11 Apr 15, 2026
2103043
Send NegotiationFailed to decider when non-decider times out to stop …
11EJDE11 Apr 15, 2026
3222220
Show negotiated player-to-player ping in /tunnelinfo instead of serve…
11EJDE11 Apr 15, 2026
a1146d7
Merge origin/new-v3-tunnels
11EJDE11 Apr 15, 2026
f488969
Distinguish 'already in progress' from 'failed' in StartNegotiation s…
11EJDE11 Apr 16, 2026
623943a
Bail decider's TunnelChoice retry loop early when NegotiationFailed p…
11EJDE11 Apr 16, 2026
eae594e
Guard PerformDeciderNegotiationAsync against an empty tunnel list, wh…
11EJDE11 Apr 16, 2026
b7b4230
Call ToString on the unwrapped PingValue rather than the Nullable wra…
11EJDE11 Apr 16, 2026
a233480
Log a warning when two peers have identical player IDs, since the dec…
11EJDE11 Apr 16, 2026
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
11 changes: 11 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"permissions": {
"allow": [
"Bash(xargs grep:*)",
"Bash(find /c/Users/QWE/.nuget/packages/fontstashsharp* -name \"*.cs\")",
"Bash(unzip -p stbtruetypesharp.1.26.12.nupkg \"lib/netstandard2.0/StbTrueTypeSharp.dll\")",
"Bash(dotnet-ilspycmd /tmp/StbTrueTypeSharp.dll)",
"Read(//tmp/**)"
]
}
}
2 changes: 1 addition & 1 deletion ClientCore/ProgramConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static class ProgramConstants

public const string QRES_EXECUTABLE = "qres.dat";

public const string CNCNET_PROTOCOL_REVISION = "R14";
public const string CNCNET_PROTOCOL_REVISION = "A";
public const string LAN_PROTOCOL_REVISION = "RL8";
public const int LAN_PORT = 1234;
public const int LAN_INGAME_PORT = 1234;
Expand Down
6 changes: 6 additions & 0 deletions ClientCore/Settings/UserINISettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ protected UserINISettings(IniFile iniFile)
MapSortState = new IntSetting(iniFile, MULTIPLAYER, "MapSortState", (int)SortDirection.None);
SearchAllGameModes = new BoolSetting(iniFile, MULTIPLAYER, "SearchAllGameModes", false);

UseLegacyTunnels = new BoolSetting(iniFile, MULTIPLAYER, "UseLegacyTunnels", false);
UseDynamicTunnels = new BoolSetting(iniFile, MULTIPLAYER, "UseDynamicTunnels", true);

CheckForUpdates = new BoolSetting(iniFile, OPTIONS, "CheckforUpdates", true);

PrivacyPolicyAccepted = new BoolSetting(iniFile, OPTIONS, "PrivacyPolicyAccepted", false);
Expand Down Expand Up @@ -261,6 +264,9 @@ protected UserINISettings(IniFile iniFile)
public BoolSetting SteamIntegration { get; private set; }
public BoolSetting AllowGameInvitesFromFriendsOnly { get; private set; }

public BoolSetting UseLegacyTunnels { get; private set; }
public BoolSetting UseDynamicTunnels { get; private set; }

public BoolSetting NotifyOnUserListChange { get; private set; }

public BoolSetting DisablePrivateMessagePopups { get; private set; }
Expand Down
48 changes: 40 additions & 8 deletions DXMainClient/DXGUI/Generic/OptionPanels/CnCNetOptionsPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ public CnCNetOptionsPanel(WindowManager windowManager, UserINISettings iniSettin

XNAClientDropDown ddAllowPrivateMessagesFrom;

XNAClientCheckBox chkUseLegacyTunnels;
XNAClientCheckBox chkUseDynamicTunnels;

GameCollection gameCollection;

List<XNAClientCheckBox> followedGameChks = new List<XNAClientCheckBox>();
Expand Down Expand Up @@ -118,7 +121,7 @@ private void InitOptions()
chkPersistentMode.Name = nameof(chkPersistentMode);
chkPersistentMode.ClientRectangle = new Rectangle(
chkSkipLoginWindow.X,
chkSkipLoginWindow.Bottom + 12, 0, 0);
chkSkipLoginWindow.Bottom + 9, 0, 0);
chkPersistentMode.Text = "Stay connected outside of the CnCNet lobby".L10N("Client:DTAConfig:StayConnect");
chkPersistentMode.CheckedChanged += ChkPersistentMode_CheckedChanged;

Expand All @@ -128,7 +131,7 @@ private void InitOptions()
chkConnectOnStartup.Name = nameof(chkConnectOnStartup);
chkConnectOnStartup.ClientRectangle = new Rectangle(
chkSkipLoginWindow.X,
chkPersistentMode.Bottom + 12, 0, 0);
chkPersistentMode.Bottom + 9, 0, 0);
chkConnectOnStartup.Text = "Connect automatically on client startup".L10N("Client:DTAConfig:ConnectOnStart");
chkConnectOnStartup.AllowChecking = false;

Expand All @@ -138,7 +141,7 @@ private void InitOptions()
chkDiscordIntegration.Name = nameof(chkDiscordIntegration);
chkDiscordIntegration.ClientRectangle = new Rectangle(
chkSkipLoginWindow.X,
chkConnectOnStartup.Bottom + 12, 0, 0);
chkConnectOnStartup.Bottom + 9, 0, 0);
chkDiscordIntegration.Text = "Show detailed game info in Discord status".L10N("Client:DTAConfig:DiscordStatus");

if (ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled)
Expand All @@ -157,7 +160,7 @@ private void InitOptions()
chkAllowGameInvitesFromFriendsOnly.Name = nameof(chkAllowGameInvitesFromFriendsOnly);
chkAllowGameInvitesFromFriendsOnly.ClientRectangle = new Rectangle(
chkDiscordIntegration.X,
chkDiscordIntegration.Bottom + 12, 0, 0);
chkDiscordIntegration.Bottom + 9, 0, 0);
chkAllowGameInvitesFromFriendsOnly.Text = "Only receive game invitations from friends".L10N("Client:DTAConfig:FriendsOnly");

AddChild(chkAllowGameInvitesFromFriendsOnly);
Expand All @@ -167,17 +170,35 @@ private void InitOptions()
chkSteamIntegration.Name = nameof(chkSteamIntegration);
chkSteamIntegration.ClientRectangle = new Rectangle(
chkAllowGameInvitesFromFriendsOnly.X,
chkAllowGameInvitesFromFriendsOnly.Bottom + 12, 0, 0);
chkAllowGameInvitesFromFriendsOnly.Bottom + 9, 0, 0);
chkSteamIntegration.Text = "Show the game being played in Steam".L10N("Client:DTAConfig:SteamStatus");

AddChild(chkSteamIntegration);

chkUseLegacyTunnels = new XNAClientCheckBox(WindowManager);
chkUseLegacyTunnels.Name = nameof(chkUseLegacyTunnels);
chkUseLegacyTunnels.ClientRectangle = new Rectangle(
chkSteamIntegration.X,
chkSteamIntegration.Bottom + 9, 0, 0);
chkUseLegacyTunnels.Text = "Use legacy tunnels when hosting".L10N("Client:DTAConfig:LegacyTunnels");
chkUseLegacyTunnels.CheckedChanged += ChkUseLegacyTunnels_CheckedChanged;

AddChild(chkUseLegacyTunnels);

chkUseDynamicTunnels = new XNAClientCheckBox(WindowManager);
chkUseDynamicTunnels.Name = nameof(chkUseDynamicTunnels);
chkUseDynamicTunnels.ClientRectangle = new Rectangle(
chkUseLegacyTunnels.X,
chkUseLegacyTunnels.Bottom + 9, 0, 0);
chkUseDynamicTunnels.Text = "Use dynamic tunnels when hosting".L10N("Client:DTAConfig:DynamicTunnels");
AddChild(chkUseDynamicTunnels);
}

private void InitAllowPrivateMessagesFromDropdown()
{
XNALabel lblAllPrivateMessagesFrom = new XNALabel(WindowManager);
lblAllPrivateMessagesFrom.Name = nameof(lblAllPrivateMessagesFrom);
lblAllPrivateMessagesFrom.Text = "Allow Private Messages From:".L10N("Client:DTAConfig:AllowPMFrom");
lblAllPrivateMessagesFrom.Text = "Allow private messages from:".L10N("Client:DTAConfig:AllowPMFrom");
lblAllPrivateMessagesFrom.ClientRectangle = new Rectangle(
chkDisablePrivateMessagePopup.X,
chkDisablePrivateMessagePopup.Bottom + 12, 165, 0);
Expand All @@ -187,8 +208,8 @@ private void InitAllowPrivateMessagesFromDropdown()
ddAllowPrivateMessagesFrom = new XNAClientDropDown(WindowManager);
ddAllowPrivateMessagesFrom.Name = nameof(ddAllowPrivateMessagesFrom);
ddAllowPrivateMessagesFrom.ClientRectangle = new Rectangle(
lblAllPrivateMessagesFrom.Right,
lblAllPrivateMessagesFrom.Y - 2, 110, 0);
lblAllPrivateMessagesFrom.Right - 110,
lblAllPrivateMessagesFrom.Y + 22, 110, 0);

ddAllowPrivateMessagesFrom.AddItem(new XNADropDownItem()
{
Expand Down Expand Up @@ -298,6 +319,11 @@ private void InitGameListPanel()
}
}

private void ChkUseLegacyTunnels_CheckedChanged(object sender, EventArgs e)
{
chkUseDynamicTunnels.AllowChecking = !chkUseLegacyTunnels.Checked;
}

private void ChkSkipLoginWindow_CheckedChanged(object sender, EventArgs e)
{
CheckConnectOnStartupAllowance();
Expand Down Expand Up @@ -334,6 +360,8 @@ public override void Load()
chkSkipLoginWindow.Checked = IniSettings.SkipConnectDialog;
chkPersistentMode.Checked = IniSettings.PersistentMode;
chkSteamIntegration.Checked = IniSettings.SteamIntegration;
chkUseLegacyTunnels.Checked = IniSettings.UseLegacyTunnels;
chkUseDynamicTunnels.Checked = IniSettings.UseDynamicTunnels;

chkDiscordIntegration.Checked = !ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled
&& IniSettings.DiscordIntegration;
Expand All @@ -354,6 +382,8 @@ public override void Load()

chkBox.Checked = IniSettings.IsGameFollowed(chkBox.Name);
}

ChkUseLegacyTunnels_CheckedChanged(null, EventArgs.Empty);
}

public override bool Save()
Expand All @@ -370,6 +400,8 @@ public override bool Save()
IniSettings.SkipConnectDialog.Value = chkSkipLoginWindow.Checked;
IniSettings.PersistentMode.Value = chkPersistentMode.Checked;
IniSettings.SteamIntegration.Value = chkSteamIntegration.Checked;
IniSettings.UseLegacyTunnels.Value = chkUseLegacyTunnels.Checked;
IniSettings.UseDynamicTunnels.Value = chkUseDynamicTunnels.Checked;

if (!ClientConfiguration.Instance.DiscordIntegrationGloballyDisabled)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,12 @@ public void OnJoined()
{
channel.SendCTCPMessage(FILE_HASH_CTCP_COMMAND + " " + fhc.GetCompleteHash(), QueuedMessageType.SYSTEM_MESSAGE, 10);

channel.SendCTCPMessage(TUNNEL_PING_CTCP_COMMAND + " " + tunnelHandler.CurrentTunnel.PingInMs, QueuedMessageType.SYSTEM_MESSAGE, 10);
channel.SendCTCPMessage(TUNNEL_PING_CTCP_COMMAND + " " + tunnelHandler.CurrentTunnel.Ping.Milliseconds, QueuedMessageType.SYSTEM_MESSAGE, 10);

if (tunnelHandler.CurrentTunnel.PingInMs < 0)
if (tunnelHandler.CurrentTunnel.Ping.IsUnknown())
AddNotice(string.Format("{0} - unknown ping to tunnel server.".L10N("Client:Main:PlayerUnknownPing"), ProgramConstants.PLAYERNAME));
else
AddNotice(string.Format("{0} - ping to tunnel server: {1} ms".L10N("Client:Main:PlayerPing"), ProgramConstants.PLAYERNAME, tunnelHandler.CurrentTunnel.PingInMs));
AddNotice(string.Format("{0} - ping to tunnel server: {1} ms".L10N("Client:Main:PlayerPing"), ProgramConstants.PLAYERNAME, tunnelHandler.CurrentTunnel.Ping.Milliseconds));
}

topBar.AddPrimarySwitchable(this);
Expand Down
88 changes: 44 additions & 44 deletions DXMainClient/DXGUI/Multiplayer/CnCNet/CnCNetLobby.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1595,9 +1595,49 @@ private void GameBroadcastChannel_CTCPReceived(object sender, ChannelCTCPEventAr
string mapName = splitMessage[7];
string gameMode = splitMessage[8];

string[] tunnelAddressAndPort = splitMessage[9].Split(':');
string tunnelAddress = tunnelAddressAndPort[0];
int tunnelPort = int.Parse(tunnelAddressAndPort[1]);
bool isDynamicTunnels = splitMessage[9] == "[DYN]";
CnCNetTunnel tunnel = null;
if (!isDynamicTunnels)
{
if (tunnelHandler.Tunnels.Count == 0)
{
Logger.Log("Ignoring CTCP game message because there are no tunnels at all. Available tunnel count: 0. Is the connection to CnCNet HTTP service broken?");

if (lbGameList.Items.Count == 0 && lbGameList.HostedGames.Count == 0 && !ctcpNoTunnelMessageShown)
{
ctcpNoTunnelMessageShown = true;
string message = ("There are no games listed. The client did receive a valid game message but can't add it to the list because there are no available tunnels. " +
"You can ignore this prompt if there are games listed later. Otherwise, it might indicate a network problem to CnCNet HTTP service.").L10N("Client:Main:NoTunnels");

lbChatMessages.AddMessage(new ChatMessage(Color.Gray, message));
}

return;
}

string[] tunnelAddressAndPort = splitMessage[9].Split(':');
string tunnelAddress = tunnelAddressAndPort[0];
int tunnelPort = int.Parse(tunnelAddressAndPort[1]);
tunnel = tunnelHandler.Tunnels.Find(t => t.Address == tunnelAddress && t.Port == tunnelPort);

if (tunnel == null)
{
Logger.Log(string.Format("Ignoring CTCP game message because the specified tunnel {0}:{1} is not available. Available tunnel count: {2}",
tunnelAddress, tunnelPort, tunnelHandler.Tunnels.Count));

if (lbGameList.Items.Count == 0 && lbGameList.HostedGames.Count == 0 && !ctcpNoTunnelForGamesMessageShown)
{
ctcpNoTunnelForGamesMessageShown = true;

string message = string.Format(("There are no games listed. The client did receive a valid game message but can't add it to the list because the specified tunnel is not available. " +
"You can ignore this prompt if there are games listed later. Otherwise, please contact support at {0}.").L10N("Client:Main:NoTunnelForGames"), ClientConfiguration.Instance.LongSupportURL);

lbChatMessages.AddMessage(new ChatMessage(Color.Gray, message));
}

return;
}
}

string loadedGameId = splitMessage[10];
int skillLevel = int.Parse(splitMessage[11]);
Expand Down Expand Up @@ -1652,48 +1692,8 @@ private void GameBroadcastChannel_CTCPReceived(object sender, ChannelCTCPEventAr
if (cncnetGame == null)
return;

// Find the tunnel server specified in the game message

if (tunnelHandler.Tunnels.Count == 0)
{
Logger.Log("Ignoring CTCP game message because there are no tunnels at all. Available tunnel count: 0. Is the connection to CnCNet HTTP service broken?");

// Remind users that the game is ignored because of no tunnel
if (lbGameList.Items.Count == 0 && lbGameList.HostedGames.Count == 0 && !ctcpNoTunnelMessageShown)
{
ctcpNoTunnelMessageShown = true;
string message = ("There are no games listed. The client did receive a valid game message but can't add it to the list because there are no available tunnels. " +
"You can ignore this prompt if there are games listed later. Otherwise, it might indicate a network problem to CnCNet HTTP service.").L10N("Client:Main:NoTunnels");

lbChatMessages.AddMessage(new ChatMessage(Color.Gray, message));
}

return;
}

CnCNetTunnel tunnel = tunnelHandler.Tunnels.Find(t => t.Address == tunnelAddress && t.Port == tunnelPort);

if (tunnel == null)
{
Logger.Log(string.Format("Ignoring CTCP game message because the specified tunnel {0}:{1} is not available. Available tunnel count: {2}",
tunnelAddress, tunnelPort, tunnelHandler.Tunnels.Count));

// Remind users that the game is ignored because of no specified tunnel
if (lbGameList.Items.Count == 0 && lbGameList.HostedGames.Count == 0 && !ctcpNoTunnelForGamesMessageShown)
{
ctcpNoTunnelForGamesMessageShown = true;

string message = string.Format(("There are no games listed. The client did receive a valid game message but can't add it to the list because the specified tunnel is not available. " +
"You can ignore this prompt if there are games listed later. Otherwise, please contact support at {0}.").L10N("Client:Main:NoTunnelForGames"), ClientConfiguration.Instance.LongSupportURL);

lbChatMessages.AddMessage(new ChatMessage(Color.Gray, message));
}

return;
}

HostedCnCNetGame game = new HostedCnCNetGame(gameRoomChannelName, revision, gameVersion, maxPlayers,
gameRoomDisplayName, isCustomPassword, true, players,
gameRoomDisplayName, isCustomPassword, !isDynamicTunnels, players,
e.UserName, mapName, gameMode, mapHash);
game.IsLoadedGame = isLoadedGame;
game.MatchID = loadedGameId;
Expand Down
Loading
Loading