Add support for V3 tunnels#845
Conversation
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
|
Nightly build for this pull request:
|
|
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 |
Fix V2 tunnels not matching
| int received = udpClient.Client.ReceiveFrom(receiveBuffer, ref remoteEndpoint); | ||
| var receivedTime = Stopwatch.GetTimestamp(); | ||
|
|
||
| if (_endpointToTunnel.TryGetValue((IPEndPoint)remoteEndpoint, out var tunnel)) | ||
| ProcessReceivedPacket(receiveBuffer.AsMemory(0, received), receivedTime, tunnel); | ||
| else if (_pendingStunQueries.TryRemove((IPEndPoint)remoteEndpoint, out var stunTcs)) | ||
| { | ||
| var stunData = new byte[received]; | ||
| Buffer.BlockCopy(receiveBuffer, 0, stunData, 0, received); | ||
| stunTcs.TrySetResult(stunData); | ||
| } | ||
| else if (!TryAutoLearnAndDispatch(receiveBuffer.AsMemory(0, received), receivedTime, (IPEndPoint)remoteEndpoint)) | ||
| Logger.Log($"V3TunnelCommunicator: Received packet from unknown endpoint: {remoteEndpoint}"); |
| /// <summary> | ||
| /// Returns a string representation of this ping value. | ||
| /// Format: "50" for valid pings, or localized "Unknown" for invalid pings. | ||
| /// Note: Does not include the "ms" unit - callers should append the localized unit. | ||
| /// </summary> |
umm, what if the player is indeed offline suddenly? could be better if we can show Unknown ping after several attempts (or maybe leave it as another PR) |
| else | ||
| { | ||
| connection.QueueMessage(QueuedMessageType.SYSTEM_MESSAGE, 9, "PART " + ChannelName); | ||
| connection.QueueMessage(QueuedMessageType.INSTANT_MESSAGE, 0, "PART " + ChannelName); |
| tunnelHandler.CurrentTunnel = tunnel; | ||
| AddNotice(string.Format("The game host has changed the tunnel server to: {0}".L10N("Client:Main:HostChangeTunnel"), tunnel.Name)); | ||
|
|
||
| foreach (PlayerInfo pInfo in Players) | ||
| { | ||
| pInfo.Ping = -1; | ||
| UpdatePlayerPingIndicator(pInfo); | ||
| } | ||
|
|
||
| CopyPlayerDataToUI(); | ||
| UpdatePing(); | ||
|
|
- Fix negotiated auto-learned P2P paths breaking in-game - Fix the game tunnel bridge thread dying permanently on transient socket errors - Fix tunnel-choice split brain after a failed P2P upgrade - Fix TunnelAck ambiguity - Fix pair status masking - Fix stale-packet corruption from overlapping renegotiation rounds - Fix disposed negotiators raising spurious failure/complete events - Fix a race where fire-and-forget ping tasks could touch the negotiator's disposed CancellationTokenSource (token now cached) - Fix V2 regression: STARTV2 carries the real tunnel address again and joiners re-sync their tunnel from it - Restrict TunnelFailed to two consecutive bad pings - Add 15-second lobby keepalives on negotiated relay tunnels and P2P paths so NAT mappings and tunnel registrations survive - Marshal negotiator events to the game thread, fixing the render-target NullReferenceException when negotiations completed
- Add a Renegotiate All debounce - Add failure notices for every pair transition to Failed (deduplicated), so the host now hears about failures between other players too - Add a renegotiate hint to the launch-blocked message and auto-open the negotiation status panel - Add visible error notices when a client can't process the host's STARTV3 message
- Rebuild the panel list view as a scrolling multicolumn listbox with ping bars - Report the full round-trip time between players instead of half - Prevent lobby ping icons flickering - Sort the list view worst-first - Align the panel's ping colors/bars with the lobby ping icon tiers - Center the "Tunnel Negotiation Status" panel title - Hide the local player's ping icon in dynamic mode - Show launch-blocked negotiation statuses as readable text - Add a host-only suggestion when kicking one player would significantly reduce the worst connection ping - Log the stale-failure-notification burst once per negotiator instead of once per tunnel
… up to date. Add a pre-launch connectivity check Fix duplicate L10N key Snapshot remote endpoints before storing them as P2P routing keys Reset player pings to unknown when host changes tunnel in V2 or V3 static mode.
Fix some nullable issues
Fix L10N strings
Fix game starting with null tunnel in v3 static mode when tunnel doesn't exist locally
Fix issue renegotiating with a player in game Probe all users in the pre-launch connectivity verification
Reset tunnelErrorMode on lobby setup/clear Set pings to unkown when TNLPNG -1 arrives Change tunnel picker to address:port instead of just address Make TunnelEventArgs.Tunnel nullable Make ApplyV3StartEntry fail if missing V3PlayerInfo record(s) Dedupe P2P candidates
Fix layout issue with the listbox in the negotiation status panel
Not entirely sure what you mean - it's a ping indicator, not a disconnect indicator. The client was incorrectly setting them to unknown as well - like when a player joins the lobby it resets everyone's to unknown. They could also be unknown from the "Only ping official tunnels" setting. There were some errors with the change I made though - corrected now. Had to add a keep alive thing to keep the NAT mapped, so a lot of new code in the latest commits unfortunately. Good news is I've tested just about every scenario I can think of and it's all working brilliantly. |
| else | ||
| { | ||
| connection.QueueMessage(QueuedMessageType.SYSTEM_MESSAGE, 9, "PART " + ChannelName); | ||
| connection.QueueMessage(QueuedMessageType.INSTANT_MESSAGE, 0, "PART " + ChannelName); | ||
| } |
| private void EvaluateTunnelHealth(CnCNetTunnel tunnel) | ||
| { | ||
| bool pingBad = tunnel.Ping.IsUnknown() || tunnel.Ping.Milliseconds > TUNNEL_FAILED_PING_AMOUNT; | ||
|
|
||
| if (!pingBad) |
| return; | ||
|
|
||
| XNAListBoxItem lbItem = GetItem(2, filteredIndex); | ||
| XNAListBoxItem lbItem = GetItem(3, filteredIndex); |
There was a problem hiding this comment.
Why this magic number has been changed?
|
|
||
|
|
|
|
||
| /// <summary> | ||
| /// This tracks what each player reports about their negotiation with each other player | ||
| /// // reportingPlayer -> targetPlayer -> ping |
There was a problem hiding this comment.
| /// // reportingPlayer -> targetPlayer -> ping | |
| /// reportingPlayer -> targetPlayer -> ping |
|
|
||
|
|
| else | ||
| return $"Negotiations: {succeeded}/{total} complete"; | ||
| } | ||
| } No newline at end of file |
Closes #349
How V3 Tunnels Work
In V2, all players share a single tunnel.
Example:
In V3 dynamic mode, each pair of players negotiates the best tunnel route:
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 or the tunnel mode changes, and takes a few seconds to run once the player list has arrived.
Phase 1 - Relay Negotiation
Which tunnels are tested is controlled by the Ping unofficial CnCNet tunnels option - when off, only official and recommended tunnels are used.
Phase 2 - P2P Upgrade Round (optional; requires both players to have P2P enabled)
After a relay tunnel is agreed: