From 144c24c27612dd264bd68ae40f3099d6adb1e442 Mon Sep 17 00:00:00 2001 From: KennyTV Date: Tue, 20 Apr 2021 13:19:34 +0200 Subject: [PATCH] Move packet tracking methods out of UserConnectionn --- README.md | 2 - .../ViaVersion/api/data/PacketTracker.java | 176 ++++++++++++++ .../ViaVersion/api/data/UserConnection.java | 230 ++++++------------ .../ViaVersion/api/platform/ViaPlatform.java | 3 +- .../bukkit/handlers/BukkitPacketHandler.java | 2 +- .../commands/defaultsubs/PPSSubCmd.java | 12 +- .../Protocol1_17To1_16_4.java | 3 +- .../myles/ViaVersion/update/UpdateUtil.java | 11 +- .../us/myles/ViaVersion/SpongePlugin.java | 5 +- .../sponge/handlers/SpongePacketHandler.java | 2 +- .../us/myles/ViaVersion/VelocityPlugin.java | 3 +- 11 files changed, 271 insertions(+), 178 deletions(-) create mode 100644 api/src/main/java/us/myles/ViaVersion/api/data/PacketTracker.java diff --git a/README.md b/README.md index 47ed26b87..b5d3c93cb 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,6 @@ Please note the [differences in licensing](#license). Building: -------- -You need JDK 9 or higher to build ViaVersion. The plugin works on Java 8, but needs to be compiled with a higher version for compatibility reasons. - After cloning this repository, build the project with Gradle by running `/gradlew build` and take the created jar out of the `build/libs` directory. diff --git a/api/src/main/java/us/myles/ViaVersion/api/data/PacketTracker.java b/api/src/main/java/us/myles/ViaVersion/api/data/PacketTracker.java new file mode 100644 index 000000000..d4db401a9 --- /dev/null +++ b/api/src/main/java/us/myles/ViaVersion/api/data/PacketTracker.java @@ -0,0 +1,176 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2021 ViaVersion and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package us.myles.ViaVersion.api.data; + +import org.checkerframework.checker.nullness.qual.Nullable; +import us.myles.ViaVersion.api.Via; +import us.myles.ViaVersion.api.ViaVersionConfig; + +public class PacketTracker { + private final UserConnection connection; + private Object lastPacket; + private long sentPackets; + private long receivedPackets; + // Used for tracking pps + private long startTime; + private long intervalPackets; + private long packetsPerSecond = -1L; + // Used for handling warnings (over time) + private int secondsObserved; + private int warnings; + + public PacketTracker(UserConnection connection) { + this.connection = connection; + } + + /** + * Used for incrementing the number of packets sent to the client. + */ + public void incrementSent() { + this.sentPackets++; + } + + /** + * Used for incrementing the number of packets received from the client. + * + * @return true if the interval has reset and can now be checked for the packets sent + */ + public boolean incrementReceived() { + // handle stats + long diff = System.currentTimeMillis() - startTime; + if (diff >= 1000) { + packetsPerSecond = intervalPackets; + startTime = System.currentTimeMillis(); + intervalPackets = 1; + return true; + } else { + intervalPackets++; + } + // increase total + this.receivedPackets++; + return false; + } + + /** + * Checks for packet flood with the packets sent in the last second. + * ALWAYS check for {@link #incrementReceived()} before using this method. + * + * @return true if the packet should be cancelled + * @see #incrementReceived() + */ + public boolean exceedsMaxPPS() { + if (connection.isClientSide()) return false; // Don't apply PPS limiting for client-side + ViaVersionConfig conf = Via.getConfig(); + // Max PPS Checker + if (conf.getMaxPPS() > 0) { + if (packetsPerSecond >= conf.getMaxPPS()) { + connection.disconnect(conf.getMaxPPSKickMessage().replace("%pps", Long.toString(packetsPerSecond))); + return true; // don't send current packet + } + } + + // Tracking PPS Checker + if (conf.getMaxWarnings() > 0 && conf.getTrackingPeriod() > 0) { + if (secondsObserved > conf.getTrackingPeriod()) { + // Reset + warnings = 0; + secondsObserved = 1; + } else { + secondsObserved++; + if (packetsPerSecond >= conf.getWarningPPS()) { + warnings++; + } + + if (warnings >= conf.getMaxWarnings()) { + connection.disconnect(conf.getMaxWarningsKickMessage().replace("%pps", Long.toString(packetsPerSecond))); + return true; // don't send current packet + } + } + } + return false; + } + + public @Nullable Object getLastPacket() { + return lastPacket; + } + + public void setLastPacket(Object lastPacket) { + this.lastPacket = lastPacket; + } + + public long getSentPackets() { + return sentPackets; + } + + public void setSentPackets(long sentPackets) { + this.sentPackets = sentPackets; + } + + public long getReceivedPackets() { + return receivedPackets; + } + + public void setReceivedPackets(long receivedPackets) { + this.receivedPackets = receivedPackets; + } + + public long getStartTime() { + return startTime; + } + + public void setStartTime(long startTime) { + this.startTime = startTime; + } + + public long getIntervalPackets() { + return intervalPackets; + } + + public void setIntervalPackets(long intervalPackets) { + this.intervalPackets = intervalPackets; + } + + public long getPacketsPerSecond() { + return packetsPerSecond; + } + + public void setPacketsPerSecond(long packetsPerSecond) { + this.packetsPerSecond = packetsPerSecond; + } + + public int getSecondsObserved() { + return secondsObserved; + } + + public void setSecondsObserved(int secondsObserved) { + this.secondsObserved = secondsObserved; + } + + public int getWarnings() { + return warnings; + } + + public void setWarnings(int warnings) { + this.warnings = warnings; + } +} diff --git a/api/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java b/api/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java index 8606850ea..030cb898b 100644 --- a/api/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java +++ b/api/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java @@ -51,25 +51,16 @@ import java.util.function.Function; public class UserConnection { private static final AtomicLong IDS = new AtomicLong(); private final long id = IDS.incrementAndGet(); - private final Channel channel; - private final boolean clientSide; - Map, StoredObject> storedObjects = new ConcurrentHashMap<>(); - private ProtocolInfo protocolInfo; + private final Map, StoredObject> storedObjects = new ConcurrentHashMap<>(); + private final PacketTracker packetTracker = new PacketTracker(this); private final Set passthroughTokens = Collections.newSetFromMap(CacheBuilder.newBuilder() .expireAfterWrite(10, TimeUnit.SECONDS) .build().asMap()); + private final Channel channel; + private final boolean clientSide; + private ProtocolInfo protocolInfo; private boolean active = true; private boolean pendingDisconnect; - private Object lastPacket; - private long sentPackets; - private long receivedPackets; - // Used for tracking pps - private long startTime; - private long intervalPackets; - private long packetsPerSecond = -1L; - // Used for handling warnings (over time) - private int secondsObserved; - private int warnings; /** * Creates an UserConnection. When it's a client-side connection, some method behaviors are modified. @@ -188,70 +179,12 @@ public class UserConnection { } /** - * Used for incrementing the number of packets sent to the client. - */ - public void incrementSent() { - this.sentPackets++; - } - - /** - * Used for incrementing the number of packets received from the client. + * Returns the user's packet tracker used for the inbuilt packet-limiter. * - * @return true if the interval has reset and can now be checked for the packets sent + * @return packet tracker */ - public boolean incrementReceived() { - // handle stats - long diff = System.currentTimeMillis() - startTime; - if (diff >= 1000) { - packetsPerSecond = intervalPackets; - startTime = System.currentTimeMillis(); - intervalPackets = 1; - return true; - } else { - intervalPackets++; - } - // increase total - this.receivedPackets++; - return false; - } - - /** - * Checks for packet flood with the packets sent in the last second. - * ALWAYS check for {@link #incrementReceived()} before using this method. - * - * @return true if the packet should be cancelled - * @see #incrementReceived() - */ - public boolean exceedsMaxPPS() { - if (clientSide) return false; // Don't apply PPS limiting for client-side - ViaVersionConfig conf = Via.getConfig(); - // Max PPS Checker - if (conf.getMaxPPS() > 0) { - if (packetsPerSecond >= conf.getMaxPPS()) { - disconnect(conf.getMaxPPSKickMessage().replace("%pps", Long.toString(packetsPerSecond))); - return true; // don't send current packet - } - } - - // Tracking PPS Checker - if (conf.getMaxWarnings() > 0 && conf.getTrackingPeriod() > 0) { - if (secondsObserved > conf.getTrackingPeriod()) { - // Reset - warnings = 0; - secondsObserved = 1; - } else { - secondsObserved++; - if (packetsPerSecond >= conf.getWarningPPS()) { - warnings++; - } - - if (warnings >= conf.getMaxWarnings()) { - disconnect(conf.getMaxWarningsKickMessage().replace("%pps", Long.toString(packetsPerSecond))); - return true; // don't send current packet - } - } - } - return false; + public PacketTracker getPacketTracker() { + return packetTracker; } /** @@ -351,22 +284,22 @@ public class UserConnection { */ public boolean checkIncomingPacket() { if (clientSide) { - return checkClientBound(); + return checkClientbound(); } else { - return checkServerBound(); + return checkServerbound(); } } - private boolean checkClientBound() { - incrementSent(); + private boolean checkClientbound() { + packetTracker.incrementSent(); return true; } - private boolean checkServerBound() { + private boolean checkServerbound() { // Ignore if pending disconnect if (pendingDisconnect) return false; // Increment received + Check PPS - return !incrementReceived() || !exceedsMaxPPS(); + return !packetTracker.incrementReceived() || !packetTracker.exceedsMaxPPS(); } /** @@ -376,9 +309,9 @@ public class UserConnection { */ public boolean checkOutgoingPacket() { if (clientSide) { - return checkServerBound(); + return checkServerbound(); } else { - return checkClientBound(); + return checkClientbound(); } } @@ -448,14 +381,29 @@ public class UserConnection { } } + /** + * Returns the internal id incremented for each new connection. + * + * @return internal id + */ public long getId() { return id; } + /** + * Returns the netty channel if present. + * + * @return netty channel if present + */ public @Nullable Channel getChannel() { return channel; } + /** + * Returns info containing the current protocol state and userdata. + * + * @return info containing the current protocol state and userdata + */ public @Nullable ProtocolInfo getProtocolInfo() { return protocolInfo; } @@ -469,10 +417,23 @@ public class UserConnection { } } + /** + * Returns a map of stored objects. + * + * @return map of stored objects + * @see #has(Class) + * @see #get(Class) + * @see #put(StoredObject) + */ public Map, StoredObject> getStoredObjects() { return storedObjects; } + /** + * Returns whether the connection has protocols other than the base protocol applied. + * + * @return whether the connection is active + */ public boolean isActive() { return active; } @@ -481,6 +442,11 @@ public class UserConnection { this.active = active; } + /** + * Returns whether the connection is pending a disconnect, initiated through {@link #disconnect(String)}. + * + * @return whether the connection is pending a disconnect + */ public boolean isPendingDisconnect() { return pendingDisconnect; } @@ -489,68 +455,34 @@ public class UserConnection { this.pendingDisconnect = pendingDisconnect; } - public @Nullable Object getLastPacket() { - return lastPacket; + /** + * Returns whether this is a client-side connection (a mod integrated into the client itself). + * + * @return whether this is a client-side connection + */ + public boolean isClientSide() { + return clientSide; } - public void setLastPacket(@Nullable Object lastPacket) { - this.lastPacket = lastPacket; + /** + * Returns whether {@link ViaVersionConfig#getBlockedProtocols()} should be checked for this connection. + * + * @return whether blocked protocols should be applied + */ + public boolean shouldApplyBlockProtocol() { + return !clientSide; // Don't apply protocol blocking on client-side } - public long getSentPackets() { - return sentPackets; - } - - public void setSentPackets(long sentPackets) { - this.sentPackets = sentPackets; - } - - public long getReceivedPackets() { - return receivedPackets; - } - - public void setReceivedPackets(long receivedPackets) { - this.receivedPackets = receivedPackets; - } - - public long getStartTime() { - return startTime; - } - - public void setStartTime(long startTime) { - this.startTime = startTime; - } - - public long getIntervalPackets() { - return intervalPackets; - } - - public void setIntervalPackets(long intervalPackets) { - this.intervalPackets = intervalPackets; - } - - public long getPacketsPerSecond() { - return packetsPerSecond; - } - - public void setPacketsPerSecond(long packetsPerSecond) { - this.packetsPerSecond = packetsPerSecond; - } - - public int getSecondsObserved() { - return secondsObserved; - } - - public void setSecondsObserved(int secondsObserved) { - this.secondsObserved = secondsObserved; - } - - public int getWarnings() { - return warnings; - } - - public void setWarnings(int warnings) { - this.warnings = warnings; + /** + * Returns a newly generated uuid that will let a packet be passed through without + * transformig its contents if used together with {@link PacketWrapper#PASSTHROUGH_ID}. + * + * @return generated passthrough token + */ + public UUID generatePassthroughToken() { + UUID token = UUID.randomUUID(); + passthroughTokens.add(token); + return token; } @Override @@ -565,18 +497,4 @@ public class UserConnection { public int hashCode() { return Long.hashCode(id); } - - public boolean isClientSide() { - return clientSide; - } - - public boolean shouldApplyBlockProtocol() { - return !clientSide; // Don't apply protocol blocking on client-side - } - - public UUID generatePassthroughToken() { - UUID token = UUID.randomUUID(); - passthroughTokens.add(token); - return token; - } } diff --git a/api/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java b/api/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java index beb67b828..4bc4256db 100644 --- a/api/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java +++ b/api/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java @@ -30,7 +30,6 @@ import us.myles.ViaVersion.api.ViaVersionConfig; import us.myles.ViaVersion.api.command.ViaCommandSender; import us.myles.ViaVersion.api.configuration.ConfigurationProvider; import us.myles.ViaVersion.api.data.UserConnection; -import us.myles.ViaVersion.protocols.base.ProtocolInfo; import us.myles.ViaVersion.util.UnsupportedSoftware; import java.io.File; @@ -159,7 +158,7 @@ public interface ViaPlatform { */ default boolean disconnect(UserConnection connection, String message) { if (connection.isClientSide()) return false; - UUID uuid = connection.get(ProtocolInfo.class).getUuid(); + UUID uuid = connection.getProtocolInfo().getUuid(); if (uuid == null) return false; return kickPlayer(uuid, message); } diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitPacketHandler.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitPacketHandler.java index fcf99ead2..f807aaf17 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitPacketHandler.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitPacketHandler.java @@ -37,7 +37,7 @@ public class BukkitPacketHandler extends MessageToMessageEncoder { // This will prevent issues with several plugins and other protocol handlers due to the chunks being sent twice. // It also sends the chunks in the right order possible resolving some issues with added chunks/block/entity data. if (!(o instanceof ByteBuf)) { - info.setLastPacket(o); + info.getPacketTracker().setLastPacket(o); /* This transformer is more for fixing issues which we find hard at packet level :) */ if (info.isActive()) { if (info.getProtocolInfo().getPipeline().filter(o, list)) { diff --git a/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/PPSSubCmd.java b/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/PPSSubCmd.java index f0e21c029..9c1790a3f 100644 --- a/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/PPSSubCmd.java +++ b/common/src/main/java/us/myles/ViaVersion/commands/defaultsubs/PPSSubCmd.java @@ -55,13 +55,13 @@ public class PPSSubCmd extends ViaSubCommand { for (ViaCommandSender p : Via.getPlatform().getOnlinePlayers()) { int playerVersion = Via.getAPI().getPlayerVersion(p.getUUID()); if (!playerVersions.containsKey(playerVersion)) - playerVersions.put(playerVersion, new HashSet()); + playerVersions.put(playerVersion, new HashSet<>()); UserConnection uc = Via.getManager().getConnectionManager().getConnectedClient(p.getUUID()); - if (uc != null && uc.getPacketsPerSecond() > -1) { - playerVersions.get(playerVersion).add(p.getName() + " (" + uc.getPacketsPerSecond() + " PPS)"); - totalPackets += uc.getPacketsPerSecond(); - if (uc.getPacketsPerSecond() > max) { - max = uc.getPacketsPerSecond(); + if (uc != null && uc.getPacketTracker().getPacketsPerSecond() > -1) { + playerVersions.get(playerVersion).add(p.getName() + " (" + uc.getPacketTracker().getPacketsPerSecond() + " PPS)"); + totalPackets += uc.getPacketTracker().getPacketsPerSecond(); + if (uc.getPacketTracker().getPacketsPerSecond() > max) { + max = uc.getPacketTracker().getPacketsPerSecond(); } clients++; } diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_17to1_16_4/Protocol1_17To1_16_4.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_17to1_16_4/Protocol1_17To1_16_4.java index bb14bc499..bbfc03a8c 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_17to1_16_4/Protocol1_17To1_16_4.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_17to1_16_4/Protocol1_17To1_16_4.java @@ -17,7 +17,6 @@ */ package us.myles.ViaVersion.protocols.protocol1_17to1_16_4; -import org.checkerframework.checker.nullness.qual.Nullable; import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.api.data.MappingData; import us.myles.ViaVersion.api.data.UserConnection; @@ -230,7 +229,7 @@ public class Protocol1_17To1_16_4 extends Protocol { @DefaultConfig(sharedRoot = false) private File spongeConfig; - public static final LegacyComponentSerializer COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().character('§').extractUrls().build(); + public static final LegacyComponentSerializer COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().character(ChatColorUtil.COLOR_CHAR).extractUrls().build(); private final SpongeViaAPI api = new SpongeViaAPI(); private SpongeViaConfig conf; private Logger logger; @@ -192,7 +193,7 @@ public class SpongePlugin implements ViaPlatform { @Override public boolean kickPlayer(UUID uuid, String message) { return game.getServer().getPlayer(uuid).map(player -> { - player.kick(TextSerializers.formattingCode('§').deserialize(message)); + player.kick(TextSerializers.formattingCode(ChatColorUtil.COLOR_CHAR).deserialize(message)); return true; }).orElse(false); } diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongePacketHandler.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongePacketHandler.java index ceb311c58..96e690656 100644 --- a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongePacketHandler.java +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongePacketHandler.java @@ -37,7 +37,7 @@ public class SpongePacketHandler extends MessageToMessageEncoder { // This will prevent issues with several plugins and other protocol handlers due to the chunks being sent twice. // It also sends the chunks in the right order possible resolving some issues with added chunks/block/entity data. if (!(o instanceof ByteBuf)) { - info.setLastPacket(o); + info.getPacketTracker().setLastPacket(o); /* This transformer is more for fixing issues which we find hard at packet level :) */ if (info.isActive()) { if (info.getProtocolInfo().getPipeline().filter(o, list)) { diff --git a/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java b/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java index 65c66dd07..008284ecf 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java +++ b/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java @@ -36,6 +36,7 @@ import us.myles.ViaVersion.api.data.MappingDataLoader; import us.myles.ViaVersion.api.platform.TaskId; import us.myles.ViaVersion.api.platform.ViaPlatform; import us.myles.ViaVersion.dump.PluginInfo; +import us.myles.ViaVersion.util.ChatColorUtil; import us.myles.ViaVersion.util.GsonUtil; import us.myles.ViaVersion.util.VersionInfo; import us.myles.ViaVersion.velocity.command.VelocityCommandHandler; @@ -64,7 +65,7 @@ import java.util.concurrent.TimeUnit; url = "https://viaversion.com" ) public class VelocityPlugin implements ViaPlatform { - public static final LegacyComponentSerializer COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().character('§').extractUrls().build(); + public static final LegacyComponentSerializer COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().character(ChatColorUtil.COLOR_CHAR).extractUrls().build(); public static ProxyServer PROXY; @Inject