diff --git a/src/main/java/us/myles/ViaVersion/ConnectionInfo.java b/src/main/java/us/myles/ViaVersion/ConnectionInfo.java index 964fcd3c5..28bd84f85 100644 --- a/src/main/java/us/myles/ViaVersion/ConnectionInfo.java +++ b/src/main/java/us/myles/ViaVersion/ConnectionInfo.java @@ -12,6 +12,9 @@ import us.myles.ViaVersion.packets.State; @Getter @Setter public class ConnectionInfo { + private static final long IDLE_PACKET_DELAY = 50L; // Update every 50ms (20tps) + private static final long IDLE_PACKET_LIMIT = 20; // Max 20 ticks behind + private final SocketChannel channel; private Object lastPacket; private java.util.UUID UUID; @@ -22,6 +25,7 @@ public class ConnectionInfo { private int entityID; private boolean active = true; private String username; + private long nextIdlePacket = 0L; public ConnectionInfo(SocketChannel socketChannel) { this.channel = socketChannel; @@ -33,6 +37,7 @@ public class ConnectionInfo { public void setActive(boolean active) { this.active = active; + this.nextIdlePacket = System.currentTimeMillis() + IDLE_PACKET_DELAY; // Update every 50 ticks } public void sendRawPacket(final ByteBuf packet, boolean currentThread) { @@ -56,4 +61,10 @@ public class ConnectionInfo { public void closeWindow() { this.openWindow = null; } + + public void incrementIdlePacket() { + // Notify of next update + // Allow a maximum lag spike of 1 second (20 ticks/updates) + this.nextIdlePacket = Math.max(nextIdlePacket + IDLE_PACKET_DELAY, System.currentTimeMillis() - IDLE_PACKET_DELAY * IDLE_PACKET_LIMIT); + } } diff --git a/src/main/java/us/myles/ViaVersion/ViaIdleThread.java b/src/main/java/us/myles/ViaVersion/ViaIdleThread.java new file mode 100644 index 000000000..31ce146db --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/ViaIdleThread.java @@ -0,0 +1,42 @@ +package us.myles.ViaVersion; + +import org.bukkit.scheduler.BukkitRunnable; +import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.util.ReflectionUtil; + +import java.util.Map; +import java.util.UUID; + +public class ViaIdleThread extends BukkitRunnable { + private final Map portedPlayers; + private final Class idlePacketClass; + + public ViaIdleThread(Map portedPlayers) { + this.portedPlayers = portedPlayers; + try { + this.idlePacketClass = ReflectionUtil.nms("PacketPlayInFlying"); + } catch(ClassNotFoundException e) { + throw new RuntimeException("Couldn't find player idle packet, help!", e); + } + } + + @Override + public void run() { + for(ConnectionInfo info : portedPlayers.values()) { + long nextIdleUpdate = info.getNextIdlePacket(); + if(nextIdleUpdate <= System.currentTimeMillis()) { + try { + Object packet = idlePacketClass.newInstance(); + info.getChannel().pipeline().fireChannelRead(packet); + } catch(InstantiationException | IllegalAccessException e) { + System.out.println("Failed to create idle packet."); + if(ViaVersion.getInstance().isDebug()) { + e.printStackTrace(); + } + } finally { + info.incrementIdlePacket(); + } + } + } + } +} diff --git a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index 12625e8b2..fb9347e01 100644 --- a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java +++ b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -73,6 +73,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); injectPacketHandler(); + new ViaIdleThread(portedPlayers).runTaskTimerAsynchronously(this, 1L, 1L); // Updates player's idle status if (getConfig().getBoolean("checkforupdates")) UpdateUtil.sendUpdateMessage(this); diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java b/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java index d283e39e1..ef455cd07 100644 --- a/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java +++ b/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java @@ -38,6 +38,11 @@ public class ViaDecodeHandler extends ByteToMessageDecoder { bytebuf.clear(); throw e; } + + // Update idle status (player, position, look, positionandlook) + if(id == 0x0F || id == 0x0E || id == 0x0D || id == 0x0C) { + info.incrementIdlePacket(); + } } // call minecraft decoder list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf));