Skip to content

Add support for V3 tunnels#845

Open
11EJDE11 wants to merge 101 commits into
CnCNet:developfrom
11EJDE11:new-v3-tunnels
Open

Add support for V3 tunnels#845
11EJDE11 wants to merge 101 commits into
CnCNet:developfrom
11EJDE11:new-v3-tunnels

Conversation

@11EJDE11
Copy link
Copy Markdown
Member

@11EJDE11 11EJDE11 commented Oct 11, 2025

Closes #349

  • V2 tunnels remain fully supported.
  • Adds V3 Static tunnels: One host-chosen tunnel shared by all players (similar to V2).
  • Adds V3 Dynamic tunnels: Each pair of players automatically negotiates the optimal tunnel between them.

New UserINISettings

Setting Description
UseLegacyTunnels If on: games use V2 tunnels (overrides UseDynamicTunnels).
If off: games use V3 tunnels.
UseDynamicTunnels If on: lobbies start with no predefined tunnel; tunnel negotiations occurs as players join.
If off: games use static V3 tunnels (unless legacy mode is enabled).

New Lobby Commands

Command Description
/tunnelmode [<mode>] Cycles through tunnel modes or sets one explicitly.
Modes:
0 - V3 (static)
1 - V3 (dynamic)
2 - V2 (legacy)
/negstatus Opens the negotiation status panel, showing per-pair negotiation progress and measured pings.
/tunnelinfo Modified to display the current tunnel version. Doesn't display info when dynamic tunnels are enabled.

New CTCP Messages

Command Description
PLYTNL <address>:<port> Announces which tunnel a player pair is using (sent by the decider).
NEGINFO <target_player>;status[;<ping>] Broadcasts negotiation progress and ping results.
TNLRENEG <failed_address>:<failed_port> Requests renegotiation if a tunnel fails in dynamic mode.
TNLFAIL <tunnel_name> Reports a failed tunnel to other players.
STARTV2 / STARTV3 START has been versioned into STARTV2 and STARTV3.

How V3 Tunnels Work

In V2, all players share a single tunnel.
Example:

Player1 (Australia)
Player2 (New Zealand)
Player3 (England)
Tunnel: France

For P1 to send data to P2:
P1 Australia -> Tunnel France -> P2 NZ  = 550ms

In V3 dynamic mode, each pair of players negotiates the best tunnel route:

P1 <--> P2  -> Tunnel: Australia (50ms)
P1 <--> P3  -> Tunnel: France (300ms)
P2 <--> P3  -> Tunnel: Seattle (310ms)
So now for P1 to send data to P2:
P1 Australia > Tunnel Australia > P2 NZ = 50ms (90.9% faster)
And the slowest link is now 310ms instead of 550ms (43.6% faster overall).

The client now acts as an intermediary: the game connects to the client, which then routes packets to the negotiated tunnels. This is for both static and dynamic V3.


Negotiation Process

Negotiations occur automatically when players join a lobby and take a few seconds once the player list has arrived.

  1. Each player pair tests all available tunnels by exchanging 5 UDP pings per tunnel.
    RTT, packet loss, and average latency are measured.
  2. One player is designated as the decider (the one with the lower player ID).
    • The decider receives Connected packets and sends PingRequest packets.
    • The non-decider responds with PingResponse packets.
  3. Once 80% of tunnels have been tested, the decider picks the best tunnel and informs the non-decider.
  4. The non-decider acknowledges, completing negotiation.

If Ping unofficial CnCNet tunnels is disabled, you will only negotiate over official tunnels.


Example Log Output (truncated)

09.10. 12:18:15.170    === Negotiation Results for QWE21 (ID: 3760759627) ===
Player: QWE21 | Tunnel: CnCNet Australia | Avg RTT: 65.2ms | Real ping: 34.0ms | Real ping*2: 68.0ms | Difference: -2.8ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: CnCNet Europe | Avg RTT: 583.5ms | Real ping: 284.0ms | Real ping*2: 568.0ms | Difference: 15.5ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: CnCNet Japan | Avg RTT: 291.3ms | Real ping: 193.0ms | Real ping*2: 386.0ms | Difference: -94.7ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: CnCNet Singapore | Avg RTT: 256.3ms | Real ping: 123.0ms | Real ping*2: 246.0ms | Difference: 10.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: United-Forum.de | Avg RTT: N/A | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: "[EU] Fast Server HA Germany leardev.de" | Avg RTT: 633.7ms | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [ EJ ] N. America (California) | Avg RTT: 269.9ms | Real ping: 140.0ms | Real ping*2: 280.0ms | Difference: -10.1ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [CN][JS]CORA_SERVER | Avg RTT: 349.1ms | Real ping: 176.0ms | Real ping*2: 352.0ms | Difference: -2.9ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [CN][JS]MonkeyRay's Server #1 | Avg RTT: 371.3ms | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [CN][NBO]long_ken's Server #1 | Avg RTT: 393.3ms | Real ping: 192.0ms | Real ping*2: 384.0ms | Difference: 9.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [CN][SH]REV_v3_N | Avg RTT: 393.3ms | Real ping: 186.0ms | Real ping*2: 372.0ms | Difference: 21.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [DE]XuanServerDE | Avg RTT: 591.3ms | Real ping: 288.0ms | Real ping*2: 576.0ms | Difference: 15.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [EU] Kisiek.net | Avg RTT: 560.9ms | Real ping: 284.0ms | Real ping*2: 568.0ms | Difference: -7.1ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [EU] Z Server MP https://zserver.org | Avg RTT: 592.2ms | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [TR] CK Server | Avg RTT: 653.1ms | Real ping: 307.0ms | Real ping*2: 614.0ms | Difference: 39.1ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [TW] Jun's Tunnel Server | Avg RTT: 380.7ms | Real ping: 192.0ms | Real ping*2: 384.0ms | Difference: -3.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [UK] London | Avg RTT: 547.7ms | Real ping: 275.0ms | Real ping*2: 550.0ms | Difference: -2.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [US West] Z Server MP https://zserver.org | Avg RTT: 317.4ms | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [US-C] Bot.Rip CNC Relay | Avg RTT: 363.6ms | Real ping: 180.0ms | Real ping*2: 360.0ms | Difference: 3.6ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [US-LA]XuanServerUS-LA | Avg RTT: 289.0ms | Real ping: 140.0ms | Real ping*2: 280.0ms | Difference: 9.0ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: [US-NY]XuanServerUS-NY | Avg RTT: N/A | Real ping: 305.0ms | Real ping*2: 610.0ms | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: [US][EU] Coolissy | Avg RTT: 306.0ms | Real ping: 152.0ms | Real ping*2: 304.0ms | Difference: 2.0ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: aliyunbj.cdn.leohearts.com | Avg RTT: N/A | Real ping: 217.0ms | Real ping*2: 434.0ms | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: AsSaltJordan | Avg RTT: 694.1ms | Real ping: 347.0ms | Real ping*2: 694.0ms | Difference: 0.1ms | Packet Loss: 20.0% | Pings: 4/5 | Connected: True
Player: QWE21 | Tunnel: Bloody Eye | Avg RTT: 422.5ms | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: bot.rip-eastus | Avg RTT: 560.0ms | Real ping: 284.0ms | Real ping*2: 568.0ms | Difference: -8.0ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: ISS Server (HK) | Avg RTT: 322.6ms | Real ping: 163.0ms | Real ping*2: 326.0ms | Difference: -3.4ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: KFCV50-BeiJing-1-mindwhy.top-by-2049265547@qq.com- | Avg RTT: 416.0ms | Real ping: 205.0ms | Real ping*2: 410.0ms | Difference: 6.0ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: KFCV50-BeiJing-2-mindwhy.top-by-2049265547@qq.com- | Avg RTT: 416.0ms | Real ping: 207.0ms | Real ping*2: 414.0ms | Difference: 2.0ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: KFCV50-QingDao-mindwhy.top-by-2049265547@qq.com- | Avg RTT: N/A | Real ping: 201.0ms | Real ping*2: 402.0ms | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: Los Angeles Server | Avg RTT: 293.9ms | Real ping: 139.0ms | Real ping*2: 278.0ms | Difference: 15.9ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: MangosServer - NY | Avg RTT: 416.0ms | Real ping: 199.0ms | Real ping*2: 398.0ms | Difference: 18.0ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: Mixyzzs Server | Avg RTT: N/A | Real ping: 193.0ms | Real ping*2: 386.0ms | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: Mytunnel | Avg RTT: 23.8ms | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: Mytunnel | Avg RTT: 534.3ms | Real ping: 266.0ms | Real ping*2: 532.0ms | Difference: 2.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: PL TurnWar - turn-guild.ru | Avg RTT: 567.5ms | Real ping: 281.0ms | Real ping*2: 562.0ms | Difference: 5.5ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: RedAlert2.com the best | Avg RTT: 544.7ms | Real ping: 274.0ms | Real ping*2: 548.0ms | Difference: -3.3ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: RMMD UK Tunnel | Avg RTT: 544.9ms | Real ping: 271.0ms | Real ping*2: 542.0ms | Difference: 2.9ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: RU MSK TurnWar - turn-guild.ru | Avg RTT: 649.1ms | Real ping: 296.0ms | Real ping*2: 592.0ms | Difference: 57.1ms | Packet Loss: 0.0% | Pings: 5/5 | Connected: True
Player: QWE21 | Tunnel: Studio DNA | Avg RTT: N/A | Real ping: N/A | Real ping*2: N/A | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: Wick-Server | Avg RTT: N/A | Real ping: 196.0ms | Real ping*2: 392.0ms | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
Player: QWE21 | Tunnel: zz-tunnel | Avg RTT: N/A | Real ping: 349.0ms | Real ping*2: 698.0ms | Difference: N/A | Packet Loss: 100.0% | Pings: 0/0 | Connected: False
BEST TUNNEL for QWE21: Mytunnel (RTT: 23.8ms, Loss: 0.0%)
=== End Results for QWE21 ===

Tunnel Failure Handling

A new TunnelFailed event has been added to the TunnelHandler.

  • Dynamic tunnels:
    Automatically trigger renegotiation when a connection goes down (only affected players).

  • Static tunnels:

    • Hosts automatically select the next best tunnel if their tunnel fails.
    • Non-hosts broadcast failure messages so the host can respond (switch tunnel or kick the player).

Still to come...

  • GameLoadingLobby integration
  • Peer-to-Peer (P2P) connections
  • In-game tunnel monitoring and hot-swapping

Showing the negotiation in the lobby, and the negotiation status panel (available for all players).
image

Showing a change to the game creation window and a game's ping:
image

New settings:
image

Add V3GameTunnelBridge
Add V3TunnelCommunicator
Add V3TunnelNegotiator
Add TunnelFailed event
Update supported tunnel versions
Fixup GameCreationWindow control visibility
Remove unnecessary tunnel parameter in V3PlayerInfo
Add NegotiationStatusPanel
Add tunnel negotiations to CnCNetGameLobby
Add V3 tunnel support to CnCNetGameLobby
Move to file-scoped namespaces
Fix missing texture on status panel
Fix issue with decider seeing OK instead of ping results
Fix an issue with automatic tunnel selection
Style fixups
Split TunnelChosenEventArgs into new file
Remove IDisposable on bridge
Remove unecessary code
Better updating of player ping indicators
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Oct 11, 2025

Nightly build for this pull request:

  • artifacts.zip
    This comment is automatic and is meant to allow guests to get latest automatic builds without registering. It is updated on every successful build.

@CnCRAZER
Copy link
Copy Markdown
Contributor

Perhaps the ability to add a button within one of the clients ini files to toggle between V2 and V3 would be useful. Just a thought

@@ -0,0 +1,248 @@
using System;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write #nullable enable since you are using the nullable syntax. Same for other new files

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done now.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done now.

Please place #nullable enable in the first line

Comment thread DXMainClient/Domain/Multiplayer/CnCNet/TunnelHandler.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/TunnelHandler.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerInfo.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerInfo.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerNegotiator.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerNegotiator.cs
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3TunnelCommunicator.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 29 changed files in this pull request and generated 5 comments.

Comment thread DXMainClient/DXGUI/Multiplayer/CnCNet/GameCreationWindow.cs
Comment thread DXMainClient/DXGUI/Multiplayer/GameLobby/CnCNetGameLobby.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3TunnelCommunicator.cs
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/HostedCnCNetGame.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 30 changed files in this pull request and generated 3 comments.

Comment thread DXMainClient/DXGUI/Generic/OptionPanels/CnCNetOptionsPanel.cs
Comment thread DXMainClient/DXGUI/Multiplayer/CnCNet/TunnelSelectionWindow.cs
Comment thread DXMainClient/DXGUI/Multiplayer/GameLobby/CnCNetGameLobby.cs
11EJDE11 added 4 commits May 29, 2026 13:17
…hind a lock.

-Send negotiated packet loss in the TunnelChoice packet so both peers display it without extra IRC messages.
-Derive V3 in-game player ids from the host's start message position instead of the local player index.
-Link decider tunnel-wait timeouts to the negotiation token so they cancel on dispose.
-Disable the host launch button until all V3 dynamic negotiations succeed.
-Remove dead tunnelErrorMode field, make AddTunnels atomic, and fix V3PlayerNegotiator log prefix.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 28 out of 30 changed files in this pull request and generated 25 comments.

Comment thread DXMainClient/DXGUI/Multiplayer/GameLobby/TunnelNegotiationStatusPanel.cs Outdated
Comment thread DXMainClient/DXGUI/Multiplayer/GameLobby/CnCNetGameLobby.cs
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3TunnelCommunicator.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerNegotiator.cs
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerNegotiator.cs
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerNegotiator.cs Outdated
Comment thread DXMainClient/Domain/Multiplayer/CnCNet/V3PlayerNegotiator.cs Outdated
Comment thread ClientCore/ProgramConstants.cs
Comment thread DXMainClient/DXGUI/Multiplayer/GameLobby/CnCNetGameLobby.cs Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 31 changed files in this pull request and generated 3 comments.

else
{
connection.QueueMessage(QueuedMessageType.SYSTEM_MESSAGE, 9, "PART " + ChannelName);
connection.QueueMessage(QueuedMessageType.INSTANT_MESSAGE, 0, "PART " + ChannelName);
Comment thread ClientCore/ProgramConstants.cs
if (status == NegotiationStatus.Failed)
return "Tunnel negotiation failed".L10N("Client:Main:TunnelNegotiationFailed");

if (v3Info?.Tunnel != null && status == NegotiationStatus.Succeeded)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P2P & V3 tunnel support

6 participants