diff --git a/Emulator/.idea/misc.xml b/Emulator/.idea/misc.xml
index 749dacb..44185bb 100644
--- a/Emulator/.idea/misc.xml
+++ b/Emulator/.idea/misc.xml
@@ -8,5 +8,5 @@
-
+
\ No newline at end of file
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/LatencyTracker.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/LatencyTracker.java
new file mode 100644
index 0000000..43e1c55
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/LatencyTracker.java
@@ -0,0 +1,47 @@
+package com.eu.habbo.habbohotel;
+
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+public class LatencyTracker {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GameClient.class);
+
+ private boolean initialPing;
+
+ private long last;
+ private long average;
+
+ public LatencyTracker() {
+ this.initialPing = true;
+ this.average = 0;
+ }
+
+ public void update(long latencyInNano) {
+ this.last = latencyInNano;
+
+ if (this.initialPing) {
+ this.initialPing = false;
+ this.average = latencyInNano;
+ return;
+ }
+
+ this.average = (long) (this.average * .7f + latencyInNano * .3f);
+ }
+
+ public boolean hasInitialized() {
+ return !this.initialPing;
+ }
+
+ public long getLastMs() {
+ return TimeUnit.NANOSECONDS.toMillis(this.last);
+ }
+
+ public long getAverageMs() {
+ return TimeUnit.NANOSECONDS.toMillis(this.average);
+ }
+
+}
\ No newline at end of file
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java
index b2f98f3..42e32d5 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/CommandHandler.java
@@ -230,6 +230,7 @@ public class CommandHandler {
addCommand(new MutePetsCommand());
addCommand(new PetInfoCommand());
addCommand(new PickallCommand());
+ addCommand(new PingCommand());
addCommand(new PixelCommand());
addCommand(new PluginsCommand());
addCommand(new PointsCommand());
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/PingCommand.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/PingCommand.java
new file mode 100644
index 0000000..5e1c0bd
--- /dev/null
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/commands/PingCommand.java
@@ -0,0 +1,29 @@
+package com.eu.habbo.habbohotel.commands;
+
+import com.eu.habbo.habbohotel.LatencyTracker;
+import com.eu.habbo.habbohotel.gameclients.GameClient;
+
+public class PingCommand extends Command {
+ public PingCommand() {
+ super(null, new String[]{"ping"});
+ }
+
+ @Override
+ public boolean handle(GameClient gameClient, String[] params) throws Exception {
+ if (gameClient.getHabbo().getRoomUnit() == null) {
+ return true;
+ }
+
+ final LatencyTracker latencyTracker = gameClient.getLatencyTracker();
+
+ if (latencyTracker.hasInitialized()) {
+ gameClient.getHabbo().whisper(String.format("Average ping %dms, last ping %dms",
+ latencyTracker.getAverageMs(),
+ latencyTracker.getLastMs()));
+ } else {
+ gameClient.getHabbo().whisper("\n" + "Ping speed has not been calculated yet, please try again in a minute.");
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java
index a05ef0a..6c8d580 100644
--- a/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java
+++ b/Emulator/src/main/java/com/eu/habbo/habbohotel/gameclients/GameClient.java
@@ -2,6 +2,7 @@ package com.eu.habbo.habbohotel.gameclients;
import com.eu.habbo.Emulator;
import com.eu.habbo.crypto.HabboEncryption;
+import com.eu.habbo.habbohotel.LatencyTracker;
import com.eu.habbo.habbohotel.users.Habbo;
import com.eu.habbo.messages.ServerMessage;
import com.eu.habbo.messages.incoming.MessageHandler;
@@ -10,9 +11,6 @@ import io.netty.channel.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
@@ -22,7 +20,7 @@ public class GameClient {
private final Channel channel;
private final HabboEncryption encryption;
-
+ private final LatencyTracker latencyTracker;
private Habbo habbo;
private boolean handshakeFinished;
private String machineId = "";
@@ -39,6 +37,7 @@ public class GameClient {
Emulator.getCrypto().getModulus(),
Emulator.getCrypto().getPrivateExponent())
: null;
+ this.latencyTracker = new LatencyTracker();
}
public Channel getChannel() {
@@ -49,6 +48,8 @@ public class GameClient {
return encryption;
}
+ public LatencyTracker getLatencyTracker() { return latencyTracker; }
+
public Habbo getHabbo() {
return this.habbo;
}
diff --git a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/handlers/IdleTimeoutHandler.java b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/handlers/IdleTimeoutHandler.java
index 26c5566..e17e2d9 100644
--- a/Emulator/src/main/java/com/eu/habbo/networking/gameserver/handlers/IdleTimeoutHandler.java
+++ b/Emulator/src/main/java/com/eu/habbo/networking/gameserver/handlers/IdleTimeoutHandler.java
@@ -18,7 +18,9 @@ public class IdleTimeoutHandler extends ChannelDuplexHandler {
private final long pongTimeoutNanos;
volatile ScheduledFuture> pingScheduleFuture;
- volatile long lastPongTime;// in nanoseconds
+ volatile boolean sentPing;
+ volatile long lastPingTime; // in nanoseconds
+ volatile long lastPongTime; // in nanoseconds
private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed
@@ -97,10 +99,18 @@ public class IdleTimeoutHandler extends ChannelDuplexHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// check if its a pong message
- if(msg instanceof ClientMessage) {
+ if (msg instanceof ClientMessage) {
ClientMessage packet = (ClientMessage) msg;
- if(packet.getMessageId() == Incoming.PongEvent) {
+ if (packet.getMessageId() == Incoming.PongEvent) {
this.lastPongTime = System.nanoTime();
+
+ final GameClient client = ctx.channel().attr(GameServerAttributes.CLIENT).get();
+ if (client != null) {
+ if (sentPing) {
+ sentPing = false;
+ client.getLatencyTracker().update(lastPongTime - lastPingTime);
+ }
+ }
}
}
super.channelRead(ctx, msg);
@@ -120,13 +130,15 @@ public class IdleTimeoutHandler extends ChannelDuplexHandler {
}
long currentTime = System.nanoTime();
- if(currentTime - lastPongTime > pongTimeoutNanos) {
+ if (currentTime - lastPongTime > pongTimeoutNanos) {
ctx.close();// add a promise here ?
return;
}
- GameClient client = ctx.channel().attr(GameServerAttributes.CLIENT).get();
+ final GameClient client = ctx.channel().attr(GameServerAttributes.CLIENT).get();
if (client != null) {
+ lastPingTime = System.nanoTime();
+ sentPing = true;
client.sendResponse(new PingComposer());
}