Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Improve server list ping, especially for legacy MC versions.
Dieser Commit ist enthalten in:
Ursprung
df34c78b23
Commit
fdf5f27da6
@ -32,6 +32,12 @@ public interface ProxyServer {
|
|||||||
*/
|
*/
|
||||||
Collection<Player> getAllPlayers();
|
Collection<Player> getAllPlayers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of players currently connected to this proxy.
|
||||||
|
* @return the players on this proxy
|
||||||
|
*/
|
||||||
|
int getPlayerCount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a registered {@link ServerInfo} instance by its name.
|
* Retrieves a registered {@link ServerInfo} instance by its name.
|
||||||
* @param name the name of the server
|
* @param name the name of the server
|
||||||
|
@ -149,6 +149,11 @@ public class VelocityServer implements ProxyServer {
|
|||||||
return ImmutableList.copyOf(connectionsByUuid.values());
|
return ImmutableList.copyOf(connectionsByUuid.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPlayerCount() {
|
||||||
|
return connectionsByUuid.size();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<ServerInfo> getServerInfo(@Nonnull String name) {
|
public Optional<ServerInfo> getServerInfo(@Nonnull String name) {
|
||||||
Preconditions.checkNotNull(name, "name");
|
Preconditions.checkNotNull(name, "name");
|
||||||
|
@ -6,6 +6,7 @@ import com.velocitypowered.natives.encryption.VelocityCipherFactory;
|
|||||||
import com.velocitypowered.natives.util.Natives;
|
import com.velocitypowered.natives.util.Natives;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.protocol.PacketWrapper;
|
import com.velocitypowered.proxy.protocol.PacketWrapper;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.natives.encryption.JavaVelocityCipher;
|
import com.velocitypowered.natives.encryption.JavaVelocityCipher;
|
||||||
import com.velocitypowered.natives.encryption.VelocityCipher;
|
import com.velocitypowered.natives.encryption.VelocityCipher;
|
||||||
@ -167,8 +168,14 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
|
|||||||
|
|
||||||
public void setProtocolVersion(int protocolVersion) {
|
public void setProtocolVersion(int protocolVersion) {
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
this.channel.pipeline().get(MinecraftEncoder.class).setProtocolVersion(protocolVersion);
|
if (protocolVersion != ProtocolConstants.LEGACY) {
|
||||||
this.channel.pipeline().get(MinecraftDecoder.class).setProtocolVersion(protocolVersion);
|
this.channel.pipeline().get(MinecraftEncoder.class).setProtocolVersion(protocolVersion);
|
||||||
|
this.channel.pipeline().get(MinecraftDecoder.class).setProtocolVersion(protocolVersion);
|
||||||
|
} else {
|
||||||
|
// Legacy handshake handling
|
||||||
|
this.channel.pipeline().remove(MINECRAFT_ENCODER);
|
||||||
|
this.channel.pipeline().remove(MINECRAFT_DECODER);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MinecraftSessionHandler getSessionHandler() {
|
public MinecraftSessionHandler getSessionHandler() {
|
||||||
|
@ -4,7 +4,7 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
public interface MinecraftSessionHandler {
|
public interface MinecraftSessionHandler {
|
||||||
void handle(MinecraftPacket packet) throws Exception;
|
void handle(MinecraftPacket packet);
|
||||||
|
|
||||||
default void handleUnknown(ByteBuf buf) {
|
default void handleUnknown(ByteBuf buf) {
|
||||||
// No-op: we'll release the buffer later.
|
// No-op: we'll release the buffer later.
|
||||||
|
@ -1,14 +1,18 @@
|
|||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
|
import com.velocitypowered.proxy.data.ServerPing;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||||
import com.velocitypowered.proxy.protocol.packet.Disconnect;
|
import com.velocitypowered.proxy.protocol.packet.*;
|
||||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.TranslatableComponent;
|
import net.kyori.text.TranslatableComponent;
|
||||||
|
import net.kyori.text.format.TextColor;
|
||||||
|
|
||||||
public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
||||||
private final MinecraftConnection connection;
|
private final MinecraftConnection connection;
|
||||||
@ -19,6 +23,12 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MinecraftPacket packet) {
|
public void handle(MinecraftPacket packet) {
|
||||||
|
if (packet instanceof LegacyPing || packet instanceof LegacyHandshake) {
|
||||||
|
connection.setProtocolVersion(ProtocolConstants.LEGACY);
|
||||||
|
handleLegacy(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(packet instanceof Handshake)) {
|
if (!(packet instanceof Handshake)) {
|
||||||
throw new IllegalArgumentException("Did not expect packet " + packet.getClass().getName());
|
throw new IllegalArgumentException("Did not expect packet " + packet.getClass().getName());
|
||||||
}
|
}
|
||||||
@ -43,6 +53,21 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
|||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid state " + handshake.getNextStatus());
|
throw new IllegalArgumentException("Invalid state " + handshake.getNextStatus());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLegacy(MinecraftPacket packet) {
|
||||||
|
if (packet instanceof LegacyPing) {
|
||||||
|
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
|
||||||
|
ServerPing ping = new ServerPing(
|
||||||
|
new ServerPing.Version(ProtocolConstants.MAXIMUM_GENERIC_VERSION, "Velocity " + ProtocolConstants.SUPPORTED_GENERIC_VERSION_STRING),
|
||||||
|
new ServerPing.Players(VelocityServer.getServer().getPlayerCount(), configuration.getShowMaxPlayers()),
|
||||||
|
configuration.getMotdComponent(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
// The disconnect packet is the same as the server response one.
|
||||||
|
connection.closeWith(LegacyDisconnect.fromPingResponse(LegacyPingResponse.from(ping)));
|
||||||
|
} else if (packet instanceof LegacyHandshake) {
|
||||||
|
connection.closeWith(LegacyDisconnect.from(TextComponent.of("Your client is old, please upgrade!", TextColor.RED)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
@ -53,7 +54,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MinecraftPacket packet) throws Exception {
|
public void handle(MinecraftPacket packet) {
|
||||||
if (packet instanceof LoginPluginResponse) {
|
if (packet instanceof LoginPluginResponse) {
|
||||||
LoginPluginResponse lpr = (LoginPluginResponse) packet;
|
LoginPluginResponse lpr = (LoginPluginResponse) packet;
|
||||||
if (lpr.getId() == playerInfoId && lpr.isSuccess()) {
|
if (lpr.getId() == playerInfoId && lpr.isSuccess()) {
|
||||||
@ -75,34 +76,41 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
|||||||
handleSuccessfulLogin(GameProfile.forOfflinePlayer(login.getUsername()));
|
handleSuccessfulLogin(GameProfile.forOfflinePlayer(login.getUsername()));
|
||||||
}
|
}
|
||||||
} else if (packet instanceof EncryptionResponse) {
|
} else if (packet instanceof EncryptionResponse) {
|
||||||
KeyPair serverKeyPair = VelocityServer.getServer().getServerKeyPair();
|
try {
|
||||||
EncryptionResponse response = (EncryptionResponse) packet;
|
KeyPair serverKeyPair = VelocityServer.getServer().getServerKeyPair();
|
||||||
byte[] decryptedVerifyToken = EncryptionUtils.decryptRsa(serverKeyPair, response.getVerifyToken());
|
EncryptionResponse response = (EncryptionResponse) packet;
|
||||||
if (!Arrays.equals(verify, decryptedVerifyToken)) {
|
byte[] decryptedVerifyToken = EncryptionUtils.decryptRsa(serverKeyPair, response.getVerifyToken());
|
||||||
throw new IllegalStateException("Unable to successfully decrypt the verification token.");
|
if (!Arrays.equals(verify, decryptedVerifyToken)) {
|
||||||
|
throw new IllegalStateException("Unable to successfully decrypt the verification token.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] decryptedSharedSecret = EncryptionUtils.decryptRsa(serverKeyPair, response.getSharedSecret());
|
||||||
|
String serverId = EncryptionUtils.generateServerId(decryptedSharedSecret, serverKeyPair.getPublic());
|
||||||
|
|
||||||
|
String playerIp = ((InetSocketAddress) inbound.getChannel().remoteAddress()).getHostString();
|
||||||
|
VelocityServer.getServer().getHttpClient()
|
||||||
|
.get(new URL(String.format(MOJANG_SERVER_AUTH_URL, login.getUsername(), serverId, playerIp)))
|
||||||
|
.thenAcceptAsync(profileResponse -> {
|
||||||
|
try {
|
||||||
|
inbound.enableEncryption(decryptedSharedSecret);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameProfile profile = VelocityServer.GSON.fromJson(profileResponse, GameProfile.class);
|
||||||
|
handleSuccessfulLogin(profile);
|
||||||
|
}, inbound.getChannel().eventLoop())
|
||||||
|
.exceptionally(exception -> {
|
||||||
|
logger.error("Unable to enable encryption", exception);
|
||||||
|
inbound.close();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
logger.error("Unable to enable encryption", e);
|
||||||
|
inbound.close();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] decryptedSharedSecret = EncryptionUtils.decryptRsa(serverKeyPair, response.getSharedSecret());
|
|
||||||
String serverId = EncryptionUtils.generateServerId(decryptedSharedSecret, serverKeyPair.getPublic());
|
|
||||||
|
|
||||||
String playerIp = ((InetSocketAddress) inbound.getChannel().remoteAddress()).getHostString();
|
|
||||||
VelocityServer.getServer().getHttpClient()
|
|
||||||
.get(new URL(String.format(MOJANG_SERVER_AUTH_URL, login.getUsername(), serverId, playerIp)))
|
|
||||||
.thenAcceptAsync(profileResponse -> {
|
|
||||||
try {
|
|
||||||
inbound.enableEncryption(decryptedSharedSecret);
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
GameProfile profile = VelocityServer.GSON.fromJson(profileResponse, GameProfile.class);
|
|
||||||
handleSuccessfulLogin(profile);
|
|
||||||
}, inbound.getChannel().eventLoop())
|
|
||||||
.exceptionally(exception -> {
|
|
||||||
logger.error("Unable to enable encryption", exception);
|
|
||||||
inbound.close();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,9 +35,11 @@ public class StatusSessionHandler implements MinecraftSessionHandler {
|
|||||||
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
|
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
|
||||||
|
|
||||||
// Status request
|
// Status request
|
||||||
|
int shownVersion = ProtocolConstants.isSupported(connection.getProtocolVersion()) ? connection.getProtocolVersion() :
|
||||||
|
ProtocolConstants.MAXIMUM_GENERIC_VERSION;
|
||||||
ServerPing ping = new ServerPing(
|
ServerPing ping = new ServerPing(
|
||||||
new ServerPing.Version(connection.getProtocolVersion(), "Velocity " + ProtocolConstants.SUPPORTED_GENERIC_VERSION_STRING),
|
new ServerPing.Version(shownVersion, "Velocity " + ProtocolConstants.SUPPORTED_GENERIC_VERSION_STRING),
|
||||||
new ServerPing.Players(0, configuration.getShowMaxPlayers()),
|
new ServerPing.Players(VelocityServer.getServer().getPlayerCount(), configuration.getShowMaxPlayers()),
|
||||||
configuration.getMotdComponent(),
|
configuration.getMotdComponent(),
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,8 @@ package com.velocitypowered.proxy.protocol;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public enum ProtocolConstants { ;
|
public enum ProtocolConstants { ;
|
||||||
|
public static final int LEGACY = -1;
|
||||||
|
|
||||||
public static final int MINECRAFT_1_8 = 47;
|
public static final int MINECRAFT_1_8 = 47;
|
||||||
public static final int MINECRAFT_1_9 = 107;
|
public static final int MINECRAFT_1_9 = 107;
|
||||||
public static final int MINECRAFT_1_9_1 = 108;
|
public static final int MINECRAFT_1_9_1 = 108;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package com.velocitypowered.proxy.protocol.netty;
|
package com.velocitypowered.proxy.protocol.netty;
|
||||||
|
|
||||||
|
import com.velocitypowered.proxy.protocol.PacketWrapper;
|
||||||
|
import com.velocitypowered.proxy.protocol.packet.LegacyHandshake;
|
||||||
import com.velocitypowered.proxy.protocol.packet.LegacyPing;
|
import com.velocitypowered.proxy.protocol.packet.LegacyPing;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
|
|
||||||
@ -18,9 +21,12 @@ public class LegacyPingDecoder extends ByteToMessageDecoder {
|
|||||||
short second = in.getUnsignedByte(in.readerIndex() + 1);
|
short second = in.getUnsignedByte(in.readerIndex() + 1);
|
||||||
if (first == 0xfe && second == 0x01) {
|
if (first == 0xfe && second == 0x01) {
|
||||||
in.skipBytes(in.readableBytes());
|
in.skipBytes(in.readableBytes());
|
||||||
out.add(new LegacyPing());
|
out.add(new PacketWrapper(new LegacyPing(), Unpooled.EMPTY_BUFFER));
|
||||||
|
} else if (first == 0x02) {
|
||||||
|
in.skipBytes(in.readableBytes());
|
||||||
|
out.add(new PacketWrapper(new LegacyHandshake(), Unpooled.EMPTY_BUFFER));
|
||||||
|
} else {
|
||||||
|
ctx.pipeline().remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.pipeline().remove(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,27 @@
|
|||||||
package com.velocitypowered.proxy.protocol.netty;
|
package com.velocitypowered.proxy.protocol.netty;
|
||||||
|
|
||||||
import com.google.common.base.Joiner;
|
import com.velocitypowered.proxy.protocol.packet.LegacyDisconnect;
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.velocitypowered.proxy.protocol.packet.LegacyPingResponse;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
@ChannelHandler.Sharable
|
||||||
public class LegacyPingEncoder extends MessageToByteEncoder<LegacyPingResponse> {
|
public class LegacyPingEncoder extends MessageToByteEncoder<LegacyDisconnect> {
|
||||||
public static final LegacyPingEncoder INSTANCE = new LegacyPingEncoder();
|
public static final LegacyPingEncoder INSTANCE = new LegacyPingEncoder();
|
||||||
|
|
||||||
private LegacyPingEncoder() {}
|
private LegacyPingEncoder() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(ChannelHandlerContext ctx, LegacyPingResponse msg, ByteBuf out) throws Exception {
|
protected void encode(ChannelHandlerContext ctx, LegacyDisconnect msg, ByteBuf out) throws Exception {
|
||||||
out.writeByte(0xff);
|
out.writeByte(0xff);
|
||||||
String serializedResponse = serialize(msg);
|
writeLegacyString(out, msg.getReason());
|
||||||
out.writeShort(serializedResponse.length());
|
|
||||||
out.writeBytes(serializedResponse.getBytes(StandardCharsets.UTF_16BE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String serialize(LegacyPingResponse response) {
|
private static void writeLegacyString(ByteBuf out, String string) {
|
||||||
List<String> parts = ImmutableList.of(
|
out.writeShort(string.length());
|
||||||
"§1",
|
out.writeBytes(string.getBytes(StandardCharsets.UTF_16BE));
|
||||||
Integer.toString(response.getProtocolVersion()),
|
|
||||||
response.getServerVersion(),
|
|
||||||
response.getMotd(),
|
|
||||||
Integer.toString(response.getPlayersOnline()),
|
|
||||||
Integer.toString(response.getPlayersMax())
|
|
||||||
);
|
|
||||||
return Joiner.on('\0').join(parts);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.velocitypowered.proxy.protocol.packet;
|
||||||
|
|
||||||
|
import net.kyori.text.TextComponent;
|
||||||
|
import net.kyori.text.serializer.ComponentSerializers;
|
||||||
|
|
||||||
|
public class LegacyDisconnect {
|
||||||
|
private final String reason;
|
||||||
|
|
||||||
|
public LegacyDisconnect(String reason) {
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LegacyDisconnect fromPingResponse(LegacyPingResponse response) {
|
||||||
|
String kickMessage = String.join("\0",
|
||||||
|
"§1",
|
||||||
|
Integer.toString(response.getProtocolVersion()),
|
||||||
|
response.getServerVersion(),
|
||||||
|
response.getMotd(),
|
||||||
|
Integer.toString(response.getPlayersOnline()),
|
||||||
|
Integer.toString(response.getPlayersMax())
|
||||||
|
);
|
||||||
|
return new LegacyDisconnect(kickMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LegacyDisconnect from(TextComponent component) {
|
||||||
|
return new LegacyDisconnect(ComponentSerializers.LEGACY.serialize(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package com.velocitypowered.proxy.protocol.packet;
|
||||||
|
|
||||||
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
public class LegacyHandshake implements MinecraftPacket {
|
||||||
|
@Override
|
||||||
|
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,17 @@
|
|||||||
package com.velocitypowered.proxy.protocol.packet;
|
package com.velocitypowered.proxy.protocol.packet;
|
||||||
|
|
||||||
public class LegacyPing {
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
|
import com.velocitypowered.proxy.protocol.ProtocolConstants;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
public class LegacyPing implements MinecraftPacket {
|
||||||
|
@Override
|
||||||
|
public void decode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren