13
0
geforkt von Mirrors/Velocity

Merge branch 'Xernium-future/1.17' into dev/3.0.0

# Conflicts:
#	api/src/main/java/com/velocitypowered/api/proxy/ProxyServer.java
#	proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java
Dieser Commit ist enthalten in:
Andrew Steinborn 2021-06-06 03:48:49 -04:00
Commit b1b3882fab
26 geänderte Dateien mit 1337 neuen und 251 gelöschten Zeilen

Datei anzeigen

@ -8,7 +8,11 @@
package com.velocitypowered.api.event.player; package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* This event is fired when the status of a resource pack sent to the player by the server is * This event is fired when the status of a resource pack sent to the player by the server is
@ -18,10 +22,29 @@ public class PlayerResourcePackStatusEvent {
private final Player player; private final Player player;
private final Status status; private final Status status;
private final @MonotonicNonNull ResourcePackInfo packInfo;
private boolean overwriteKick;
/**
* Instantiates this event.
* @deprecated Use {@link PlayerResourcePackStatusEvent#PlayerResourcePackStatusEvent
* (Player, Status, ResourcePackInfo)} instead.
*/
@Deprecated
public PlayerResourcePackStatusEvent(Player player, Status status) { public PlayerResourcePackStatusEvent(Player player, Status status) {
this.player = Preconditions.checkNotNull(player, "player"); this.player = Preconditions.checkNotNull(player, "player");
this.status = Preconditions.checkNotNull(status, "status"); this.status = Preconditions.checkNotNull(status, "status");
this.packInfo = null;
}
/**
* Instantiates this event.
*/
public PlayerResourcePackStatusEvent(Player player, Status status, ResourcePackInfo packInfo) {
this.player = Preconditions.checkNotNull(player, "player");
this.status = Preconditions.checkNotNull(status, "status");
this.packInfo = packInfo;
} }
/** /**
@ -42,11 +65,49 @@ public class PlayerResourcePackStatusEvent {
return status; return status;
} }
/**
* Returns the {@link ResourcePackInfo} this response is for.
*
* @return the resource-pack info or null if no request was recorded
*/
@Nullable
public ResourcePackInfo getPackInfo() {
return packInfo;
}
/**
* Gets whether or not to override the kick resulting from
* {@link ResourcePackInfo#getShouldForce()} being true.
*
* @return whether or not to overwrite the result
*/
public boolean isOverwriteKick() {
return overwriteKick;
}
/**
* Set to true to prevent {@link ResourcePackInfo#getShouldForce()}
* from kicking the player.
* Overwriting this kick is only possible on versions older than 1.17,
* as the client or server will enforce this regardless. Cancelling the resulting
* kick-events will not prevent the player from disconnecting from the proxy.
*
* @param overwriteKick whether or not to cancel the kick
* @throws IllegalArgumentException if the player version is 1.17 or newer
*/
public void setOverwriteKick(boolean overwriteKick) {
Preconditions.checkArgument(player.getProtocolVersion()
.compareTo(ProtocolVersion.MINECRAFT_1_17) < 0,
"overwriteKick is not supported on 1.17 or newer");
this.overwriteKick = overwriteKick;
}
@Override @Override
public String toString() { public String toString() {
return "PlayerResourcePackStatusEvent{" return "PlayerResourcePackStatusEvent{"
+ "player=" + player + "player=" + player
+ ", status=" + status + ", status=" + status
+ ", packInfo=" + packInfo
+ '}'; + '}';
} }

Datei anzeigen

@ -53,7 +53,8 @@ public enum ProtocolVersion {
MINECRAFT_1_16_1(736, "1.16.1"), MINECRAFT_1_16_1(736, "1.16.1"),
MINECRAFT_1_16_2(751, "1.16.2"), MINECRAFT_1_16_2(751, "1.16.2"),
MINECRAFT_1_16_3(753, "1.16.3"), MINECRAFT_1_16_3(753, "1.16.3"),
MINECRAFT_1_16_4(754, "1.16.4", "1.16.5"); MINECRAFT_1_16_4(754, "1.16.4", "1.16.5"),
MINECRAFT_1_17(-1, 34, "1.17"); // Snapshot: 1.17-rc1, future protocol: 755
private static final int SNAPSHOT_BIT = 30; private static final int SNAPSHOT_BIT = 30;

Datei anzeigen

@ -13,6 +13,7 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource; import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
@ -22,6 +23,7 @@ import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* Represents a player who is connected to the proxy. * Represents a player who is connected to the proxy.
@ -158,7 +160,9 @@ public interface Player extends CommandSource, Identified, InboundConnection,
* sent resource pack, subscribe to {@link PlayerResourcePackStatusEvent}. * sent resource pack, subscribe to {@link PlayerResourcePackStatusEvent}.
* *
* @param url the URL for the resource pack * @param url the URL for the resource pack
* @deprecated Use {@link #sendResourcePackOffer(ResourcePackInfo)} instead
*/ */
@Deprecated
void sendResourcePack(String url); void sendResourcePack(String url);
/** /**
@ -168,9 +172,41 @@ public interface Player extends CommandSource, Identified, InboundConnection,
* *
* @param url the URL for the resource pack * @param url the URL for the resource pack
* @param hash the SHA-1 hash value for the resource pack * @param hash the SHA-1 hash value for the resource pack
* @deprecated Use {@link #sendResourcePackOffer(ResourcePackInfo)} instead
*/ */
@Deprecated
void sendResourcePack(String url, byte[] hash); void sendResourcePack(String url, byte[] hash);
/**
* Queues and sends a new Resource-pack offer to the player.
* To monitor the status of the sent resource pack, subscribe to
* {@link PlayerResourcePackStatusEvent}.
* To create a {@link ResourcePackInfo} use the
* {@link ProxyServer#createResourcePackBuilder(String)} builder.
*
* @param packInfo the resource-pack in question
*/
void sendResourcePackOffer(ResourcePackInfo packInfo);
/**
* Gets the {@link ResourcePackInfo} of the currently applied
* resource-pack or null if none.
*
* @return the applied resource pack or null if none.
*/
@Nullable
ResourcePackInfo getAppliedResourcePack();
/**
* Gets the {@link ResourcePackInfo} of the resource pack
* the user is currently downloading or is currently
* prompted to install or null if none.
*
* @return the pending resource pack or null if none
*/
@Nullable
ResourcePackInfo getPendingResourcePack();
/** /**
* <strong>Note that this method does not send a plugin message to the server the player * <strong>Note that this method does not send a plugin message to the server the player
* is connected to.</strong> You should only use this method if you are trying to communicate * is connected to.</strong> You should only use this method if you are trying to communicate

Datei anzeigen

@ -13,6 +13,7 @@ import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.config.ProxyConfig; import com.velocitypowered.api.proxy.config.ProxyConfig;
import com.velocitypowered.api.proxy.messages.ChannelRegistrar; import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.scheduler.Scheduler; import com.velocitypowered.api.scheduler.Scheduler;
@ -186,4 +187,26 @@ public interface ProxyServer extends Audience {
* @return the proxy version * @return the proxy version
*/ */
ProxyVersion getVersion(); ProxyVersion getVersion();
/**
* Creates a builder to build a {@link ResourcePackInfo} instance for use with
* {@link com.velocitypowered.api.proxy.Player#sendResourcePackOffer(ResourcePackInfo)}.
*
* <p>Note: The resource-pack location should always:
* - Use HTTPS with a valid certificate.
* - Be in a crawler-accessible location. Having it behind Cloudflare or other DoS/Bot/crawler
* protection may cause issues in downloading.
* - Be on a web-server with enough bandwidth and reliable connection
* so the download does not time out or fail.</p>
*
* <p>Do also make sure that the resource pack is in the correct format for the version
* of the client. It is also highly recommended to always provide the resource-pack SHA-1 hash
* of the resource pack with {@link ResourcePackInfo.Builder#setHash(byte[])}
* whenever possible to save bandwidth. If a hash is present the client will first check
* if it already has a resource pack by that hash cached.</p>
*
* @param url The url where the resource pack can be found
* @return a ResourcePackInfo builder
*/
ResourcePackInfo.Builder createResourcePackBuilder(String url);
} }

Datei anzeigen

@ -0,0 +1,120 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* The Velocity API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the api top-level directory.
*/
package com.velocitypowered.api.proxy.player;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface ResourcePackInfo {
/**
* Gets the link the resource-pack can be found at.
*
* @return the location of the resource-pack
*/
String getUrl();
/**
* Gets the {@link Component} that is displayed on the resource-pack prompt.
* This is only displayed if the client version is 1.17 or newer.
*
* @return the prompt if present or null otherwise
*/
@Nullable
Component getPrompt();
/**
* Gets whether or not the acceptance of the resource-pack is enforced.
* See {@link Builder#setShouldForce(boolean)} for more information.
*
* @return whether or not to force usage of this resource-pack
*/
boolean getShouldForce();
/**
* Gets the SHA-1 hash of the resource-pack
* See {@link Builder#setHash(byte[])} for more information.
*
* @return the hash if present or null otherwise
*/
@Nullable
byte[] getHash();
/**
* Gets the {@link Origin} of the resource-pack.
*
* @return the origin of the resource pack
*/
Origin getOrigin();
interface Builder {
/**
* Sets the resource-pack as required to play on the network.
* This feature was introduced in 1.17.
* Setting this to true has one of two effects:
* If the client is on 1.17 or newer:
* - The resource-pack prompt will display without a decline button
* - Accept or disconnect are the only available options but players may still press escape.
* - Forces the resource-pack offer prompt to display even if the player has
* previously declined or disabled resource packs
* - The player will be disconnected from the network if they close/skip the prompt.
* If the client is on a version older than 1.17:
* - If the player accepts the resource pack or has previously accepted a resource-pack
* then nothing else will happen.
* - If the player declines the resource pack or has previously declined a resource-pack
* the player will be disconnected from the network
*
* @param shouldForce whether or not to force the client to accept the resource pack
*/
Builder setShouldForce(boolean shouldForce);
/**
* Sets the SHA-1 hash of the provided resource pack.
* Note: It is recommended to always set this hash.
* If this hash is not set/ not present then the client will always download
* the resource pack even if it may still be cached. By having this hash present,
* the client will check first whether or not a resource pack by this hash is cached
* before downloading.
*
* @param hash the SHA-1 hash of the resource-pack
*/
Builder setHash(@Nullable byte[] hash);
/**
* Sets a {@link Component} to display on the download prompt.
* This will only display if the client version is 1.17 or newer.
*
* @param prompt the component to display
*/
Builder setPrompt(@Nullable Component prompt);
/**
* Builds the {@link ResourcePackInfo} from the provided info for use with
* {@link com.velocitypowered.api.proxy.Player#sendResourcePackOffer(ResourcePackInfo)}.
* Note: Some features may be version-dependent. Check before use.
*
* @return a ResourcePackInfo instance from the provided information
*/
ResourcePackInfo build();
}
/**
* Represents the origin of the resource-pack.
*/
enum Origin {
/**
* Resource-pack originated from the downstream server.
*/
DOWNSTREAM_SERVER,
/**
* The resource-pack originated from a plugin on this proxy.
*/
PLUGIN_ON_PROXY
}
}

Datei anzeigen

@ -31,6 +31,7 @@ import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginManager; import com.velocitypowered.api.plugin.PluginManager;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.util.Favicon; import com.velocitypowered.api.util.Favicon;
@ -43,6 +44,7 @@ import com.velocitypowered.proxy.command.builtin.ShutdownCommand;
import com.velocitypowered.proxy.command.builtin.VelocityCommand; import com.velocitypowered.proxy.command.builtin.VelocityCommand;
import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
import com.velocitypowered.proxy.console.VelocityConsole; import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.event.VelocityEventManager; import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.network.ConnectionManager;
@ -660,4 +662,9 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
return version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0 ? POST_1_16_PING_SERIALIZER return version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0 ? POST_1_16_PING_SERIALIZER
: PRE_1_16_PING_SERIALIZER; : PRE_1_16_PING_SERIALIZER;
} }
@Override
public ResourcePackInfo.Builder createResourcePackBuilder(String url) {
return new VelocityResourcePackInfo.BuilderImpl(url);
}
} }

Datei anzeigen

@ -46,7 +46,12 @@ import com.velocitypowered.proxy.protocol.packet.StatusRequest;
import com.velocitypowered.proxy.protocol.packet.StatusResponse; import com.velocitypowered.proxy.protocol.packet.StatusResponse;
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest; import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse; import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
import com.velocitypowered.proxy.protocol.packet.TitlePacket; import com.velocitypowered.proxy.protocol.packet.title.LegacyTitlePacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleActionbarPacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleClearPacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleSubtitlePacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleTextPacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleTimesPacket;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public interface MinecraftSessionHandler { public interface MinecraftSessionHandler {
@ -191,7 +196,27 @@ public interface MinecraftSessionHandler {
return false; return false;
} }
default boolean handle(TitlePacket packet) { default boolean handle(LegacyTitlePacket packet) {
return false;
}
default boolean handle(TitleTextPacket packet) {
return false;
}
default boolean handle(TitleSubtitlePacket packet) {
return false;
}
default boolean handle(TitleActionbarPacket packet) {
return false;
}
default boolean handle(TitleTimesPacket packet) {
return false;
}
default boolean handle(TitleClearPacket packet) {
return false; return false;
} }

Datei anzeigen

@ -19,6 +19,7 @@ package com.velocitypowered.proxy.connection.backend;
import static com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder.getBungeeCordChannel; import static com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder.getBungeeCordChannel;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
@ -28,10 +29,12 @@ import com.velocitypowered.api.event.command.PlayerAvailableCommandsEvent;
import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PluginMessageEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
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.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.packet.AvailableCommands; import com.velocitypowered.proxy.protocol.packet.AvailableCommands;
@ -40,6 +43,7 @@ import com.velocitypowered.proxy.protocol.packet.Disconnect;
import com.velocitypowered.proxy.protocol.packet.KeepAlive; import com.velocitypowered.proxy.protocol.packet.KeepAlive;
import com.velocitypowered.proxy.protocol.packet.PlayerListItem; import com.velocitypowered.proxy.protocol.packet.PlayerListItem;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import com.velocitypowered.proxy.protocol.packet.ResourcePackRequest;
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;
@ -128,6 +132,21 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
return false; // forward return false; // forward
} }
@Override
public boolean handle(ResourcePackRequest packet) {
ResourcePackInfo.Builder builder = new VelocityResourcePackInfo.BuilderImpl(
Preconditions.checkNotNull(packet.getUrl()))
.setPrompt(packet.getPrompt())
.setShouldForce(packet.isRequired());
// Why SpotBugs decides that this is unsafe I have no idea;
if (packet.getHash() != null && !Preconditions.checkNotNull(packet.getHash()).isEmpty()) {
builder.setHash(ByteBufUtil.decodeHexDump(packet.getHash()));
}
serverConn.getPlayer().queueResourcePack(builder.build());
return true;
}
@Override @Override
public boolean handle(PluginMessage packet) { public boolean handle(PluginMessage packet) {
if (bungeecordMessageResponder.process(packet)) { if (bungeecordMessageResponder.process(packet)) {

Datei anzeigen

@ -33,6 +33,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.ConnectionTypes; import com.velocitypowered.proxy.connection.ConnectionTypes;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
@ -53,7 +54,7 @@ import com.velocitypowered.proxy.protocol.packet.Respawn;
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest; import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse; import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse.Offer; import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse.Offer;
import com.velocitypowered.proxy.protocol.packet.TitlePacket; import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
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.ByteBufUtil;
@ -72,6 +73,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
@ -283,9 +285,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(ResourcePackResponse packet) { public boolean handle(ResourcePackResponse packet) {
server.getEventManager().fireAndForget(new PlayerResourcePackStatusEvent(player, return player.onResourcePackResponse(packet.getStatus(),
packet.getStatus())); ByteBufUtil.decodeHexDump(packet.getHash()));
return false;
} }
@Override @Override
@ -400,7 +401,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Clear any title from the previous server. // Clear any title from the previous server.
if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) { if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
player.getConnection() player.getConnection()
.delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion())); .delayedWrite(GenericTitlePacket.constructTitlePacket(
GenericTitlePacket.ActionType.RESET, player.getProtocolVersion()));
} }
// Flush everything // Flush everything

Datei anzeigen

@ -26,7 +26,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
public class ClientSettingsWrapper implements PlayerSettings { public class ClientSettingsWrapper implements PlayerSettings {
static final PlayerSettings DEFAULT = new ClientSettingsWrapper( static final PlayerSettings DEFAULT = new ClientSettingsWrapper(
new ClientSettings("en_US", (byte) 10, 0, true, (short) 127, 1)); new ClientSettings("en_US", (byte) 10, 0, true, (short) 127, 1, true));
private final ClientSettings settings; private final ClientSettings settings;
private final SkinParts parts; private final SkinParts parts;

Datei anzeigen

@ -31,6 +31,7 @@ import com.velocitypowered.api.event.player.KickedFromServerEvent.Notify;
import com.velocitypowered.api.event.player.KickedFromServerEvent.RedirectPlayer; import com.velocitypowered.api.event.player.KickedFromServerEvent.RedirectPlayer;
import com.velocitypowered.api.event.player.KickedFromServerEvent.ServerKickResult; import com.velocitypowered.api.event.player.KickedFromServerEvent.ServerKickResult;
import com.velocitypowered.api.event.player.PlayerModInfoEvent; import com.velocitypowered.api.event.player.PlayerModInfoEvent;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent; import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEvent; import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
@ -42,6 +43,7 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.ModInfo;
@ -51,6 +53,7 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants; import com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
@ -62,7 +65,7 @@ import com.velocitypowered.proxy.protocol.packet.HeaderAndFooter;
import com.velocitypowered.proxy.protocol.packet.KeepAlive; import com.velocitypowered.proxy.protocol.packet.KeepAlive;
import com.velocitypowered.proxy.protocol.packet.PluginMessage; import com.velocitypowered.proxy.protocol.packet.PluginMessage;
import com.velocitypowered.proxy.protocol.packet.ResourcePackRequest; import com.velocitypowered.proxy.protocol.packet.ResourcePackRequest;
import com.velocitypowered.proxy.protocol.packet.TitlePacket; import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import com.velocitypowered.proxy.tablist.VelocityTabList; import com.velocitypowered.proxy.tablist.VelocityTabList;
@ -72,12 +75,14 @@ import com.velocitypowered.proxy.util.collect.CappedSet;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.ArrayDeque;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Queue;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
@ -129,6 +134,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
private final Collection<String> knownChannels; private final Collection<String> knownChannels;
private final CompletableFuture<Void> teardownFuture = new CompletableFuture<>(); private final CompletableFuture<Void> teardownFuture = new CompletableFuture<>();
private @MonotonicNonNull List<String> serversToTry = null; private @MonotonicNonNull List<String> serversToTry = null;
private @MonotonicNonNull Boolean previousResourceResponse;
private final Queue<ResourcePackInfo> outstandingResourcePacks = new ArrayDeque<>();
private @Nullable ResourcePackInfo pendingResourcePack;
private @Nullable ResourcePackInfo appliedResourcePack;
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
@Nullable InetSocketAddress virtualHost, boolean onlineMode) { @Nullable InetSocketAddress virtualHost, boolean onlineMode) {
@ -269,8 +278,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
ProtocolVersion playerVersion = getProtocolVersion(); ProtocolVersion playerVersion = getProtocolVersion();
if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) { if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) {
// Use the title packet instead. // Use the title packet instead.
TitlePacket pkt = new TitlePacket(); GenericTitlePacket pkt = GenericTitlePacket.constructTitlePacket(
pkt.setAction(TitlePacket.SET_ACTION_BAR); GenericTitlePacket.ActionType.SET_ACTION_BAR, playerVersion);
pkt.setComponent(ProtocolUtils.getJsonChatSerializer(playerVersion) pkt.setComponent(ProtocolUtils.getJsonChatSerializer(playerVersion)
.serialize(message)); .serialize(message));
connection.write(pkt); connection.write(pkt);
@ -321,17 +330,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
GsonComponentSerializer serializer = ProtocolUtils.getJsonChatSerializer(this GsonComponentSerializer serializer = ProtocolUtils.getJsonChatSerializer(this
.getProtocolVersion()); .getProtocolVersion());
TitlePacket titlePkt = new TitlePacket(); GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket(
titlePkt.setAction(TitlePacket.SET_TITLE); GenericTitlePacket.ActionType.SET_TITLE, this.getProtocolVersion());
titlePkt.setComponent(serializer.serialize(title.title())); titlePkt.setComponent(serializer.serialize(title.title()));
connection.delayedWrite(titlePkt); connection.delayedWrite(titlePkt);
TitlePacket subtitlePkt = new TitlePacket(); GenericTitlePacket subtitlePkt = GenericTitlePacket.constructTitlePacket(
subtitlePkt.setAction(TitlePacket.SET_SUBTITLE); GenericTitlePacket.ActionType.SET_SUBTITLE, this.getProtocolVersion());
subtitlePkt.setComponent(serializer.serialize(title.subtitle())); subtitlePkt.setComponent(serializer.serialize(title.subtitle()));
connection.delayedWrite(subtitlePkt); connection.delayedWrite(subtitlePkt);
TitlePacket timesPkt = TitlePacket.timesForProtocolVersion(this.getProtocolVersion()); GenericTitlePacket timesPkt = GenericTitlePacket.constructTitlePacket(
GenericTitlePacket.ActionType.SET_TIMES, this.getProtocolVersion());
net.kyori.adventure.title.Title.Times times = title.times(); net.kyori.adventure.title.Title.Times times = title.times();
if (times != null) { if (times != null) {
timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn())); timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn()));
@ -347,14 +357,16 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
@Override @Override
public void clearTitle() { public void clearTitle() {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(TitlePacket.hideForProtocolVersion(this.getProtocolVersion())); connection.write(GenericTitlePacket.constructTitlePacket(
GenericTitlePacket.ActionType.HIDE, this.getProtocolVersion()));
} }
} }
@Override @Override
public void resetTitle() { public void resetTitle() {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
connection.write(TitlePacket.resetForProtocolVersion(this.getProtocolVersion())); connection.write(GenericTitlePacket.constructTitlePacket(
GenericTitlePacket.ActionType.RESET, this.getProtocolVersion()));
} }
} }
@ -752,29 +764,132 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
} }
@Override @Override
@Deprecated
public void sendResourcePack(String url) { public void sendResourcePack(String url) {
Preconditions.checkNotNull(url, "url"); sendResourcePackOffer(new VelocityResourcePackInfo.BuilderImpl(url).build());
}
@Override
@Deprecated
public void sendResourcePack(String url, byte[] hash) {
sendResourcePackOffer(new VelocityResourcePackInfo.BuilderImpl(url).setHash(hash).build());
}
@Override
public void sendResourcePackOffer(ResourcePackInfo packInfo) {
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
Preconditions.checkNotNull(packInfo, "packInfo");
queueResourcePack(packInfo);
}
}
/**
* Queues a resource-pack for sending to the player and sends it
* immediately if the queue is empty.
*/
public void queueResourcePack(ResourcePackInfo info) {
outstandingResourcePacks.add(info);
if (outstandingResourcePacks.size() == 1) {
tickResourcePackQueue();
}
}
private void tickResourcePackQueue() {
ResourcePackInfo queued = outstandingResourcePacks.peek();
if (queued != null) {
// Check if the player declined a resource pack once already
if (previousResourceResponse != null && !previousResourceResponse) {
// If that happened we can flush the queue right away.
// Unless its 1.17+ and forced it will come back denied anyway
while (!outstandingResourcePacks.isEmpty()) {
queued = outstandingResourcePacks.peek();
if (queued.getShouldForce() && getProtocolVersion()
.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
break;
}
onResourcePackResponse(PlayerResourcePackStatusEvent.Status.DECLINED, new byte[0]);
queued = null;
}
if (queued == null) {
// Exit as the queue was cleared
return;
}
}
ResourcePackRequest request = new ResourcePackRequest(); ResourcePackRequest request = new ResourcePackRequest();
request.setUrl(url); request.setUrl(queued.getUrl());
request.setHash(""); if (queued.getHash() != null) {
request.setHash(ByteBufUtil.hexDump(queued.getHash()));
} else {
request.setHash("");
}
request.setRequired(queued.getShouldForce());
request.setPrompt(queued.getPrompt());
connection.write(request); connection.write(request);
} }
} }
@Override @Override
public void sendResourcePack(String url, byte[] hash) { public @Nullable ResourcePackInfo getAppliedResourcePack() {
Preconditions.checkNotNull(url, "url"); return appliedResourcePack;
Preconditions.checkNotNull(hash, "hash"); }
Preconditions.checkArgument(hash.length == 20, "Hash length is not 20");
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { @Override
ResourcePackRequest request = new ResourcePackRequest(); public @Nullable ResourcePackInfo getPendingResourcePack() {
request.setUrl(url); return pendingResourcePack;
request.setHash(ByteBufUtil.hexDump(hash)); }
connection.write(request);
/**
* Processes a client response to a sent resource-pack.
*/
public boolean onResourcePackResponse(PlayerResourcePackStatusEvent.Status status,
@Nullable byte[] hash) {
final boolean peek = status == PlayerResourcePackStatusEvent.Status.ACCEPTED;
final ResourcePackInfo queued = peek
? outstandingResourcePacks.peek() : outstandingResourcePacks.poll();
server.getEventManager().fire(new PlayerResourcePackStatusEvent(this, status, queued))
.thenAcceptAsync(event -> {
if (event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED
&& event.getPackInfo() != null && event.getPackInfo().getShouldForce()
&& (!event.isOverwriteKick() || event.getPlayer()
.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0)
) {
event.getPlayer().disconnect(Component
.translatable("multiplayer.requiredTexturePrompt.disconnect"));
}
});
switch (status) {
case ACCEPTED:
previousResourceResponse = true;
pendingResourcePack = queued;
break;
case DECLINED:
previousResourceResponse = false;
break;
case SUCCESSFUL:
appliedResourcePack = queued;
pendingResourcePack = null;
break;
case FAILED_DOWNLOAD:
pendingResourcePack = null;
break;
default:
break;
} }
if (!peek) {
connection.eventLoop().execute(() -> {
tickResourcePackQueue();
});
}
return queued != null && queued.getOrigin() == ResourcePackInfo.Origin.DOWNSTREAM_SERVER;
} }
/** /**

Datei anzeigen

@ -0,0 +1,110 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.connection.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class VelocityResourcePackInfo implements ResourcePackInfo {
private final String url;
private final @Nullable byte[] hash;
private final boolean shouldForce;
private final @Nullable Component prompt; // 1.17+ only
private final Origin origin;
private VelocityResourcePackInfo(String url, @Nullable byte[] hash, boolean shouldForce,
@Nullable Component prompt, Origin origin) {
this.url = url;
this.hash = hash;
this.shouldForce = shouldForce;
this.prompt = prompt;
this.origin = origin;
}
@Override
public String getUrl() {
return url;
}
@Override
public @Nullable Component getPrompt() {
return prompt;
}
@Override
public boolean getShouldForce() {
return shouldForce;
}
@Override
public @Nullable byte[] getHash() {
return hash == null ? null : hash.clone(); // Thanks spotbugs, very helpful.
}
@Override
public Origin getOrigin() {
return origin;
}
public static final class BuilderImpl implements ResourcePackInfo.Builder {
private final String url;
private boolean shouldForce;
private @Nullable byte[] hash;
private @Nullable Component prompt;
private Origin origin = Origin.PLUGIN_ON_PROXY;
public BuilderImpl(String url) {
this.url = Preconditions.checkNotNull(url, "url");
}
@Override
public BuilderImpl setShouldForce(boolean shouldForce) {
this.shouldForce = shouldForce;
return this;
}
@Override
public BuilderImpl setHash(@Nullable byte[] hash) {
if (hash != null) {
Preconditions.checkArgument(hash.length == 20, "Hash length is not 20");
this.hash = hash.clone(); // Thanks spotbugs, very helpful.
} else {
this.hash = null;
}
return this;
}
@Override
public BuilderImpl setPrompt(@Nullable Component prompt) {
this.prompt = prompt;
return this;
}
@Override
public ResourcePackInfo build() {
return new VelocityResourcePackInfo(url, hash, shouldForce, prompt, origin);
}
public void setOrigin(Origin origin) {
this.origin = origin;
}
}
}

Datei anzeigen

@ -43,6 +43,8 @@ public final class DimensionData {
private final @Nullable Boolean createDragonFight; private final @Nullable Boolean createDragonFight;
private final @Nullable Double coordinateScale; private final @Nullable Double coordinateScale;
private final @Nullable String effects; private final @Nullable String effects;
private final @Nullable Integer minY; // Required and added by 1.17
private final @Nullable Integer height; // Required and added by 1.17
/** /**
* Initializes a new {@link DimensionData} instance. * Initializes a new {@link DimensionData} instance.
@ -64,6 +66,8 @@ public final class DimensionData {
* @param createDragonFight optional. Internal flag used in the end dimension * @param createDragonFight optional. Internal flag used in the end dimension
* @param coordinateScale optional, unknown purpose * @param coordinateScale optional, unknown purpose
* @param effects optional, unknown purpose * @param effects optional, unknown purpose
* @param minY the world effective lowest build-level
* @param height the world height above zero
*/ */
public DimensionData(String registryIdentifier, public DimensionData(String registryIdentifier,
@Nullable Integer dimensionId, @Nullable Integer dimensionId,
@ -75,7 +79,8 @@ public final class DimensionData {
int logicalHeight, String burningBehaviourIdentifier, int logicalHeight, String burningBehaviourIdentifier,
@Nullable Long fixedTime, @Nullable Boolean createDragonFight, @Nullable Long fixedTime, @Nullable Boolean createDragonFight,
@Nullable Double coordinateScale, @Nullable Double coordinateScale,
@Nullable String effects) { @Nullable String effects,
@Nullable Integer minY, @Nullable Integer height) {
Preconditions.checkNotNull( Preconditions.checkNotNull(
registryIdentifier, "registryIdentifier cannot be null"); registryIdentifier, "registryIdentifier cannot be null");
Preconditions.checkArgument(registryIdentifier.length() > 0, Preconditions.checkArgument(registryIdentifier.length() > 0,
@ -103,6 +108,8 @@ public final class DimensionData {
this.createDragonFight = createDragonFight; this.createDragonFight = createDragonFight;
this.coordinateScale = coordinateScale; this.coordinateScale = coordinateScale;
this.effects = effects; this.effects = effects;
this.minY = minY;
this.height = height;
} }
public String getRegistryIdentifier() { public String getRegistryIdentifier() {
@ -173,6 +180,14 @@ public final class DimensionData {
return coordinateScale; return coordinateScale;
} }
public @Nullable Integer getMinY() {
return minY;
}
public @Nullable Integer getHeight() {
return height;
}
/** /**
* Returns a fresh {@link DimensionData} with the specified {@code registryIdentifier} * Returns a fresh {@link DimensionData} with the specified {@code registryIdentifier}
* and {@code dimensionId}. * and {@code dimensionId}.
@ -186,7 +201,7 @@ public final class DimensionData {
return new DimensionData(registryIdentifier, dimensionId, isNatural, ambientLight, isShrunk, return new DimensionData(registryIdentifier, dimensionId, isNatural, ambientLight, isShrunk,
isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork, isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork,
hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, createDragonFight, hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, createDragonFight,
coordinateScale, effects); coordinateScale, effects, minY, height);
} }
public boolean isUnannotated() { public boolean isUnannotated() {
@ -223,11 +238,19 @@ public final class DimensionData {
? details.getDouble("coordinate_scale") : null; ? details.getDouble("coordinate_scale") : null;
String effects = details.keySet().contains("effects") ? details.getString("effects") String effects = details.keySet().contains("effects") ? details.getString("effects")
: null; : null;
Integer minY = details.keySet().contains("min_y") ? details.getInt("min_y") : null;
Integer height = details.keySet().contains("height") ? details.getInt("height") : null;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
Preconditions.checkNotNull(height,
"DimensionData requires 'height' to be present for this version");
Preconditions.checkNotNull(minY,
"DimensionData requires 'minY' to be present for this version");
}
return new DimensionData( return new DimensionData(
UNKNOWN_DIMENSION_ID, null, isNatural, ambientLight, isShrunk, UNKNOWN_DIMENSION_ID, null, isNatural, ambientLight, isShrunk,
isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork, isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork,
hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, hasEnderdragonFight, hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, hasEnderdragonFight,
coordinateScale, effects); coordinateScale, effects, minY, height);
} }
/** /**
@ -306,6 +329,12 @@ public final class DimensionData {
if (effects != null) { if (effects != null) {
ret.putString("effects", effects); ret.putString("effects", effects);
} }
if (minY != null) {
ret.putInt("min_y", minY);
}
if (height != null) {
ret.putInt("height", height);
}
return ret.build(); return ret.build();
} }
} }

Datei anzeigen

@ -25,6 +25,8 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_4;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
@ -60,7 +62,12 @@ import com.velocitypowered.proxy.protocol.packet.StatusRequest;
import com.velocitypowered.proxy.protocol.packet.StatusResponse; import com.velocitypowered.proxy.protocol.packet.StatusResponse;
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest; import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse; import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
import com.velocitypowered.proxy.protocol.packet.TitlePacket; import com.velocitypowered.proxy.protocol.packet.title.LegacyTitlePacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleActionbarPacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleClearPacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleSubtitlePacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleTextPacket;
import com.velocitypowered.proxy.protocol.packet.title.TitleTimesPacket;
import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
@ -124,7 +131,8 @@ public enum StateRegistry {
map(0x0A, MINECRAFT_1_12, false), map(0x0A, MINECRAFT_1_12, false),
map(0x09, MINECRAFT_1_12_1, false), map(0x09, MINECRAFT_1_12_1, false),
map(0x0A, MINECRAFT_1_13, false), map(0x0A, MINECRAFT_1_13, false),
map(0x0B, MINECRAFT_1_14, false)); map(0x0B, MINECRAFT_1_14, false),
map(0x0A, MINECRAFT_1_17, false));
serverbound.register(KeepAlive.class, KeepAlive::new, serverbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_7_2, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x0B, MINECRAFT_1_9, false), map(0x0B, MINECRAFT_1_9, false),
@ -132,7 +140,8 @@ public enum StateRegistry {
map(0x0B, MINECRAFT_1_12_1, false), map(0x0B, MINECRAFT_1_12_1, false),
map(0x0E, MINECRAFT_1_13, false), map(0x0E, MINECRAFT_1_13, false),
map(0x0F, MINECRAFT_1_14, false), map(0x0F, MINECRAFT_1_14, false),
map(0x10, MINECRAFT_1_16, false)); map(0x10, MINECRAFT_1_16, false),
map(0x0F, MINECRAFT_1_17, false));
serverbound.register(ResourcePackResponse.class, ResourcePackResponse::new, serverbound.register(ResourcePackResponse.class, ResourcePackResponse::new,
map(0x19, MINECRAFT_1_8, false), map(0x19, MINECRAFT_1_8, false),
map(0x16, MINECRAFT_1_9, false), map(0x16, MINECRAFT_1_9, false),
@ -145,25 +154,29 @@ public enum StateRegistry {
clientbound.register(BossBar.class, BossBar::new, clientbound.register(BossBar.class, BossBar::new,
map(0x0C, MINECRAFT_1_9, false), map(0x0C, MINECRAFT_1_9, false),
map(0x0D, MINECRAFT_1_15, false), map(0x0D, MINECRAFT_1_15, false),
map(0x0C, MINECRAFT_1_16, false)); map(0x0C, MINECRAFT_1_16, false),
map(0x0D, MINECRAFT_1_17, false));
clientbound.register(Chat.class, Chat::new, clientbound.register(Chat.class, Chat::new,
map(0x02, MINECRAFT_1_7_2, true), map(0x02, MINECRAFT_1_7_2, true),
map(0x0F, MINECRAFT_1_9, true), map(0x0F, MINECRAFT_1_9, true),
map(0x0E, MINECRAFT_1_13, true), map(0x0E, MINECRAFT_1_13, true),
map(0x0F, MINECRAFT_1_15, true), map(0x0F, MINECRAFT_1_15, true),
map(0x0E, MINECRAFT_1_16, true)); map(0x0E, MINECRAFT_1_16, true),
map(0x0F, MINECRAFT_1_17, true));
clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new, clientbound.register(TabCompleteResponse.class, TabCompleteResponse::new,
map(0x3A, MINECRAFT_1_7_2, false), map(0x3A, MINECRAFT_1_7_2, false),
map(0x0E, MINECRAFT_1_9, false), map(0x0E, MINECRAFT_1_9, false),
map(0x10, MINECRAFT_1_13, false), map(0x10, MINECRAFT_1_13, false),
map(0x11, MINECRAFT_1_15, false), map(0x11, MINECRAFT_1_15, false),
map(0x10, MINECRAFT_1_16, false), map(0x10, MINECRAFT_1_16, false),
map(0x0F, MINECRAFT_1_16_2, false)); map(0x0F, MINECRAFT_1_16_2, false),
map(0x11, MINECRAFT_1_17, false));
clientbound.register(AvailableCommands.class, AvailableCommands::new, clientbound.register(AvailableCommands.class, AvailableCommands::new,
map(0x11, MINECRAFT_1_13, false), map(0x11, MINECRAFT_1_13, false),
map(0x12, MINECRAFT_1_15, false), map(0x12, MINECRAFT_1_15, false),
map(0x11, MINECRAFT_1_16, false), map(0x11, MINECRAFT_1_16, false),
map(0x10, MINECRAFT_1_16_2, false)); map(0x10, MINECRAFT_1_16_2, false),
map(0x12, MINECRAFT_1_17, false));
clientbound.register(PluginMessage.class, PluginMessage::new, clientbound.register(PluginMessage.class, PluginMessage::new,
map(0x3F, MINECRAFT_1_7_2, false), map(0x3F, MINECRAFT_1_7_2, false),
map(0x18, MINECRAFT_1_9, false), map(0x18, MINECRAFT_1_9, false),
@ -171,7 +184,8 @@ public enum StateRegistry {
map(0x18, MINECRAFT_1_14, false), map(0x18, MINECRAFT_1_14, false),
map(0x19, MINECRAFT_1_15, false), map(0x19, MINECRAFT_1_15, false),
map(0x18, MINECRAFT_1_16, false), map(0x18, MINECRAFT_1_16, false),
map(0x17, MINECRAFT_1_16_2, false)); map(0x17, MINECRAFT_1_16_2, false),
map(0x18, MINECRAFT_1_17, false));
clientbound.register(Disconnect.class, Disconnect::new, clientbound.register(Disconnect.class, Disconnect::new,
map(0x40, MINECRAFT_1_7_2, false), map(0x40, MINECRAFT_1_7_2, false),
map(0x1A, MINECRAFT_1_9, false), map(0x1A, MINECRAFT_1_9, false),
@ -179,7 +193,8 @@ public enum StateRegistry {
map(0x1A, MINECRAFT_1_14, false), map(0x1A, MINECRAFT_1_14, false),
map(0x1B, MINECRAFT_1_15, false), map(0x1B, MINECRAFT_1_15, false),
map(0x1A, MINECRAFT_1_16, false), map(0x1A, MINECRAFT_1_16, false),
map(0x19, MINECRAFT_1_16_2, false)); map(0x19, MINECRAFT_1_16_2, false),
map(0x1A, MINECRAFT_1_17, false));
clientbound.register(KeepAlive.class, KeepAlive::new, clientbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_7_2, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x1F, MINECRAFT_1_9, false), map(0x1F, MINECRAFT_1_9, false),
@ -187,7 +202,8 @@ public enum StateRegistry {
map(0x20, MINECRAFT_1_14, false), map(0x20, MINECRAFT_1_14, false),
map(0x21, MINECRAFT_1_15, false), map(0x21, MINECRAFT_1_15, false),
map(0x20, MINECRAFT_1_16, false), map(0x20, MINECRAFT_1_16, false),
map(0x1F, MINECRAFT_1_16_2, false)); map(0x1F, MINECRAFT_1_16_2, false),
map(0x21, MINECRAFT_1_17, false));
clientbound.register(JoinGame.class, JoinGame::new, clientbound.register(JoinGame.class, JoinGame::new,
map(0x01, MINECRAFT_1_7_2, false), map(0x01, MINECRAFT_1_7_2, false),
map(0x23, MINECRAFT_1_9, false), map(0x23, MINECRAFT_1_9, false),
@ -195,7 +211,8 @@ public enum StateRegistry {
map(0x25, MINECRAFT_1_14, false), map(0x25, MINECRAFT_1_14, false),
map(0x26, MINECRAFT_1_15, false), map(0x26, MINECRAFT_1_15, false),
map(0x25, MINECRAFT_1_16, false), map(0x25, MINECRAFT_1_16, false),
map(0x24, MINECRAFT_1_16_2, false)); map(0x24, MINECRAFT_1_16_2, false),
map(0x26, MINECRAFT_1_17, false));
clientbound.register(Respawn.class, Respawn::new, clientbound.register(Respawn.class, Respawn::new,
map(0x07, MINECRAFT_1_7_2, true), map(0x07, MINECRAFT_1_7_2, true),
map(0x33, MINECRAFT_1_9, true), map(0x33, MINECRAFT_1_9, true),
@ -205,17 +222,19 @@ public enum StateRegistry {
map(0x3A, MINECRAFT_1_14, true), map(0x3A, MINECRAFT_1_14, true),
map(0x3B, MINECRAFT_1_15, true), map(0x3B, MINECRAFT_1_15, true),
map(0x3A, MINECRAFT_1_16, true), map(0x3A, MINECRAFT_1_16, true),
map(0x39, MINECRAFT_1_16_2, true)); map(0x39, MINECRAFT_1_16_2, true),
map(0x3D, MINECRAFT_1_17, true));
clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new, clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new,
map(0x48, MINECRAFT_1_8, true), map(0x48, MINECRAFT_1_8, false),
map(0x32, MINECRAFT_1_9, true), map(0x32, MINECRAFT_1_9, false),
map(0x33, MINECRAFT_1_12, true), map(0x33, MINECRAFT_1_12, false),
map(0x34, MINECRAFT_1_12_1, true), map(0x34, MINECRAFT_1_12_1, false),
map(0x37, MINECRAFT_1_13, true), map(0x37, MINECRAFT_1_13, false),
map(0x39, MINECRAFT_1_14, true), map(0x39, MINECRAFT_1_14, false),
map(0x3A, MINECRAFT_1_15, true), map(0x3A, MINECRAFT_1_15, false),
map(0x39, MINECRAFT_1_16, true), map(0x39, MINECRAFT_1_16, false),
map(0x38, MINECRAFT_1_16_2, true)); map(0x38, MINECRAFT_1_16_2, false),
map(0x3C, MINECRAFT_1_17, false));
clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new, clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new,
map(0x47, MINECRAFT_1_8, true), map(0x47, MINECRAFT_1_8, true),
map(0x48, MINECRAFT_1_9, true), map(0x48, MINECRAFT_1_9, true),
@ -225,8 +244,9 @@ public enum StateRegistry {
map(0x4E, MINECRAFT_1_13, true), map(0x4E, MINECRAFT_1_13, true),
map(0x53, MINECRAFT_1_14, true), map(0x53, MINECRAFT_1_14, true),
map(0x54, MINECRAFT_1_15, true), map(0x54, MINECRAFT_1_15, true),
map(0x53, MINECRAFT_1_16, true)); map(0x53, MINECRAFT_1_16, true),
clientbound.register(TitlePacket.class, TitlePacket::new, map(0x5E, MINECRAFT_1_17, true));
clientbound.register(LegacyTitlePacket.class, LegacyTitlePacket::new,
map(0x45, MINECRAFT_1_8, true), map(0x45, MINECRAFT_1_8, true),
map(0x45, MINECRAFT_1_9, true), map(0x45, MINECRAFT_1_9, true),
map(0x47, MINECRAFT_1_12, true), map(0x47, MINECRAFT_1_12, true),
@ -234,7 +254,17 @@ public enum StateRegistry {
map(0x4B, MINECRAFT_1_13, true), map(0x4B, MINECRAFT_1_13, true),
map(0x4F, MINECRAFT_1_14, true), map(0x4F, MINECRAFT_1_14, true),
map(0x50, MINECRAFT_1_15, true), map(0x50, MINECRAFT_1_15, true),
map(0x4F, MINECRAFT_1_16, true)); map(0x4F, MINECRAFT_1_16, MINECRAFT_1_16_4, true));
clientbound.register(TitleSubtitlePacket.class, TitleSubtitlePacket::new,
map(0x57, MINECRAFT_1_17, true));
clientbound.register(TitleTextPacket.class, TitleTextPacket::new,
map(0x59, MINECRAFT_1_17, true));
clientbound.register(TitleActionbarPacket.class, TitleActionbarPacket::new,
map(0x41, MINECRAFT_1_17, true));
clientbound.register(TitleTimesPacket.class, TitleTimesPacket::new,
map(0x5A, MINECRAFT_1_17, true));
clientbound.register(TitleClearPacket.class, TitleClearPacket::new,
map(0x10, MINECRAFT_1_17, true));
clientbound.register(PlayerListItem.class, PlayerListItem::new, clientbound.register(PlayerListItem.class, PlayerListItem::new,
map(0x38, MINECRAFT_1_7_2, false), map(0x38, MINECRAFT_1_7_2, false),
map(0x2D, MINECRAFT_1_9, false), map(0x2D, MINECRAFT_1_9, false),
@ -243,7 +273,8 @@ public enum StateRegistry {
map(0x33, MINECRAFT_1_14, false), map(0x33, MINECRAFT_1_14, false),
map(0x34, MINECRAFT_1_15, false), map(0x34, MINECRAFT_1_15, false),
map(0x33, MINECRAFT_1_16, false), map(0x33, MINECRAFT_1_16, false),
map(0x32, MINECRAFT_1_16_2, false)); map(0x32, MINECRAFT_1_16_2, false),
map(0x36, MINECRAFT_1_17, false));
} }
}, },
LOGIN { LOGIN {
@ -311,8 +342,20 @@ public enum StateRegistry {
for (int i = 0; i < mappings.length; i++) { for (int i = 0; i < mappings.length; i++) {
PacketMapping current = mappings[i]; PacketMapping current = mappings[i];
PacketMapping next = (i + 1 < mappings.length) ? mappings[i + 1] : current; PacketMapping next = (i + 1 < mappings.length) ? mappings[i + 1] : current;
ProtocolVersion from = current.protocolVersion; ProtocolVersion from = current.protocolVersion;
ProtocolVersion to = current == next ? getLast(SUPPORTED_VERSIONS) : next.protocolVersion; ProtocolVersion lastValid = current.lastValidProtocolVersion;
if (lastValid != null) {
if (next != current) {
throw new IllegalArgumentException("Cannot add a mapping after last valid mapping");
}
if (from.compareTo(lastValid) > 0) {
throw new IllegalArgumentException(
"Last mapping version cannot be higher than highest mapping version");
}
}
ProtocolVersion to = current == next ? lastValid != null
? lastValid : getLast(SUPPORTED_VERSIONS) : next.protocolVersion;
if (from.compareTo(to) >= 0 && from != getLast(SUPPORTED_VERSIONS)) { if (from.compareTo(to) >= 0 && from != getLast(SUPPORTED_VERSIONS)) {
throw new IllegalArgumentException(String.format( throw new IllegalArgumentException(String.format(
@ -400,10 +443,13 @@ public enum StateRegistry {
private final int id; private final int id;
private final ProtocolVersion protocolVersion; private final ProtocolVersion protocolVersion;
private final boolean encodeOnly; private final boolean encodeOnly;
private final @Nullable ProtocolVersion lastValidProtocolVersion;
PacketMapping(int id, ProtocolVersion protocolVersion, boolean packetDecoding) { PacketMapping(int id, ProtocolVersion protocolVersion,
ProtocolVersion lastValidProtocolVersion, boolean packetDecoding) {
this.id = id; this.id = id;
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
this.lastValidProtocolVersion = lastValidProtocolVersion;
this.encodeOnly = packetDecoding; this.encodeOnly = packetDecoding;
} }
@ -445,7 +491,21 @@ public enum StateRegistry {
* @return PacketMapping with the provided arguments * @return PacketMapping with the provided arguments
*/ */
private static PacketMapping map(int id, ProtocolVersion version, boolean encodeOnly) { private static PacketMapping map(int id, ProtocolVersion version, boolean encodeOnly) {
return new PacketMapping(id, version, encodeOnly); return map(id, version, null, encodeOnly);
}
/**
* Creates a PacketMapping using the provided arguments.
*
* @param id Packet Id
* @param version Protocol version
* @param encodeOnly When true packet decoding will be disabled
* @param lastValidProtocolVersion Last version this Mapping is valid at
* @return PacketMapping with the provided arguments
*/
private static PacketMapping map(int id, ProtocolVersion version,
ProtocolVersion lastValidProtocolVersion, boolean encodeOnly) {
return new PacketMapping(id, version, lastValidProtocolVersion, encodeOnly);
} }
} }

Datei anzeigen

@ -33,12 +33,13 @@ public class ClientSettings implements MinecraftPacket {
private byte difficulty; // 1.7 Protocol private byte difficulty; // 1.7 Protocol
private short skinParts; private short skinParts;
private int mainHand; private int mainHand;
private boolean chatFilteringEnabled; // Added in 1.17
public ClientSettings() { public ClientSettings() {
} }
public ClientSettings(String locale, byte viewDistance, int chatVisibility, boolean chatColors, public ClientSettings(String locale, byte viewDistance, int chatVisibility, boolean chatColors,
short skinParts, int mainHand) { short skinParts, int mainHand, boolean chatFilteringEnabled) {
this.locale = locale; this.locale = locale;
this.viewDistance = viewDistance; this.viewDistance = viewDistance;
this.chatVisibility = chatVisibility; this.chatVisibility = chatVisibility;
@ -98,6 +99,14 @@ public class ClientSettings implements MinecraftPacket {
this.mainHand = mainHand; this.mainHand = mainHand;
} }
public boolean isChatFilteringEnabled() {
return chatFilteringEnabled;
}
public void setChatFilteringEnabled(boolean chatFilteringEnabled) {
this.chatFilteringEnabled = chatFilteringEnabled;
}
@Override @Override
public String toString() { public String toString() {
return "ClientSettings{" return "ClientSettings{"
@ -107,6 +116,7 @@ public class ClientSettings implements MinecraftPacket {
+ ", chatColors=" + chatColors + ", chatColors=" + chatColors
+ ", skinParts=" + skinParts + ", skinParts=" + skinParts
+ ", mainHand=" + mainHand + ", mainHand=" + mainHand
+ ", chatFilteringEnabled=" + chatFilteringEnabled
+ '}'; + '}';
} }
@ -125,6 +135,10 @@ public class ClientSettings implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
this.mainHand = ProtocolUtils.readVarInt(buf); this.mainHand = ProtocolUtils.readVarInt(buf);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
this.chatFilteringEnabled = buf.readBoolean();
}
} }
} }
@ -146,6 +160,10 @@ public class ClientSettings implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
ProtocolUtils.writeVarInt(buf, mainHand); ProtocolUtils.writeVarInt(buf, mainHand);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
buf.writeBoolean(chatFilteringEnabled);
}
} }
} }

Datei anzeigen

@ -23,6 +23,8 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -30,6 +32,8 @@ public class ResourcePackRequest implements MinecraftPacket {
private @MonotonicNonNull String url; private @MonotonicNonNull String url;
private @MonotonicNonNull String hash; private @MonotonicNonNull String hash;
private boolean isRequired; // 1.17+
private @Nullable Component prompt; // 1.17+
public @Nullable String getUrl() { public @Nullable String getUrl() {
return url; return url;
@ -39,6 +43,10 @@ public class ResourcePackRequest implements MinecraftPacket {
this.url = url; this.url = url;
} }
public boolean isRequired() {
return isRequired;
}
public @Nullable String getHash() { public @Nullable String getHash() {
return hash; return hash;
} }
@ -47,10 +55,30 @@ public class ResourcePackRequest implements MinecraftPacket {
this.hash = hash; this.hash = hash;
} }
public void setRequired(boolean required) {
isRequired = required;
}
public @Nullable Component getPrompt() {
return prompt;
}
public void setPrompt(Component prompt) {
this.prompt = prompt;
}
@Override @Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
this.url = ProtocolUtils.readString(buf); this.url = ProtocolUtils.readString(buf);
this.hash = ProtocolUtils.readString(buf); this.hash = ProtocolUtils.readString(buf);
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
this.isRequired = buf.readBoolean();
if (buf.readBoolean()) {
this.prompt = GsonComponentSerializer.gson().deserialize(ProtocolUtils.readString(buf));
} else {
this.prompt = null;
}
}
} }
@Override @Override
@ -60,6 +88,15 @@ public class ResourcePackRequest implements MinecraftPacket {
} }
ProtocolUtils.writeString(buf, url); ProtocolUtils.writeString(buf, url);
ProtocolUtils.writeString(buf, hash); ProtocolUtils.writeString(buf, hash);
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
buf.writeBoolean(isRequired);
if (prompt != null) {
buf.writeBoolean(true);
ProtocolUtils.writeString(buf, GsonComponentSerializer.gson().serialize(prompt));
} else {
buf.writeBoolean(false);
}
}
} }
@Override @Override
@ -72,6 +109,8 @@ public class ResourcePackRequest implements MinecraftPacket {
return "ResourcePackRequest{" return "ResourcePackRequest{"
+ "url='" + url + '\'' + "url='" + url + '\''
+ ", hash='" + hash + '\'' + ", hash='" + hash + '\''
+ ", isRequired=" + isRequired
+ ", prompt='" + prompt + '\''
+ '}'; + '}';
} }
} }

Datei anzeigen

@ -38,6 +38,10 @@ public class ResourcePackResponse implements MinecraftPacket {
return status; return status;
} }
public String getHash() {
return hash;
}
@Override @Override
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_9_4) <= 0) { if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_9_4) <= 0) {

Datei anzeigen

@ -1,174 +0,0 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet;
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 io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class TitlePacket implements MinecraftPacket {
public static final int SET_TITLE = 0;
public static final int SET_SUBTITLE = 1;
public static final int SET_ACTION_BAR = 2;
public static final int SET_TIMES = 3;
public static final int SET_TIMES_OLD = 2;
public static final int HIDE = 4;
public static final int HIDE_OLD = 3;
public static final int RESET = 5;
public static final int RESET_OLD = 4;
private int action;
private @Nullable String component;
private int fadeIn;
private int stay;
private int fadeOut;
@Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
throw new UnsupportedOperationException(); // encode only
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeVarInt(buf, action);
if (version.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) {
// 1.11+ shifted the action enum by 1 to handle the action bar
switch (action) {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
if (component == null) {
throw new IllegalStateException("No component found for " + action);
}
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES:
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
break;
case HIDE:
case RESET:
break;
default:
throw new UnsupportedOperationException("Unknown action " + action);
}
} else {
switch (action) {
case SET_TITLE:
case SET_SUBTITLE:
if (component == null) {
throw new IllegalStateException("No component found for " + action);
}
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES_OLD:
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
break;
case HIDE_OLD:
case RESET_OLD:
break;
default:
throw new UnsupportedOperationException("Unknown action " + action);
}
}
}
public int getAction() {
return action;
}
public void setAction(int action) {
this.action = action;
}
public @Nullable String getComponent() {
return component;
}
public void setComponent(@Nullable String component) {
this.component = component;
}
public int getFadeIn() {
return fadeIn;
}
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
public int getStay() {
return stay;
}
public void setStay(int stay) {
this.stay = stay;
}
public int getFadeOut() {
return fadeOut;
}
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
public static TitlePacket hideForProtocolVersion(ProtocolVersion version) {
TitlePacket packet = new TitlePacket();
packet.setAction(version.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0 ? TitlePacket.HIDE
: TitlePacket.HIDE_OLD);
return packet;
}
public static TitlePacket resetForProtocolVersion(ProtocolVersion version) {
TitlePacket packet = new TitlePacket();
packet.setAction(version.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0 ? TitlePacket.RESET
: TitlePacket.RESET_OLD);
return packet;
}
public static TitlePacket timesForProtocolVersion(ProtocolVersion version) {
TitlePacket packet = new TitlePacket();
packet.setAction(version.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0 ? TitlePacket.SET_TIMES
: TitlePacket.SET_TIMES_OLD);
return packet;
}
@Override
public String toString() {
return "TitlePacket{"
+ "action=" + action
+ ", component='" + component + '\''
+ ", fadeIn=" + fadeIn
+ ", stay=" + stay
+ ", fadeOut=" + fadeOut
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -0,0 +1,136 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public abstract class GenericTitlePacket implements MinecraftPacket {
public enum ActionType {
SET_TITLE(0),
SET_SUBTITLE(1),
SET_ACTION_BAR(2),
SET_TIMES(3),
HIDE(4),
RESET(5);
private final int action;
ActionType(int action) {
this.action = action;
}
public int getAction(ProtocolVersion version) {
return version.compareTo(ProtocolVersion.MINECRAFT_1_11) < 0
? action > 2 ? action - 1 : action : action;
}
}
private ActionType action;
protected void setAction(ActionType action) {
this.action = action;
}
public final ActionType getAction() {
return action;
}
public String getComponent() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setComponent(String component) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public int getFadeIn() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setFadeIn(int fadeIn) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public int getStay() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setStay(int stay) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public int getFadeOut() {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
public void setFadeOut(int fadeOut) {
throw new UnsupportedOperationException("Invalid function for this TitlePacket ActionType");
}
@Override
public final void decode(ByteBuf buf, ProtocolUtils.Direction direction,
ProtocolVersion version) {
throw new UnsupportedOperationException(); // encode only
}
/**
* Creates a version and type dependent TitlePacket.
*
* @param type Action the packet should invoke
* @param version Protocol version of the target player
* @return GenericTitlePacket instance that follows the invoker type/version
*/
public static GenericTitlePacket constructTitlePacket(ActionType type, ProtocolVersion version) {
GenericTitlePacket packet = null;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
switch (type) {
case SET_ACTION_BAR:
packet = new TitleActionbarPacket();
break;
case SET_SUBTITLE:
packet = new TitleSubtitlePacket();
break;
case SET_TIMES:
packet = new TitleTimesPacket();
break;
case SET_TITLE:
packet = new TitleTextPacket();
break;
case HIDE:
case RESET:
packet = new TitleClearPacket();
break;
default:
throw new IllegalArgumentException("Invalid ActionType");
}
} else {
packet = new LegacyTitlePacket();
}
packet.setAction(type);
return packet;
}
}

Datei anzeigen

@ -0,0 +1,124 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
import org.checkerframework.checker.nullness.qual.Nullable;
public class LegacyTitlePacket extends GenericTitlePacket {
private @Nullable String component;
private int fadeIn;
private int stay;
private int fadeOut;
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_11) < 0
&& getAction() == ActionType.SET_ACTION_BAR) {
throw new IllegalStateException("Action bars are only supported on 1.11 and newer");
}
ProtocolUtils.writeVarInt(buf, getAction().getAction(version));
switch (getAction()) {
case SET_TITLE:
case SET_SUBTITLE:
case SET_ACTION_BAR:
if (component == null) {
throw new IllegalStateException("No component found for " + getAction());
}
ProtocolUtils.writeString(buf, component);
break;
case SET_TIMES:
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
break;
case HIDE:
case RESET:
break;
default:
throw new UnsupportedOperationException("Unknown action " + getAction());
}
}
@Override
public void setAction(ActionType action) {
super.setAction(action);
}
@Override
public @Nullable String getComponent() {
return component;
}
@Override
public void setComponent(@Nullable String component) {
this.component = component;
}
@Override
public int getFadeIn() {
return fadeIn;
}
@Override
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
@Override
public int getStay() {
return stay;
}
@Override
public void setStay(int stay) {
this.stay = stay;
}
@Override
public int getFadeOut() {
return fadeOut;
}
@Override
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
@Override
public String toString() {
return "GenericTitlePacket{"
+ "action=" + getAction()
+ ", component='" + component + '\''
+ ", fadeIn=" + fadeIn
+ ", stay=" + stay
+ ", fadeOut=" + fadeOut
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleActionbarPacket extends GenericTitlePacket {
private String component;
public TitleActionbarPacket() {
setAction(ActionType.SET_TITLE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeString(buf, component);
}
@Override
public String getComponent() {
return component;
}
@Override
public void setComponent(String component) {
this.component = component;
}
@Override
public String toString() {
return "TitleActionbarPacket{"
+ ", component='" + component + '\''
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleClearPacket extends GenericTitlePacket {
public TitleClearPacket() {
setAction(ActionType.HIDE);
}
@Override
public void setAction(ActionType action) {
if (action != ActionType.HIDE && action != ActionType.RESET) {
throw new IllegalArgumentException("TitleClearPacket only accepts CLEAR and RESET actions");
}
super.setAction(action);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeBoolean(getAction() == ActionType.RESET);
}
@Override
public String toString() {
return "TitleClearPacket{"
+ ", resetTimes=" + (getAction() == ActionType.RESET)
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleSubtitlePacket extends GenericTitlePacket {
private String component;
public TitleSubtitlePacket() {
setAction(ActionType.SET_SUBTITLE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeString(buf, component);
}
@Override
public String getComponent() {
return component;
}
@Override
public void setComponent(String component) {
this.component = component;
}
@Override
public String toString() {
return "TitleSubtitlePacket{"
+ ", component='" + component + '\''
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleTextPacket extends GenericTitlePacket {
private String component;
public TitleTextPacket() {
setAction(ActionType.SET_TITLE);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
ProtocolUtils.writeString(buf, component);
}
@Override
public String getComponent() {
return component;
}
@Override
public void setComponent(String component) {
this.component = component;
}
@Override
public String toString() {
return "TitleTextPacket{"
+ ", component='" + component + '\''
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -0,0 +1,85 @@
/*
* Copyright (C) 2018 Velocity Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.velocitypowered.proxy.protocol.packet.title;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf;
public class TitleTimesPacket extends GenericTitlePacket {
private int fadeIn;
private int stay;
private int fadeOut;
public TitleTimesPacket() {
setAction(ActionType.SET_TIMES);
}
@Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeInt(fadeIn);
buf.writeInt(stay);
buf.writeInt(fadeOut);
}
@Override
public int getFadeIn() {
return fadeIn;
}
@Override
public void setFadeIn(int fadeIn) {
this.fadeIn = fadeIn;
}
@Override
public int getStay() {
return stay;
}
@Override
public void setStay(int stay) {
this.stay = stay;
}
@Override
public int getFadeOut() {
return fadeOut;
}
@Override
public void setFadeOut(int fadeOut) {
this.fadeOut = fadeOut;
}
@Override
public String toString() {
return "TitleTimesPacket{"
+ ", fadeIn=" + fadeIn
+ ", stay=" + stay
+ ", fadeOut=" + fadeOut
+ '}';
}
@Override
public boolean handle(MinecraftSessionHandler handler) {
return handler.handle(this);
}
}

Datei anzeigen

@ -23,7 +23,11 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -43,8 +47,9 @@ class PacketRegistryTest {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND); ProtocolUtils.Direction.CLIENTBOUND);
registry.register(Handshake.class, Handshake::new, registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, null, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, false)); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_15, MINECRAFT_1_16, false));
return registry; return registry;
} }
@ -73,6 +78,8 @@ class PacketRegistryTest {
"Registry did not return the correct packet ID"); "Registry did not return the correct packet ID");
assertNull(registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x01), assertNull(registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x01),
"Registry should return a null"); "Registry should return a null");
assertNull(registry.getProtocolRegistry(MINECRAFT_1_16_2).createPacket(0),
"Registry should return null");
} }
@Test @Test
@ -91,12 +98,19 @@ class PacketRegistryTest {
ProtocolUtils.Direction.CLIENTBOUND); ProtocolUtils.Direction.CLIENTBOUND);
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(Handshake.class, Handshake::new, () -> registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, false))); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false)));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(Handshake.class, Handshake::new, () -> registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, false))); new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false)));
assertThrows(IllegalArgumentException.class,
() -> registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, MINECRAFT_1_8, false)));
assertThrows(IllegalArgumentException.class,
() -> registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, MINECRAFT_1_14, false),
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_16, null, false)));
} }
@Test @Test
@ -104,13 +118,13 @@ class PacketRegistryTest {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND); ProtocolUtils.Direction.CLIENTBOUND);
registry.register(Handshake.class, Handshake::new, registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, false)); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(Handshake.class, Handshake::new, () -> registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12, false))); new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12, null, false)));
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> registry.register(StatusPing.class, StatusPing::new, () -> registry.register(StatusPing.class, StatusPing::new,
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, false))); new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, null, false)));
} }
@Test @Test
@ -118,9 +132,9 @@ class PacketRegistryTest {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND); ProtocolUtils.Direction.CLIENTBOUND);
assertDoesNotThrow(() -> registry.register(Handshake.class, Handshake::new, assertDoesNotThrow(() -> registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, false), new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false),
new StateRegistry.PacketMapping(0x01, getLast(ProtocolVersion.SUPPORTED_VERSIONS), new StateRegistry.PacketMapping(0x01, getLast(ProtocolVersion.SUPPORTED_VERSIONS),
false))); null, false)));
} }
@Test @Test
@ -128,9 +142,9 @@ class PacketRegistryTest {
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry( StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
ProtocolUtils.Direction.CLIENTBOUND); ProtocolUtils.Direction.CLIENTBOUND);
registry.register(Handshake.class, Handshake::new, registry.register(Handshake.class, Handshake::new,
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, false), new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false),
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, false), new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, null, false),
new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, false)); new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, null, false));
assertEquals(Handshake.class, assertEquals(Handshake.class,
registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0x00).getClass()); registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0x00).getClass());
assertEquals(Handshake.class, assertEquals(Handshake.class,