Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-24 23:30:26 +01:00
Convert Velocity to use ByteBuf-derived plugin message packets
Only tested with 1.12.2, not 1.13 or Forge
Dieser Commit ist enthalten in:
Ursprung
39c505f6fd
Commit
11a86e9bb9
@ -1,5 +1,7 @@
|
|||||||
package com.velocitypowered.proxy;
|
package com.velocitypowered.proxy;
|
||||||
|
|
||||||
|
import io.netty.util.ResourceLeakDetector;
|
||||||
|
import io.netty.util.ResourceLeakDetector.Level;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@ -35,6 +37,8 @@ public class Velocity {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResourceLeakDetector.setLevel(Level.PARANOID);
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
|
||||||
VelocityServer server = new VelocityServer(options);
|
VelocityServer server = new VelocityServer(options);
|
||||||
|
@ -131,8 +131,8 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
|||||||
try {
|
try {
|
||||||
sessionHandler.exception(cause);
|
sessionHandler.exception(cause);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.error("{}: exception handling exception", (association != null ? association :
|
logger.error("{}: exception handling exception in {}",
|
||||||
channel.remoteAddress()), cause);
|
(association != null ? association : channel.remoteAddress()), sessionHandler, cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
|||||||
if (cause instanceof ReadTimeoutException) {
|
if (cause instanceof ReadTimeoutException) {
|
||||||
logger.error("{}: read timed out", association);
|
logger.error("{}: read timed out", association);
|
||||||
} else {
|
} else {
|
||||||
logger.error("{}: exception encountered", association, cause);
|
logger.error("{}: exception encountered in {}", association, sessionHandler, cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
|
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
|
||||||
public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||||
|
|
||||||
@ -119,12 +121,15 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] copy = ByteBufUtil.getBytes(packet.content());
|
||||||
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id,
|
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id,
|
||||||
packet.getData());
|
copy);
|
||||||
server.getEventManager().fire(event)
|
server.getEventManager().fire(event)
|
||||||
.thenAcceptAsync(pme -> {
|
.thenAcceptAsync(pme -> {
|
||||||
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
|
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
|
||||||
playerConnection.write(packet);
|
PluginMessage copied = new PluginMessage(packet.getChannel(),
|
||||||
|
Unpooled.wrappedBuffer(copy));
|
||||||
|
playerConnection.write(copied);
|
||||||
}
|
}
|
||||||
}, playerConnection.eventLoop());
|
}, playerConnection.eventLoop());
|
||||||
return true;
|
return true;
|
||||||
@ -163,6 +168,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleGeneric(MinecraftPacket packet) {
|
public void handleGeneric(MinecraftPacket packet) {
|
||||||
|
if (packet instanceof PluginMessage) {
|
||||||
|
((PluginMessage) packet).retain();
|
||||||
|
}
|
||||||
playerConnection.write(packet);
|
playerConnection.write(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import com.velocitypowered.proxy.protocol.packet.Handshake;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||||
import com.velocitypowered.proxy.protocol.packet.ServerLogin;
|
import com.velocitypowered.proxy.protocol.packet.ServerLogin;
|
||||||
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
import com.velocitypowered.proxy.server.VelocityRegisteredServer;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
@ -212,9 +213,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
|
|||||||
|
|
||||||
MinecraftConnection mc = ensureConnected();
|
MinecraftConnection mc = ensureConnected();
|
||||||
|
|
||||||
PluginMessage message = new PluginMessage();
|
PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data));
|
||||||
message.setChannel(identifier.getId());
|
|
||||||
message.setData(data);
|
|
||||||
mc.write(message);
|
mc.write(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse.Offer;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.TitlePacket;
|
import com.velocitypowered.proxy.protocol.packet.TitlePacket;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
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.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
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
|
@Override
|
||||||
public boolean handle(KeepAlive packet) {
|
public boolean handle(KeepAlive packet) {
|
||||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||||
@ -213,10 +223,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
+ "ready. Channel: {}. Packet discarded.", packet.getChannel());
|
+ "ready. Channel: {}. Packet discarded.", packet.getChannel());
|
||||||
} else if (PluginMessageUtil.isRegister(packet)) {
|
} else if (PluginMessageUtil.isRegister(packet)) {
|
||||||
player.getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
|
player.getKnownChannels().addAll(PluginMessageUtil.getChannels(packet));
|
||||||
backendConn.write(packet);
|
backendConn.write(packet.retain());
|
||||||
} else if (PluginMessageUtil.isUnregister(packet)) {
|
} else if (PluginMessageUtil.isUnregister(packet)) {
|
||||||
player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
|
player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet));
|
||||||
backendConn.write(packet);
|
backendConn.write(packet.retain());
|
||||||
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
} else if (PluginMessageUtil.isMcBrand(packet)) {
|
||||||
backendConn.write(PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion()));
|
backendConn.write(PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion()));
|
||||||
} else {
|
} else {
|
||||||
@ -236,16 +246,23 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
// but further aggravated by Velocity. To work around these issues, we will queue any
|
// 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
|
// 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.
|
// 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 {
|
} else {
|
||||||
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
backendConn.write(packet);
|
backendConn.write(packet.retain());
|
||||||
} else {
|
} else {
|
||||||
|
byte[] copy = ByteBufUtil.getBytes(packet.content());
|
||||||
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id,
|
PluginMessageEvent event = new PluginMessageEvent(player, serverConn, id,
|
||||||
packet.getData());
|
ByteBufUtil.getBytes(packet.content()));
|
||||||
server.getEventManager().fire(event).thenAcceptAsync(pme -> backendConn.write(packet),
|
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
|
||||||
backendConn.eventLoop());
|
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();
|
MinecraftConnection smc = serverConnection.getConnection();
|
||||||
if (smc != null && serverConnection.getPhase().consideredComplete()) {
|
if (smc != null && serverConnection.getPhase().consideredComplete()) {
|
||||||
|
if (packet instanceof PluginMessage) {
|
||||||
|
((PluginMessage) packet).retain();
|
||||||
|
}
|
||||||
smc.write(packet);
|
smc.write(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ import com.velocitypowered.proxy.tablist.VelocityTabList;
|
|||||||
import com.velocitypowered.proxy.util.VelocityMessages;
|
import com.velocitypowered.proxy.util.VelocityMessages;
|
||||||
import com.velocitypowered.proxy.util.collect.CappedSet;
|
import com.velocitypowered.proxy.util.collect.CappedSet;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -570,9 +571,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
|||||||
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
|
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) {
|
||||||
Preconditions.checkNotNull(identifier, "identifier");
|
Preconditions.checkNotNull(identifier, "identifier");
|
||||||
Preconditions.checkNotNull(data, "data");
|
Preconditions.checkNotNull(data, "data");
|
||||||
PluginMessage message = new PluginMessage();
|
PluginMessage message = new PluginMessage(identifier.getId(), Unpooled.wrappedBuffer(data));
|
||||||
message.setChannel(identifier.getId());
|
|
||||||
message.setData(data);
|
|
||||||
minecraftConnection.write(message);
|
minecraftConnection.write(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ public enum LegacyForgeHandshakeBackendPhase implements BackendConnectionPhase {
|
|||||||
serverConnection.setConnectionPhase(newPhase);
|
serverConnection.setConnectionPhase(newPhase);
|
||||||
|
|
||||||
// Write the packet to the player, we don't need it now.
|
// Write the packet to the player, we don't need it now.
|
||||||
player.getMinecraftConnection().write(message);
|
player.getMinecraftConnection().write(message.retain());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ public enum LegacyForgeHandshakeClientPhase implements ClientConnectionPhase {
|
|||||||
PluginMessage message,
|
PluginMessage message,
|
||||||
MinecraftConnection backendConn) {
|
MinecraftConnection backendConn) {
|
||||||
// Send the packet on to the server.
|
// Send the packet on to the server.
|
||||||
backendConn.write(message);
|
backendConn.write(message.retain());
|
||||||
|
|
||||||
// We handled the packet. No need to continue processing.
|
// We handled the packet. No need to continue processing.
|
||||||
return true;
|
return true;
|
||||||
|
@ -27,9 +27,8 @@ class LegacyForgeUtil {
|
|||||||
*/
|
*/
|
||||||
static byte getHandshakePacketDiscriminator(PluginMessage message) {
|
static byte getHandshakePacketDiscriminator(PluginMessage message) {
|
||||||
Preconditions.checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL));
|
Preconditions.checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL));
|
||||||
byte[] data = message.getData();
|
Preconditions.checkArgument(message.content().isReadable());
|
||||||
Preconditions.checkArgument(data.length >= 1);
|
return message.content().getByte(0);
|
||||||
return data[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +43,7 @@ class LegacyForgeUtil {
|
|||||||
.checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL),
|
.checkArgument(message.getChannel().equals(FORGE_LEGACY_HANDSHAKE_CHANNEL),
|
||||||
"message is not a FML HS plugin message");
|
"message is not a FML HS plugin message");
|
||||||
|
|
||||||
ByteBuf byteBuf = Unpooled.wrappedBuffer(message.getData());
|
ByteBuf byteBuf = message.content().retainedSlice();
|
||||||
try {
|
try {
|
||||||
byte discriminator = byteBuf.readByte();
|
byte discriminator = byteBuf.readByte();
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ class LegacyForgeUtil {
|
|||||||
static PluginMessage resetPacket() {
|
static PluginMessage resetPacket() {
|
||||||
PluginMessage msg = new PluginMessage();
|
PluginMessage msg = new PluginMessage();
|
||||||
msg.setChannel(FORGE_LEGACY_HANDSHAKE_CHANNEL);
|
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;
|
return msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,24 @@ import com.velocitypowered.api.network.ProtocolVersion;
|
|||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
|
import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class LoginPluginMessage implements MinecraftPacket {
|
public class LoginPluginMessage extends DeferredByteBufHolder implements MinecraftPacket {
|
||||||
|
|
||||||
private int id;
|
private int id;
|
||||||
private @Nullable String channel;
|
private @Nullable String channel;
|
||||||
private ByteBuf data = Unpooled.EMPTY_BUFFER;
|
|
||||||
|
|
||||||
public LoginPluginMessage() {
|
public LoginPluginMessage() {
|
||||||
|
super(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoginPluginMessage(int id, @Nullable String channel, ByteBuf data) {
|
public LoginPluginMessage(int id, @Nullable String channel, ByteBuf data) {
|
||||||
|
super(data);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.data = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
@ -35,16 +35,12 @@ public class LoginPluginMessage implements MinecraftPacket {
|
|||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteBuf getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "LoginPluginMessage{"
|
return "LoginPluginMessage{"
|
||||||
+ "id=" + id
|
+ "id=" + id
|
||||||
+ ", channel='" + channel + '\''
|
+ ", channel='" + channel + '\''
|
||||||
+ ", data=" + data
|
+ ", data=" + super.toString()
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,9 +49,9 @@ public class LoginPluginMessage implements MinecraftPacket {
|
|||||||
this.id = ProtocolUtils.readVarInt(buf);
|
this.id = ProtocolUtils.readVarInt(buf);
|
||||||
this.channel = ProtocolUtils.readString(buf);
|
this.channel = ProtocolUtils.readString(buf);
|
||||||
if (buf.isReadable()) {
|
if (buf.isReadable()) {
|
||||||
this.data = buf.readSlice(buf.readableBytes());
|
this.replace(buf.readSlice(buf.readableBytes()));
|
||||||
} else {
|
} 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!");
|
throw new IllegalStateException("Channel is not specified!");
|
||||||
}
|
}
|
||||||
ProtocolUtils.writeString(buf, channel);
|
ProtocolUtils.writeString(buf, channel);
|
||||||
buf.writeBytes(data);
|
buf.writeBytes(content());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
package com.velocitypowered.proxy.protocol.packet;
|
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 static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.transformLegacyToModernChannel;
|
||||||
|
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
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.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class PluginMessage implements MinecraftPacket {
|
public class PluginMessage extends DeferredByteBufHolder implements MinecraftPacket {
|
||||||
|
|
||||||
private @Nullable String channel;
|
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() {
|
public String getChannel() {
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
@ -28,19 +36,11 @@ public class PluginMessage implements MinecraftPacket {
|
|||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(byte[] data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PluginMessage{"
|
return "PluginMessage{"
|
||||||
+ "channel='" + channel + '\''
|
+ "channel='" + channel + '\''
|
||||||
+ ", data=<removed>"
|
+ ", data=" + super.toString()
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +50,7 @@ public class PluginMessage implements MinecraftPacket {
|
|||||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) {
|
if (version.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0) {
|
||||||
this.channel = transformLegacyToModernChannel(this.channel);
|
this.channel = transformLegacyToModernChannel(this.channel);
|
||||||
}
|
}
|
||||||
this.data = new byte[buf.readableBytes()];
|
this.replace(buf.readRetainedSlice(buf.readableBytes()));
|
||||||
buf.readBytes(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -64,11 +63,51 @@ public class PluginMessage implements MinecraftPacket {
|
|||||||
} else {
|
} else {
|
||||||
ProtocolUtils.writeString(buf, this.channel);
|
ProtocolUtils.writeString(buf, this.channel);
|
||||||
}
|
}
|
||||||
buf.writeBytes(data);
|
buf.writeBytes(content());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(MinecraftSessionHandler handler) {
|
public boolean handle(MinecraftSessionHandler handler) {
|
||||||
return handler.handle(this);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 + "]";
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,9 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
import com.velocitypowered.proxy.protocol.packet.PluginMessage;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.util.ByteProcessor;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -93,12 +95,12 @@ public class PluginMessageUtil {
|
|||||||
checkNotNull(message, "message");
|
checkNotNull(message, "message");
|
||||||
checkArgument(isRegister(message) || isUnregister(message), "Unknown channel type %s",
|
checkArgument(isRegister(message) || isUnregister(message), "Unknown channel type %s",
|
||||||
message.getChannel());
|
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
|
// 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.
|
// has caused issues with 1.13+ compatibility. Just return an empty list.
|
||||||
return ImmutableList.of();
|
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"));
|
return ImmutableList.copyOf(channels.split("\0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,10 +116,9 @@ public class PluginMessageUtil {
|
|||||||
Preconditions.checkArgument(channels.size() > 0, "no channels specified");
|
Preconditions.checkArgument(channels.size() > 0, "no channels specified");
|
||||||
String channelName = protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0
|
String channelName = protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_13) >= 0
|
||||||
? REGISTER_CHANNEL : REGISTER_CHANNEL_LEGACY;
|
? REGISTER_CHANNEL : REGISTER_CHANNEL_LEGACY;
|
||||||
PluginMessage message = new PluginMessage();
|
ByteBuf contents = Unpooled.buffer();
|
||||||
message.setChannel(channelName);
|
contents.writeCharSequence(String.join("\0", channels), StandardCharsets.UTF_8);
|
||||||
message.setData(String.join("\0", channels).getBytes(StandardCharsets.UTF_8));
|
return new PluginMessage(channelName, contents);
|
||||||
return message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,21 +134,11 @@ public class PluginMessageUtil {
|
|||||||
|
|
||||||
String toAppend = " (" + version.getName() + ")";
|
String toAppend = " (" + version.getName() + ")";
|
||||||
|
|
||||||
byte[] rewrittenData;
|
|
||||||
ByteBuf rewrittenBuf = Unpooled.buffer();
|
ByteBuf rewrittenBuf = Unpooled.buffer();
|
||||||
try {
|
String currentBrand = ProtocolUtils.readString(message.content().slice());
|
||||||
String currentBrand = ProtocolUtils.readString(Unpooled.wrappedBuffer(message.getData()));
|
|
||||||
ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend);
|
ProtocolUtils.writeString(rewrittenBuf, currentBrand + toAppend);
|
||||||
rewrittenData = new byte[rewrittenBuf.readableBytes()];
|
|
||||||
rewrittenBuf.readBytes(rewrittenData);
|
|
||||||
} finally {
|
|
||||||
rewrittenBuf.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginMessage newMsg = new PluginMessage();
|
return new PluginMessage(message.getChannel(), rewrittenBuf);
|
||||||
newMsg.setChannel(message.getChannel());
|
|
||||||
newMsg.setData(rewrittenData);
|
|
||||||
return newMsg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern INVALID_IDENTIFIER_REGEX = Pattern.compile("[^a-z0-9\\-_]*");
|
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("");
|
return "legacy:" + INVALID_IDENTIFIER_REGEX.matcher(lower).replaceAll("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren