Skip to content
Permalink
Browse files

Initial implementation of two way ping

  • Loading branch information...
UnknownShadow200 committed Jun 21, 2017
1 parent 2be686e commit 8d2d96bc38fafbe5ecfb8607ed50b158390e62b8
@@ -1781,8 +1781,10 @@ static void EmotesHandler(Player player, CommandReader cmd)
player.Message(" (Use &H/TPP X Y Z R L&S or &H/TPP {0}&S)", info.Name);
}
}
if (target != null && target.PingList.Any())
player.Message(" Ping: {0}ms Avg: {1}ms", target.PingList[9], target.PingList.Average());

if (target != null && target.AveragePingMilliseconds() != 0) {
player.Message(target.FormatPing());
}
}

#endregion
@@ -2093,24 +2095,26 @@ static void clpHandler(Player player, CommandReader cmd)

static void PingListHandler(Player player, CommandReader cmd) {
string offsetStr = cmd.Next();
string value;
int offset = 0;
if (!int.TryParse(offsetStr, out offset)) {
offset = 0;
}
Player[] visiblePlayers = Server.Players.Where(p => p.PingList.Average() != 0 && player.CanSee(p)).OrderBy(p => p.PingList.Average()).Reverse().ToArray();
if (visiblePlayers.Count() < 1) {
player.Message("No players online right now");
if (!int.TryParse(offsetStr, out offset)) offset = 0;

Player[] candidates = Server.Players.CanBeSeen(player)
.Where(p => p.AveragePingMilliseconds() != 0)
.OrderBy(p => p.AveragePingMilliseconds()).Reverse().ToArray();
if (candidates.Length < 1) {
player.Message("No online players have clients supporting measuring ping.");
return;
}
Player[] playerList = visiblePlayers.Skip(fixOffset(offset, visiblePlayers.Count())).Take(10).ToArray();
int pad = string.Format("Ping: {0}ms Avg: {1:N0}ms", playerList[0].PingList[9], playerList[0].PingList.Average()).Length;

Player[] list = candidates.Skip(fixOffset(offset, candidates.Count())).Take(10).ToArray();
int pad = list[0].FormatPing().Length;
player.Message("Ping/Latency List:");
for (int i = 0; i < playerList.Count(); i++) {
value = string.Format("Ping: {0}ms Avg: {1:N0}ms", playerList[i].PingList[9], playerList[i].PingList.Average());
player.Message(" &7{1}&S - {0}", playerList[i].Info.ClassyName, value.PadLeft(pad, '0'));

for (int i = 0; i < list.Length; i++) {
player.Message(" &7{1}&S - {0}", list[i].Info.ClassyName,
list[i].FormatPing().PadLeft(pad, '0'));
}
player.Message("Showing players {0}-{1} (out of {2}).", offset + 1, offset + playerList.Length, visiblePlayers.Count());
player.Message("Showing players {0}-{1} (out of {2}).", offset + 1, offset + list.Length, candidates.Count());
}

static int fixOffset(int origOffset, int allPlayerCount) {
@@ -98,6 +98,9 @@ public enum CpeExt

/// <summary> Allows for setting entity properties, such as rotation on X/Z axis. </summary>
EntityProperty,

/// <summary> Allows both client and server to measure average ping. </summary>
TwoWayPing,
}


@@ -136,6 +136,9 @@ public enum OpCode

/// <summary> Packet telling the client to update an aspect of the given entity. </summary>
SetEntityProperty = 42,

/// <summary> This extension allows both client and server to measure average ping. </summary>
TwoWayPing = 43,
}
}

@@ -350,5 +350,14 @@ public static Packet HackControl(bool flying, bool noclip, bool speedhack, bool
WriteI32( value, packet.Bytes, 3 );
return packet;
}


[Pure]
public static Packet MakeTwoWayPing( bool serverToClient, ushort data ) {
Packet packet = new Packet( OpCode.TwoWayPing );
packet.Bytes[1] = (byte)(serverToClient ? 1 : 0);
WriteU16( data, packet.Bytes, 2 );
return packet;
}
}
}
@@ -305,6 +305,7 @@ public partial struct Packet {
65, // SetEnvMapUrl
6, // SetEnvMapProperty
7, // SetEntityProperty
4, // TwoWayPing
};
}

@@ -367,6 +367,9 @@ public class Players {
case EntityPropertyExtName:
if (version == 1) ext = CpeExt.EntityProperty;
break;
case TwoWayPingExtName:
if (version == 1) ext = CpeExt.TwoWayPing;
break;
}
if (ext != CpeExt.None)
supportedExts.Add(ext);
@@ -24,7 +24,6 @@ public sealed partial class Player {
const int SocketPollInterval = 100; // multiples of SleepDelay, approx. 1 second
const int PingInterval = 1; // multiples of SocketPollInterval, approx. 3 seconds
public DateTime LastZoneNotification = DateTime.UtcNow;
public Stopwatch pingTimer = new Stopwatch();

static Player() {
MaxBlockPlacementRange = 32767;
@@ -156,11 +155,15 @@ public sealed partial class Player {
LeaveReason = LeaveReason.ClientQuit;
return;
}

if( pingCounter > PingInterval ) {
writer.Write( OpCode.Ping );
pingTimer.Reset();
pingTimer.Start();
BytesSent++;
if( Supports( CpeExt.TwoWayPing ) ) {
SendNow( Packet.MakeTwoWayPing( true, NextTwoWayPingData() ) );
} else {
writer.Write( OpCode.Ping );
BytesSent++;
}

pingCounter = 0;
MeasureBandwidthUseRates();
}
@@ -273,6 +276,10 @@ public sealed partial class Player {
case OpCode.PlayerClick:
ProcessPlayerClickPacket();
break;

case OpCode.TwoWayPing:
ProcessTwoWayPingPacket();
break;

case OpCode.Ping:
ProcessPingPacket();
@@ -313,21 +320,29 @@ public sealed partial class Player {
}
}
#endregion

ushort NextTwoWayPingData() {
// Find free ping slot
for( int i = 0; i < PingList.Length; i++ ) {
if( PingList[i].TimeSent.Ticks == 0 ) continue;

ushort prev = i > 0 ? PingList[i - 1].Data : (ushort)0;
PingList[i].Data = (ushort)(prev + 1);
PingList[i].TimeSent = DateTime.UtcNow;
return (ushort)(prev + 1);
}

// Remove oldest ping slot
for( int i = 0; i < PingList.Length - 1; i++ ) {
PingList[i] = PingList[i + 1];
}

PingList[PingList.Length - 1].Data++;
PingList[PingList.Length - 1].TimeSent = DateTime.UtcNow;
return PingList[PingList.Length - 1].Data;
}

public void ProcessPingPacket() {
pingTimer.Stop();
int total = 0;
for( int i = 0; i < 10; i++) {
if (i == 9) {
PingList[i] = (int)pingTimer.ElapsedMilliseconds;
} else {
PingList[i] = PingList[i + 1];
}
total += PingList[i];
}
if (!IsPlayingCTF) {
Message((byte)MessageType.BottomRight3, "&SPing: &f{0}&Sms Avg: &f{1}&Sms", PingList[9], PingList.Average());
}
void ProcessPingPacket() {
BytesReceived++;
}

@@ -518,7 +533,7 @@ public sealed partial class Player {
}

PlaceBlockWithEvents( coords, action, (Block)type );
}
}

void ProcessPlayerClickPacket() {
BytesReceived += 15;
@@ -532,6 +547,25 @@ public sealed partial class Player {
short targetBlockZ = reader.ReadInt16();
byte targetBlockFace = reader.ReadByte();
}

void ProcessTwoWayPingPacket() {
bool serverToClient = reader.ReadByte() != 0;
ushort data = reader.ReadUInt16();
BytesReceived += 3;

// Client-> server ping, immediately reply.
if( !serverToClient ) {
SendNow( Packet.MakeTwoWayPing( false, data ) );
return;
}

// Got a response for a server->client ping, set the time received.
for( int i = 0; i < PingList.Length; i++ ) {
if( PingList[i].Data != data ) continue;
PingList[i].TimeReceived = DateTime.UtcNow;
break;
}
}


void Disconnect() {
@@ -164,7 +164,12 @@ public sealed partial class Player : IClassy {
internal int currentBDStep = -1;
internal BlockDefinition currentBD;

public int[] PingList = new int[10];
public struct Ping {
public DateTime TimeSent, TimeReceived;
public ushort Data;
}
public Ping[] PingList = new Ping[20];

public string LastMotdMessage;
public Player LastPlayerGreeted = Player.Console;

@@ -2142,6 +2147,7 @@ public void ResetIdleTimer()
const string EnvMapAspectExtName = "EnvMapAspect";
const string ExtPlayerPositionsExtName = "ExtEntityPositions";
const string EntityPropertyExtName = "EntityProperty";
const string TwoWayPingExtName = "TwoWayPing";

bool supportsBlockDefs, supportsCustomBlocks;
internal bool supportsExtPositions;
@@ -2250,7 +2256,43 @@ public void ResetIdleTimer()
}

#endregion




/// <summary> Gets average ping in milliseconds, or 0 if no ping measures. </summary>
public double AveragePingMilliseconds() {
double totalMs = 0;
int measures = 0;

foreach (Ping ping in PingList) {
if (ping.TimeSent.Ticks == 0 || ping.TimeReceived.Ticks == 0) continue;

totalMs += (ping.TimeReceived - ping.TimeSent).TotalMilliseconds;
measures++;
}
return measures == 0 ? 0 : (totalMs / measures);
}


/// <summary> Gets worst ping in milliseconds, or 0 if no ping measures. </summary>
public double WorstPingMilliseconds() {
double totalMs = 0;

foreach (Ping ping in PingList) {
if (ping.TimeSent.Ticks == 0 || ping.TimeReceived.Ticks == 0) continue;

double ms = (ping.TimeReceived - ping.TimeSent).TotalMilliseconds;
totalMs = Math.Max(totalMs, ms);
}
return totalMs;
}

public string FormatPing() {
return String.Format(" Worst ping {0}ms, average {1}ms",
WorstPingMilliseconds().ToString("N0"),
AveragePingMilliseconds().ToString("N0"));
}


[CanBeNull]
public string LastUsedPlayerName { get; set; }

0 comments on commit 8d2d96b

Please sign in to comment.
You can’t perform that action at this time.