From 11a86e9bb92d7a738e3bf31afc52f8e0747a0ed8 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Wed, 3 Jul 2019 09:20:32 -0400 Subject: [PATCH] Convert Velocity to use ByteBuf-derived plugin message packets Only tested with 1.12.2, not 1.13 or Forge --- .../com/velocitypowered/proxy/Velocity.java | 4 + .../proxy/connection/MinecraftConnection.java | 6 +- .../backend/BackendPlaySessionHandler.java | 12 +- .../backend/VelocityServerConnection.java | 5 +- .../client/ClientPlaySessionHandler.java | 34 ++++- .../connection/client/ConnectedPlayer.java | 5 +- .../LegacyForgeHandshakeBackendPhase.java | 2 +- .../LegacyForgeHandshakeClientPhase.java | 2 +- .../forge/legacy/LegacyForgeUtil.java | 9 +- .../protocol/packet/LoginPluginMessage.java | 20 ++- .../proxy/protocol/packet/PluginMessage.java | 73 +++++++--- .../protocol/util/DeferredByteBufHolder.java | 136 ++++++++++++++++++ .../protocol/util/PluginMessageUtil.java | 30 ++-- 13 files changed, 264 insertions(+), 74 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java index d00274735..a197cce37 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java @@ -1,5 +1,7 @@ package com.velocitypowered.proxy; +import io.netty.util.ResourceLeakDetector; +import io.netty.util.ResourceLeakDetector.Level; import java.text.DecimalFormat; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,6 +37,8 @@ public class Velocity { return; } + ResourceLeakDetector.setLevel(Level.PARANOID); + long startTime = System.currentTimeMillis(); VelocityServer server = new VelocityServer(options); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index beefa6b05..f4c56ae74 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -131,8 +131,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { try { sessionHandler.exception(cause); } catch (Exception ex) { - logger.error("{}: exception handling exception", (association != null ? association : - channel.remoteAddress()), cause); + logger.error("{}: exception handling exception in {}", + (association != null ? association : channel.remoteAddress()), sessionHandler, cause); } } @@ -140,7 +140,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (cause instanceof ReadTimeoutException) { logger.error("{}: read timed out", association); } else { - logger.error("{}: exception encountered", association, cause); + logger.error("{}: exception encountered in {}", association, sessionHandler, cause); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 5128efe9e..64c72042a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -25,6 +25,8 @@ import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; public class BackendPlaySessionHandler implements MinecraftSessionHandler { @@ -119,12 +121,15 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { return false; } + byte[] copy = ByteBufUtil.getBytes(packet.content()); PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, - packet.getData()); + copy); server.getEventManager().fire(event) .thenAcceptAsync(pme -> { if (pme.getResult().isAllowed() && !playerConnection.isClosed()) { - playerConnection.write(packet); + PluginMessage copied = new PluginMessage(packet.getChannel(), + Unpooled.wrappedBuffer(copy)); + playerConnection.write(copied); } }, playerConnection.eventLoop()); return true; @@ -163,6 +168,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public void handleGeneric(MinecraftPacket packet) { + if (packet instanceof PluginMessage) { + ((PluginMessage) packet).retain(); + } playerConnection.write(packet); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index a8b97c028..bb4142904 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -33,6 +33,7 @@ import com.velocitypowered.proxy.protocol.packet.Handshake; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.ServerLogin; import com.velocitypowered.proxy.server.VelocityRegisteredServer; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; @@ -212,9 +213,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, MinecraftConnection mc = ensureConnected(); - PluginMessage message = new PluginMessage(); - message.setChannel(identifier.getId()); - message.setData(data); + PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data)); mc.write(message); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index 9593108a8..69d5ec3a3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -29,6 +29,9 @@ import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse.Offer; import com.velocitypowered.proxy.protocol.packet.TitlePacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.util.ReferenceCountUtil; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -80,6 +83,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } } + @Override + public void deactivated() { + for (PluginMessage message : loginPluginMessages) { + ReferenceCountUtil.release(message); + } + } + @Override public boolean handle(KeepAlive packet) { VelocityServerConnection serverConnection = player.getConnectedServer(); @@ -213,10 +223,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { + "ready. Channel: {}. Packet discarded.", packet.getChannel()); } else if (PluginMessageUtil.isRegister(packet)) { player.getKnownChannels().addAll(PluginMessageUtil.getChannels(packet)); - backendConn.write(packet); + backendConn.write(packet.retain()); } else if (PluginMessageUtil.isUnregister(packet)) { player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet)); - backendConn.write(packet); + backendConn.write(packet.retain()); } else if (PluginMessageUtil.isMcBrand(packet)) { backendConn.write(PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion())); } else { @@ -236,16 +246,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // but further aggravated by Velocity. To work around these issues, we will queue any // non-FML handshake messages to be sent once the FML handshake has completed or the // JoinGame packet has been received by the proxy, whichever comes first. - loginPluginMessages.add(packet); + // + // We also need to make sure to retain these packets so they can be flushed + // appropriately. + loginPluginMessages.add(packet.retain()); } else { ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel()); if (id == null) { - backendConn.write(packet); + backendConn.write(packet.retain()); } else { + byte[] copy = ByteBufUtil.getBytes(packet.content()); PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id, - packet.getData()); - server.getEventManager().fire(event).thenAcceptAsync(pme -> backendConn.write(packet), - backendConn.eventLoop()); + ByteBufUtil.getBytes(packet.content())); + server.getEventManager().fire(event).thenAcceptAsync(pme -> { + PluginMessage message = new PluginMessage(packet.getChannel(), + Unpooled.wrappedBuffer(copy)); + backendConn.write(message); + }, backendConn.eventLoop()); } } } @@ -272,6 +289,9 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { MinecraftConnection smc = serverConnection.getConnection(); if (smc != null && serverConnection.getPhase().consideredComplete()) { + if (packet instanceof PluginMessage) { + ((PluginMessage) packet).retain(); + } smc.write(packet); } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 19fedd546..9a8acb732 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -49,6 +49,7 @@ import com.velocitypowered.proxy.tablist.VelocityTabList; import com.velocitypowered.proxy.util.VelocityMessages; import com.velocitypowered.proxy.util.collect.CappedSet; import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import java.net.InetSocketAddress; import java.util.Collection; import java.util.Collections; @@ -570,9 +571,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(data, "data"); - PluginMessage message = new PluginMessage(); - message.setChannel(identifier.getId()); - message.setData(data); + PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data)); minecraftConnection.write(message); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java index ad3610231..1c8594a22 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeBackendPhase.java @@ -114,7 +114,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase { serverConnection.setConnectionPhase(newPhase); // Write the packet to the player, we don't need it now. - player.getMinecraftConnection().write(message); + player.getMinecraftConnection().write(message.retain()); return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java index fd4577181..f75dc04b7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeHandshakeClientPhase.java @@ -212,7 +212,7 @@ public enum LegacyForgeHandshakeClientPhase implements ClientConnectionPhase { PluginMessage message, MinecraftConnection backendConn) { // Send the packet on to the server. - backendConn.write(message); + backendConn.write(message.retain()); // We handled the packet. No need to continue processing. return true; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java index ca01f86d7..8d2c9d410 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/forge/legacy/LegacyForgeUtil.java @@ -27,9 +27,8 @@ class LegacyForgeUtil { */ static byte getHandshakePacketDiscriminator(PluginMessage message) { Preconditions.checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL)); - byte[] data = message.getData(); - Preconditions.checkArgument(data.length >= 1); - return data[0]; + Preconditions.checkArgument(message.content().isReadable()); + return message.content().getByte(0); } /** @@ -44,7 +43,7 @@ class LegacyForgeUtil { .checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL), "message is not a FML HS plugin message"); - ByteBuf byteBuf = Unpooled.wrappedBuffer(message.getData()); + ByteBuf byteBuf = message.content().retainedSlice(); try { byte discriminator = byteBuf.readByte(); @@ -75,7 +74,7 @@ class LegacyForgeUtil { static PluginMessage resetPacket() { PluginMessage msg = new PluginMessage(); msg.setChannel(FORGE_LEGACY_HANDSHAKE_CHANNEL); - msg.setData(FORGE_LEGACY_HANDSHAKE_RESET_DATA.clone()); + msg.replace(Unpooled.wrappedBuffer(FORGE_LEGACY_HANDSHAKE_RESET_DATA.clone())); return msg; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessage.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessage.java index 26fb8cb70..a3dae879d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessage.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessage.java @@ -4,24 +4,24 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.checkerframework.checker.nullness.qual.Nullable; -public class LoginPluginMessage implements MinecraftPacket { +public class LoginPluginMessage extends DeferredByteBufHolder implements MinecraftPacket { private int id; private @Nullable String channel; - private ByteBuf data = Unpooled.EMPTY_BUFFER; public LoginPluginMessage() { - + super(null); } public LoginPluginMessage(int id, @Nullable String channel, ByteBuf data) { + super(data); this.id = id; this.channel = channel; - this.data = data; } public int getId() { @@ -35,16 +35,12 @@ public class LoginPluginMessage implements MinecraftPacket { return channel; } - public ByteBuf getData() { - return data; - } - @Override public String toString() { return "LoginPluginMessage{" + "id=" + id + ", channel='" + channel + '\'' - + ", data=" + data + + ", data=" + super.toString() + '}'; } @@ -53,9 +49,9 @@ public class LoginPluginMessage implements MinecraftPacket { this.id = ProtocolUtils.readVarInt(buf); this.channel = ProtocolUtils.readString(buf); if (buf.isReadable()) { - this.data = buf.readSlice(buf.readableBytes()); + this.replace(buf.readSlice(buf.readableBytes())); } else { - this.data = Unpooled.EMPTY_BUFFER; + this.replace(Unpooled.EMPTY_BUFFER); } } @@ -66,7 +62,7 @@ public class LoginPluginMessage implements MinecraftPacket { throw new IllegalStateException("Channel is not specified!"); } ProtocolUtils.writeString(buf, channel); - buf.writeBytes(data); + buf.writeBytes(content()); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java index 7c5045568..e0269f77a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessage.java @@ -1,21 +1,29 @@ package com.velocitypowered.proxy.protocol.packet; -import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY; import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.transformLegacyToModernChannel; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; -import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; +import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; -public class PluginMessage implements MinecraftPacket { +public class PluginMessage extends DeferredByteBufHolder implements MinecraftPacket { private @Nullable String channel; - private byte[] data = EMPTY_BYTE_ARRAY; + + public PluginMessage() { + super(null); + } + + public PluginMessage(String channel, + @MonotonicNonNull ByteBuf backing) { + super(backing); + this.channel = channel; + } public String getChannel() { if (channel == null) { @@ -28,19 +36,11 @@ public class PluginMessage implements MinecraftPacket { this.channel = channel; } - public byte[] getData() { - return data; - } - - public void setData(byte[] data) { - this.data = data; - } - @Override public String toString() { return "PluginMessage{" + "channel='" + channel + '\'' - + ", data=" + + ", data=" + super.toString() + '}'; } @@ -50,8 +50,7 @@ public class PluginMessage implements MinecraftPacket { if (version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) { this.channel = transformLegacyToModernChannel(this.channel); } - this.data = new byte[buf.readableBytes()]; - buf.readBytes(data); + this.replace(buf.readRetainedSlice(buf.readableBytes())); } @Override @@ -64,11 +63,51 @@ public class PluginMessage implements MinecraftPacket { } else { ProtocolUtils.writeString(buf, this.channel); } - buf.writeBytes(data); + buf.writeBytes(content()); } @Override public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public PluginMessage copy() { + return (PluginMessage) super.copy(); + } + + @Override + public PluginMessage duplicate() { + return (PluginMessage) super.duplicate(); + } + + @Override + public PluginMessage retainedDuplicate() { + return (PluginMessage) super.retainedDuplicate(); + } + + @Override + public PluginMessage replace(ByteBuf content) { + return (PluginMessage) super.replace(content); + } + + @Override + public PluginMessage retain() { + return (PluginMessage) super.retain(); + } + + @Override + public PluginMessage retain(int increment) { + return (PluginMessage) super.retain(increment); + } + + @Override + public PluginMessage touch() { + return (PluginMessage) super.touch(); + } + + @Override + public PluginMessage touch(Object hint) { + return (PluginMessage) super.touch(hint); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java new file mode 100644 index 000000000..6a35a4e5e --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/DeferredByteBufHolder.java @@ -0,0 +1,136 @@ +package com.velocitypowered.proxy.protocol.util; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufHolder; +import io.netty.util.IllegalReferenceCountException; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** + * A special-purpose implementation of {@code ByteBufHolder} that can defer accepting its buffer. + * This is required because Velocity packets are, for better or worse, mutable. + */ +public class DeferredByteBufHolder implements ByteBufHolder { + + @MonotonicNonNull + private ByteBuf backing; + + public DeferredByteBufHolder( + @MonotonicNonNull ByteBuf backing) { + this.backing = backing; + } + + @Override + public ByteBuf content() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + if (backing.refCnt() <= 0) { + throw new IllegalReferenceCountException(backing.refCnt()); + } + return backing; + } + + @Override + public ByteBufHolder copy() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + return new DeferredByteBufHolder(backing.copy()); + } + + @Override + public ByteBufHolder duplicate() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + return new DeferredByteBufHolder(backing.duplicate()); + } + + @Override + public ByteBufHolder retainedDuplicate() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + return new DeferredByteBufHolder(backing.retainedDuplicate()); + } + + @Override + public ByteBufHolder replace(ByteBuf content) { + if (content == null) { + throw new NullPointerException("content"); + } + this.backing = content; + return this; + } + + @Override + public int refCnt() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + return backing.refCnt(); + } + + @Override + public ByteBufHolder retain() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + backing.retain(); + return this; + } + + @Override + public ByteBufHolder retain(int increment) { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + backing.retain(increment); + return this; + } + + @Override + public ByteBufHolder touch() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + backing.touch(); + return this; + } + + @Override + public ByteBufHolder touch(Object hint) { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + backing.touch(hint); + return this; + } + + @Override + public boolean release() { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + return backing.release(); + } + + @Override + public boolean release(int decrement) { + if (backing == null) { + throw new IllegalStateException("Trying to obtain contents of holder with a null buffer"); + } + return backing.release(decrement); + } + + @Override + public String toString() { + String str = "DeferredByteBufHolder["; + if (backing == null) { + str += "null"; + } else { + str += backing.toString(); + } + return str + "]"; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index 9fef82a1f..c659826b7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -11,7 +11,9 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.packet.PluginMessage; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.util.ByteProcessor; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Locale; @@ -93,12 +95,12 @@ public class PluginMessageUtil { checkNotNull(message, "message"); checkArgument(isRegister(message) || isUnregister(message), "Unknown channel type %s", message.getChannel()); - if (message.getData().length == 0) { + if (!message.content().isReadable()) { // If we try to split this, we will get an one-element array with the empty string, which // has caused issues with 1.13+ compatibility. Just return an empty list. return ImmutableList.of(); } - String channels = new String(message.getData(), StandardCharsets.UTF_8); + String channels = message.content().toString(StandardCharsets.UTF_8); return ImmutableList.copyOf(channels.split("\0")); } @@ -114,10 +116,9 @@ public class PluginMessageUtil { Preconditions.checkArgument(channels.size() > 0, "no channels specified"); String channelName = protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0 ? REGISTER_CHANNEL : REGISTER_CHANNEL_LEGACY; - PluginMessage message = new PluginMessage(); - message.setChannel(channelName); - message.setData(String.join("\0", channels).getBytes(StandardCharsets.UTF_8)); - return message; + ByteBuf contents = Unpooled.buffer(); + contents.writeCharSequence(String.join("\0", channels), StandardCharsets.UTF_8); + return new PluginMessage(channelName, contents); } /** @@ -133,21 +134,11 @@ public class PluginMessageUtil { String toAppend = " (" + version.getName() + ")"; - byte[] rewrittenData; ByteBuf rewrittenBuf = Unpooled.buffer(); - try { - String currentBrand = ProtocolUtils.readString(Unpooled.wrappedBuffer(message.getData())); - ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend); - rewrittenData = new byte[rewrittenBuf.readableBytes()]; - rewrittenBuf.readBytes(rewrittenData); - } finally { - rewrittenBuf.release(); - } + String currentBrand = ProtocolUtils.readString(message.content().slice()); + ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend); - PluginMessage newMsg = new PluginMessage(); - newMsg.setChannel(message.getChannel()); - newMsg.setData(rewrittenData); - return newMsg; + return new PluginMessage(message.getChannel(), rewrittenBuf); } private static final Pattern INVALID_IDENTIFIER_REGEX = Pattern.compile("[^a-z0-9\\-_]*"); @@ -185,5 +176,4 @@ public class PluginMessageUtil { return "legacy:" + INVALID_IDENTIFIER_REGEX.matcher(lower).replaceAll(""); } } - }