Skip to content

Conversation

@11EJDE11
Copy link
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 higher 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

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
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
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
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.

tunnel.PingInMs = pingResult;
}

if (previousPing > 0 && (tunnel.PingInMs <= 0 || tunnel.PingInMs > TUNNEL_FAILED_PING_AMOUNT))
Copy link
Member

Choose a reason for hiding this comment

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

Does 0 ms indicate a failure for now? Would be better using -1, in case some one runs a tunnel server AND plays the game, so the distance between tunnel server and the gaming computer is so close

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

Choose a reason for hiding this comment

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

@11EJDE11

public override int Ping => TunnelServer?.PingInMs ?? 0;

0 or -1?

Copy link
Member

Choose a reason for hiding this comment

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

also previousPing > 0

Copy link
Member

Choose a reason for hiding this comment

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

I suggest fully go through the code to ensure the ping value successfully deals with 0 and -1. Alternatively to simplify the procedure and reduce error you might also want to wrap the ping int as a readonly record (so we can use something like ping.IsValid()), if needed

Comment on lines 406 to 407
if (GameTunnelBridge != null)
StopGameBridge();
Copy link
Member

Choose a reason for hiding this comment

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

is the if statement necessary?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've removed that. Thanks.

/// <summary>
/// Retrieves the <see cref="TunnelTestResult"/> for the specified tunnel, or null if not found.
/// </summary>
public TunnelTestResult GetTunnelResult(CnCNetTunnel tunnel) => TunnelResults.TryGetValue(tunnel, out var result) ? result : null;
Copy link
Member

Choose a reason for hiding this comment

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

Just wondering why you didn't continue using nullable syntax.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

public double GetBestPing()
{
var best = SelectBestTunnel();
return best != null ? TunnelResults[best].AverageRtt : double.NaN;
Copy link
Member

Choose a reason for hiding this comment

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

Why not using Double.MaxValue? It seems that any comparison involving NaN (e.g., NaN > 5, NaN < 5, NaN == NaN) will result in false

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, I've changed that.

_localPlayer.Id,
_remotePlayer.Id,
TunnelPacketType.PingRequest,
BitConverter.GetBytes(i)
Copy link
Member

Choose a reason for hiding this comment

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

Suggest crash if BitConverter.IsLittleEndian does not match our expected value, in case some extremely rare and weird machines break the protocol

I saw you are using BinaryPrimitives later. Maybe it is better than the machine-specific BitConverter methods here

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call, sorted now thanks.

_negotiationCts.Dispose();
_tunnelAckReceived.TrySetCanceled();
_negotiationCompletionSource.TrySetCanceled();
_tunnelHandler.UnregisterV3PacketHandler(_localPlayer.Id, _remotePlayer.Id);
Copy link
Member

Choose a reason for hiding this comment

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

You have used _tunnelHandler?UnregisterV3PacketHandler syntax but here you assume the field is not null. Perhaps it's better to enable nullable for all new files.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, sorted.

@MahBoiDeveloper
Copy link
Member

Checked PR with 2 players in lobby total. YR config works fine, have no issues.

@11EJDE11
Copy link
Member Author

That's good to hear, and thanks for testing!

@SadPencil
Copy link
Member

SadPencil commented Nov 23, 2025

@11EJDE11 I suggest fully go through the code to ensure we successfully deal with 0 and -1 ping values. Currently there seems to be some mix usages between 0 and -1, e.g., previousPing > 0 and TunnelServer?.PingInMs ?? 0.

Alternatively to simplify the procedure and reduce error you might also want to wrap the ping int as a readonly record (so we can use something like ping.IsValid() to enhance readability), if needed

@11EJDE11
Copy link
Member Author

Sure, that makes sense, SP. I will take another look in the next day or two.

@11EJDE11
Copy link
Member Author

11EJDE11 commented Dec 2, 2025

@SadPencil, is the below what you had envisioned? If so, I will update the lobbies and players to all use this. There will be minor changes to a lot of files. If not, I can stick to int and there would be fewer changes required. Either is OK with me although I do prefer below.

public readonly record struct PingValue
{
    private readonly int _value;

    private PingValue(int value) => _value = value;

    /// <summary>
    /// An unknown or invalid ping measurement.
    /// </summary>
    public static PingValue Unknown => new(-1);

    /// <summary>
    /// Creates a PingValue from a millisecond amount.
    /// </summary>
    /// <param name="milliseconds">The ping in milliseconds. Must be non-negative.</param>
    /// <returns>A valid PingValue.</returns>
    /// <exception cref="ArgumentException">Thrown when milliseconds is negative.</exception>
    public static PingValue FromMs(int milliseconds)
    {
        if (milliseconds < 0)
            throw new ArgumentException("Ping cannot be negative. Use PingValue.Unknown for unknown pings.", nameof(milliseconds));
        return new(milliseconds);
    }

    /// <summary>
    /// Returns true if this ping value is valid (not unknown).
    /// </summary>
    public bool IsValid() => _value >= 0;

    /// <summary>
    /// Returns true if this ping value is unknown or invalid.
    /// </summary>
    public bool IsUnknown() => _value < 0;

    /// <summary>
    /// Gets the ping in milliseconds.
    /// Returns -1 for unknown pings.
    /// Prefer using IsValid() to check validity before accessing this value.
    /// </summary>
    public int Milliseconds => _value;

    /// <summary>
    /// Returns a string representation of theping value.
    /// </summary>
    public override string ToString() => IsValid() ? $"{_value}ms" : "Unknown";
}

@SadPencil
Copy link
Member

Yes!

Also, it's "0 ms" instead of "0ms" in your ToString implementation

@11EJDE11 11EJDE11 requested a review from SadPencil December 5, 2025 02:22
Copy link
Member

@Rampastring Rampastring left a comment

Choose a reason for hiding this comment

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

Reviewed up to V3PlayerInfo.cs, I'll have to continue another time.

The status panel seems to have a flaw regarding TextAnchor usage, otherwise I have only found stylistical issues so far. Overall it looks like good work.

0,
CELL_WIDTH,
HEADER_HEIGHT);
headerLabel.TextAnchor = LabelTextAnchorInfo.CENTER;
Copy link
Member

Choose a reason for hiding this comment

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

If you set TextAnchor, you should also set AnchorPoint. The intention is probably centering the names on the cells - based on the screenshot that doesn't happen right now, and it would be an improvement to the interface.

See https://github.com/Rampastring/Rampastring.XNAUI/blob/master/XNAControls/XNALabel.cs#L75

Copy link
Member Author

Choose a reason for hiding this comment

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

SearchAllGameModes = new BoolSetting(iniFile, MULTIPLAYER, "SearchAllGameModes", false);

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

Choose a reason for hiding this comment

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

Since these are mutually exclusive, making a single enum based (integer or string) TunnelMode setting instead could be a worthy idea. But this is OK too - doesn't significantly increase code complexity.

public const string QRES_EXECUTABLE = "qres.dat";

public const string CNCNET_PROTOCOL_REVISION = "R13";
public const string CNCNET_PROTOCOL_REVISION = "R15";
Copy link
Member

Choose a reason for hiding this comment

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

We could save a character from GameSurge traffic by using alphabetics for the number instead.

RF for example. We could also drop the R to save two bytes - it used to be short for Rampastring to differentiate my protocol from Funky's client, but I don't think that's a major consideration anymore.

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 16 to 17
// reportingPlayer -> targetPlayer -> status
// This tracks what each player reports about their negotiation with each other player
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason these aren't /// <summary> type comments? It'd make IntelliSense pick them up and show them when you hover over the variable anywhere in code.

Same for _playerPingMatrix.

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 50 to 51
if (player1 == player2)
return NegotiationStatus.NotStarted;
Copy link
Member

Choose a reason for hiding this comment

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

Can or should this ever happen? Should this be a condition for an exception instead for catching potential bugs?

Copy link
Member Author

Choose a reason for hiding this comment

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

It won't happen, but never know what people will feed it in future. Changed 5b9f62b

Comment on lines 246 to 248
if (CurrentTunnel == null && otherPreviousPing.IsValid() &&
(pingResult.IsUnknown() || pingResult.Milliseconds > TUNNEL_FAILED_PING_AMOUNT))
TunnelFailed?.Invoke(this, otherTunnel);
Copy link
Member

Choose a reason for hiding this comment

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

From the contribution guideline (Contributing.md):

Braceless code block bodies should be made only when both code block head and body are single line.

Suggested change
if (CurrentTunnel == null && otherPreviousPing.IsValid() &&
(pingResult.IsUnknown() || pingResult.Milliseconds > TUNNEL_FAILED_PING_AMOUNT))
TunnelFailed?.Invoke(this, otherTunnel);
if (CurrentTunnel == null && otherPreviousPing.IsValid() &&
(pingResult.IsUnknown() || pingResult.Milliseconds > TUNNEL_FAILED_PING_AMOUNT))
{
TunnelFailed?.Invoke(this, otherTunnel);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 68 to 70
foreach (var player in _otherPlayers)
if (player.Tunnel != null)
Logger.Log($" Player {player.Name}: {player.Tunnel.Address}:{player.Tunnel.Port}");
Copy link
Member

Choose a reason for hiding this comment

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

From the contribution guideline:

nested braceless blocks are not allowed within braceless blocks

Suggested change
foreach (var player in _otherPlayers)
if (player.Tunnel != null)
Logger.Log($" Player {player.Name}: {player.Tunnel.Address}:{player.Tunnel.Port}");
foreach (var player in _otherPlayers)
{
if (player.Tunnel != null)
Logger.Log($" Player {player.Name}: {player.Tunnel.Address}:{player.Tunnel.Port}");
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 81 to 82
/// Stops the game tunnel bridge, unregistering packet handlers,
/// and closed the local/game UDP socket.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// Stops the game tunnel bridge, unregistering packet handlers,
/// and closed the local/game UDP socket.
/// Stops the game tunnel bridge, unregisters packet handlers,
/// and closes the local/game UDP socket.

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 154 to 156
}
_tunnelHandler.SendPacket(recipient.Tunnel, _localId, recipient.Id,
TunnelPacketType.GameData, gameData);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
}
_tunnelHandler.SendPacket(recipient.Tunnel, _localId, recipient.Id,
TunnelPacketType.GameData, gameData);
}
_tunnelHandler.SendPacket(recipient.Tunnel, _localId, recipient.Id,
TunnelPacketType.GameData, gameData);

Copy link
Member Author

Choose a reason for hiding this comment

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

Comment on lines 158 to 159
else
Logger.Log($"V3GameTunnelBridge: No matching recipient found for receiverId={receiverId}");
Copy link
Member

Choose a reason for hiding this comment

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

From the contribution guidelines:

If you use if-else you should either have all of the code blocks braced or braceless to keep things consistent.

Suggested change
else
Logger.Log($"V3GameTunnelBridge: No matching recipient found for receiverId={receiverId}");
else
{
Logger.Log($"V3GameTunnelBridge: No matching recipient found for receiverId={receiverId}");
}

Copy link
Member Author

Choose a reason for hiding this comment

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

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

5 participants