mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-22 12:05:51 -04:00
Implement support for TwoWayPing CPE extension, add a /ping command to measure player ping.
This commit is contained in:
parent
3c26d97c07
commit
35b1f186ba
58
MCGalaxy/Commands/CPE/CmdPing.cs
Normal file
58
MCGalaxy/Commands/CPE/CmdPing.cs
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2015 MCGalaxy
|
||||
|
||||
Dual-licensed under the Educational Community License, Version 2.0 and
|
||||
the GNU General Public License, Version 3 (the "Licenses"); you may
|
||||
not use this file except in compliance with the Licenses. You may
|
||||
obtain a copy of the Licenses at
|
||||
|
||||
http://www.opensource.org/licenses/ecl2.php
|
||||
http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the Licenses are distributed on an "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied. See the Licenses for the specific language governing
|
||||
permissions and limitations under the Licenses.
|
||||
*/
|
||||
using MCGalaxy.Network;
|
||||
|
||||
namespace MCGalaxy.Commands.Chatting {
|
||||
public sealed class CmdPing : Command {
|
||||
public override string name { get { return "ping"; } }
|
||||
public override string type { get { return CommandTypes.Information; } }
|
||||
public override bool museumUsable { get { return true; } }
|
||||
public override LevelPermission defaultRank { get { return LevelPermission.Guest; } }
|
||||
public override CommandPerm[] ExtraPerms {
|
||||
get { return new[] { new CommandPerm(LevelPermission.Admin, "+ can see ping of all players") }; }
|
||||
}
|
||||
|
||||
public override void Use(Player p, string message) {
|
||||
if (!message.CaselessEq("all")) {
|
||||
if (Player.IsSuper(p)) { Player.Message(p, "Super users cannot measure their own ping."); return; }
|
||||
|
||||
if (p.Ping.AveragePingMilliseconds() == 0) {
|
||||
Player.Message(p, "Your client does not support measuring ping.");
|
||||
} else {
|
||||
Player.Message(p, p.Ping.Format());
|
||||
}
|
||||
} else {
|
||||
if (!CheckExtraPerm(p)) { MessageNeedExtra(p); return; }
|
||||
Player[] players = PlayerInfo.Online.Items;
|
||||
Player.Message(p, "Ping/latency list for online players:");
|
||||
|
||||
foreach (Player pl in players) {
|
||||
if (!Entities.CanSee(p, pl)) continue;
|
||||
if (pl.Ping.AveragePingMilliseconds() == 0) continue;
|
||||
Player.Message(p, pl.ColoredName + " %S- " + pl.Ping.Format());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Help(Player p) {
|
||||
Player.Message(p, "%T/ping %H- Outputs details about your ping to the server.");
|
||||
Player.Message(p, "%T/ping all %H- Outputs ping details for all players.");
|
||||
Player.Message(p, "&cNOTE: %HNot all clients support measuring ping.");
|
||||
}
|
||||
}
|
||||
}
|
@ -199,6 +199,7 @@
|
||||
<Compile Include="Commands\CPE\CmdEntityRot.cs" />
|
||||
<Compile Include="Commands\CPE\CmdEnvironment.cs" />
|
||||
<Compile Include="Commands\CPE\CmdModel.cs" />
|
||||
<Compile Include="Commands\CPE\CmdPing.cs" />
|
||||
<Compile Include="Commands\CPE\CmdReachDistance.cs" />
|
||||
<Compile Include="Commands\CPE\CmdSkin.cs" />
|
||||
<Compile Include="Commands\CPE\CmdTexture.cs" />
|
||||
@ -576,6 +577,7 @@
|
||||
<Compile Include="Network\Utils\HttpUtil.cs" />
|
||||
<Compile Include="Network\Utils\LevelChunkStream.cs" />
|
||||
<Compile Include="Network\Utils\NetUtils.cs" />
|
||||
<Compile Include="Network\Utils\PingList.cs" />
|
||||
<Compile Include="Player\Group\Group.cs" />
|
||||
<Compile Include="Player\Group\GroupProperties.cs" />
|
||||
<Compile Include="Player\List\PlayerExtList.cs" />
|
||||
|
@ -66,5 +66,6 @@ namespace MCGalaxy.Network {
|
||||
public const byte CpeSetMapEnvUrl = 40;
|
||||
public const byte CpeSetMapEnvProperty = 41;
|
||||
public const byte CpeSetEntityProperty = 42;
|
||||
public const byte CpeTwoWayPing = 43;
|
||||
}
|
||||
}
|
||||
|
@ -247,5 +247,13 @@ namespace MCGalaxy.Network {
|
||||
NetUtils.WriteI32(value, buffer, 3);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static byte[] TwoWayPing(bool serverToClient, ushort data) {
|
||||
byte[] buffer = new byte[4];
|
||||
buffer[0] = Opcode.CpeTwoWayPing;
|
||||
buffer[1] = (byte)(serverToClient ? 1 : 0);
|
||||
NetUtils.WriteU16(data, buffer, 2);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,19 @@ namespace MCGalaxy {
|
||||
if (OnPlayerClick != null) OnPlayerClick(this, Button, Action, Yaw, Pitch, EntityID, X, Y, Z, face);
|
||||
OnPlayerClickEvent.Call(this, Button, Action, Yaw, Pitch, EntityID, X, Y, Z, face);
|
||||
}
|
||||
|
||||
void HandleTwoWayPing(byte[] packet) {
|
||||
bool serverToClient = packet[1] != 0;
|
||||
ushort data = NetUtils.ReadU16(packet, 2);
|
||||
|
||||
if (!serverToClient) {
|
||||
// Client-> server ping, immediately send reply.
|
||||
Send(Packet.TwoWayPing(false, data));
|
||||
} else {
|
||||
// Server -> client ping, set time received for reply.
|
||||
Ping.Update(data);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckReadAllExtensions() {
|
||||
if (extensionCount <= 0 && !finishedCpeLogin) {
|
||||
@ -99,7 +112,7 @@ namespace MCGalaxy {
|
||||
|
||||
public static void SendMessage(Player p, string message, bool colorParse) {
|
||||
if (p == null) {
|
||||
Logger.Log(LogType.ConsoleMessage, message);
|
||||
Logger.Log(LogType.ConsoleMessage, message);
|
||||
} else {
|
||||
p.SendMessage(0, message, colorParse);
|
||||
}
|
||||
|
96
MCGalaxy/Network/Utils/PingList.cs
Normal file
96
MCGalaxy/Network/Utils/PingList.cs
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
Copyright 2015 MCGalaxy
|
||||
|
||||
Dual-licensed under the Educational Community License, Version 2.0 and
|
||||
the GNU General Public License, Version 3 (the "Licenses"); you may
|
||||
not use this file except in compliance with the Licenses. You may
|
||||
obtain a copy of the Licenses at
|
||||
|
||||
http://www.opensource.org/licenses/ecl2.php
|
||||
http://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the Licenses are distributed on an "AS IS"
|
||||
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied. See the Licenses for the specific language governing
|
||||
permissions and limitations under the Licenses.
|
||||
*/
|
||||
using System;
|
||||
|
||||
namespace MCGalaxy.Network {
|
||||
public sealed class PingList {
|
||||
|
||||
public struct PingEntry {
|
||||
public DateTime TimeSent, TimeReceived;
|
||||
public ushort Data;
|
||||
}
|
||||
public PingEntry[] Entries = new PingEntry[10];
|
||||
|
||||
|
||||
public ushort NextTwoWayPingData() {
|
||||
// Find free ping slot
|
||||
for (int i = 0; i < Entries.Length; i++) {
|
||||
if (Entries[i].TimeSent.Ticks != 0) continue;
|
||||
|
||||
ushort prev = i > 0 ? Entries[i - 1].Data : (ushort)0;
|
||||
return SetTwoWayPing(i, prev);
|
||||
}
|
||||
|
||||
// Remove oldest ping slot
|
||||
for (int i = 0; i < Entries.Length - 1; i++) {
|
||||
Entries[i] = Entries[i + 1];
|
||||
}
|
||||
int j = Entries.Length - 1;
|
||||
return SetTwoWayPing(j, Entries[j].Data);
|
||||
}
|
||||
|
||||
ushort SetTwoWayPing(int i, ushort prev) {
|
||||
Entries[i].Data = (ushort)(prev + 1);
|
||||
Entries[i].TimeSent = DateTime.UtcNow;
|
||||
return (ushort)(prev + 1);
|
||||
}
|
||||
|
||||
public void Update(ushort data) {
|
||||
for (int i = 0; i < Entries.Length; i++ ) {
|
||||
if (Entries[i].Data != data) continue;
|
||||
Entries[i].TimeReceived = DateTime.UtcNow;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary> Gets average ping in milliseconds, or 0 if no ping measures. </summary>
|
||||
public double AveragePingMilliseconds() {
|
||||
double totalMs = 0;
|
||||
int measures = 0;
|
||||
|
||||
foreach (PingEntry ping in Entries) {
|
||||
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 (PingEntry ping in Entries) {
|
||||
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 Format() {
|
||||
return String.Format(" Worst ping {0}ms, average {1}ms",
|
||||
WorstPingMilliseconds().ToString("N0"),
|
||||
AveragePingMilliseconds().ToString("N0"));
|
||||
}
|
||||
}
|
||||
}
|
@ -26,11 +26,11 @@ namespace MCGalaxy {
|
||||
public int ChangeModel, EnvMapAppearance, EnvWeatherType, HackControl;
|
||||
public int EmoteFix, MessageTypes, LongerMessages, FullCP437;
|
||||
public int BlockDefinitions, BlockDefinitionsExt, TextColors, BulkBlockUpdate;
|
||||
public int EnvMapAspect, PlayerClick, EntityProperty, ExtEntityPositions;
|
||||
public int EnvMapAspect, PlayerClick, EntityProperty, ExtEntityPositions, TwoWayPing;
|
||||
|
||||
// these are checked frequently, so avoid overhead of HasCpeExt
|
||||
// these are checked very frequently, so avoid overhead of HasCpeExt
|
||||
public bool hasCustomBlocks, hasBlockDefs,
|
||||
hasTextColors, hasChangeModel, hasExtList, hasCP437;
|
||||
hasTextColors, hasChangeModel, hasExtList, hasCP437, hasTwoWayPing;
|
||||
|
||||
public void AddExtension(string ext, int version) {
|
||||
switch (ext.Trim()) {
|
||||
@ -96,6 +96,9 @@ namespace MCGalaxy {
|
||||
case CpeExt.ExtEntityPositions:
|
||||
ExtEntityPositions = version;
|
||||
hasExtPositions = true; break;
|
||||
case CpeExt.TwoWayPing:
|
||||
TwoWayPing = version;
|
||||
hasTwoWayPing = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +129,7 @@ namespace MCGalaxy {
|
||||
case CpeExt.PlayerClick: return PlayerClick == version;
|
||||
case CpeExt.EntityProperty: return EntityProperty == version;
|
||||
case CpeExt.ExtEntityPositions: return ExtEntityPositions == version;
|
||||
case CpeExt.TwoWayPing: return TwoWayPing == version;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@ -253,6 +257,7 @@ namespace MCGalaxy {
|
||||
public const string PlayerClick = "PlayerClick";
|
||||
public const string EntityProperty = "EntityProperty";
|
||||
public const string ExtEntityPositions = "ExtEntityPositions";
|
||||
public const string TwoWayPing = "TwoWayPing";
|
||||
}
|
||||
|
||||
public enum CpeMessageType : byte {
|
||||
|
@ -58,6 +58,7 @@ namespace MCGalaxy {
|
||||
public string truename;
|
||||
internal bool dontmindme = false;
|
||||
INetworkSocket socket;
|
||||
public PingList Ping = new PingList();
|
||||
|
||||
public DateTime LastAction, AFKCooldown;
|
||||
public bool IsAfk = false, AutoAfk;
|
||||
|
@ -230,6 +230,7 @@ namespace MCGalaxy {
|
||||
case Opcode.CpeCustomBlockSupportLevel: return 2;
|
||||
case Opcode.CpePlayerClick: return 15;
|
||||
case Opcode.Ping: return 1;
|
||||
case Opcode.CpeTwoWayPing: return 4;
|
||||
|
||||
default:
|
||||
if (!dontmindme) {
|
||||
@ -262,6 +263,8 @@ namespace MCGalaxy {
|
||||
customBlockSupportLevel = buffer[1]; break;
|
||||
case Opcode.CpePlayerClick:
|
||||
HandlePlayerClicked(buffer); break;
|
||||
case Opcode.CpeTwoWayPing:
|
||||
HandleTwoWayPing(buffer); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ namespace MCGalaxy {
|
||||
}
|
||||
|
||||
void SendCpeExtensions() {
|
||||
Send(Packet.ExtInfo(25), true);
|
||||
Send(Packet.ExtInfo(26), true);
|
||||
|
||||
Send(Packet.ExtEntry(CpeExt.EnvMapAppearance, 1), true); // fix for classicube client, doesn't reply if only send EnvMapAppearance with version 2
|
||||
Send(Packet.ExtEntry(CpeExt.ClickDistance, 1), true);
|
||||
@ -90,6 +90,7 @@ namespace MCGalaxy {
|
||||
Send(Packet.ExtEntry(CpeExt.EntityProperty, 1), true);
|
||||
|
||||
Send(Packet.ExtEntry(CpeExt.ExtEntityPositions, 1), true);
|
||||
Send(Packet.ExtEntry(CpeExt.TwoWayPing, 1), true);
|
||||
}
|
||||
|
||||
void CompleteLoginProcess() {
|
||||
|
@ -95,7 +95,12 @@ namespace MCGalaxy.Tasks {
|
||||
internal static void CheckState(SchedulerTask task) {
|
||||
Player[] players = PlayerInfo.Online.Items;
|
||||
foreach (Player p in players) {
|
||||
p.Send(Packet.Ping());
|
||||
if (p.hasTwoWayPing) {
|
||||
p.Send(Packet.TwoWayPing(true, p.Ping.NextTwoWayPingData()));
|
||||
} else {
|
||||
p.Send(Packet.Ping());
|
||||
}
|
||||
|
||||
if (Server.afkminutes <= 0) return;
|
||||
if (DateTime.UtcNow < p.AFKCooldown) return;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user