Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-12-25 15:50:19 +01:00
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:
Commit
b1b3882fab
@ -8,7 +8,11 @@
|
||||
package com.velocitypowered.api.event.player;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
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
|
||||
@ -18,10 +22,29 @@ public class PlayerResourcePackStatusEvent {
|
||||
|
||||
private final Player player;
|
||||
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) {
|
||||
this.player = Preconditions.checkNotNull(player, "player");
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public String toString() {
|
||||
return "PlayerResourcePackStatusEvent{"
|
||||
+ "player=" + player
|
||||
+ ", status=" + status
|
||||
+ ", packInfo=" + packInfo
|
||||
+ '}';
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,8 @@ public enum ProtocolVersion {
|
||||
MINECRAFT_1_16_1(736, "1.16.1"),
|
||||
MINECRAFT_1_16_2(751, "1.16.2"),
|
||||
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;
|
||||
|
||||
|
@ -13,6 +13,7 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
|
||||
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.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
@ -22,6 +23,7 @@ import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.identity.Identified;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*
|
||||
* @param url the URL for the resource pack
|
||||
* @deprecated Use {@link #sendResourcePackOffer(ResourcePackInfo)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
void sendResourcePack(String url);
|
||||
|
||||
/**
|
||||
@ -168,9 +172,41 @@ public interface Player extends CommandSource, Identified, InboundConnection,
|
||||
*
|
||||
* @param url the URL 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* is connected to.</strong> You should only use this method if you are trying to communicate
|
||||
|
@ -13,6 +13,7 @@ import com.velocitypowered.api.event.EventManager;
|
||||
import com.velocitypowered.api.plugin.PluginManager;
|
||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||
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.ServerInfo;
|
||||
import com.velocitypowered.api.scheduler.Scheduler;
|
||||
@ -186,4 +187,26 @@ public interface ProxyServer extends Audience {
|
||||
* @return the proxy version
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.plugin.PluginManager;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
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.ServerInfo;
|
||||
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.config.VelocityConfiguration;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
|
||||
import com.velocitypowered.proxy.console.VelocityConsole;
|
||||
import com.velocitypowered.proxy.event.VelocityEventManager;
|
||||
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
|
||||
: PRE_1_16_PING_SERIALIZER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourcePackInfo.Builder createResourcePackBuilder(String url) {
|
||||
return new VelocityResourcePackInfo.BuilderImpl(url);
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,12 @@ import com.velocitypowered.proxy.protocol.packet.StatusRequest;
|
||||
import com.velocitypowered.proxy.protocol.packet.StatusResponse;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
|
||||
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;
|
||||
|
||||
public interface MinecraftSessionHandler {
|
||||
@ -191,7 +196,27 @@ public interface MinecraftSessionHandler {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ package com.velocitypowered.proxy.connection.backend;
|
||||
|
||||
import static com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder.getBungeeCordChannel;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
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.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
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.protocol.MinecraftPacket;
|
||||
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.PlayerListItem;
|
||||
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.util.PluginMessageUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@ -128,6 +132,21 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
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
|
||||
public boolean handle(PluginMessage packet) {
|
||||
if (bungeecordMessageResponder.process(packet)) {
|
||||
|
@ -33,6 +33,7 @@ import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.ConnectionTypes;
|
||||
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.TabCompleteResponse;
|
||||
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 io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
@ -72,6 +73,7 @@ import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
@ -283,9 +285,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(ResourcePackResponse packet) {
|
||||
server.getEventManager().fireAndForget(new PlayerResourcePackStatusEvent(player,
|
||||
packet.getStatus()));
|
||||
return false;
|
||||
return player.onResourcePackResponse(packet.getStatus(),
|
||||
ByteBufUtil.decodeHexDump(packet.getHash()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -400,7 +401,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
// Clear any title from the previous server.
|
||||
if (player.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
|
||||
player.getConnection()
|
||||
.delayedWrite(TitlePacket.resetForProtocolVersion(player.getProtocolVersion()));
|
||||
.delayedWrite(GenericTitlePacket.constructTitlePacket(
|
||||
GenericTitlePacket.ActionType.RESET, player.getProtocolVersion()));
|
||||
}
|
||||
|
||||
// Flush everything
|
||||
|
@ -26,7 +26,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
public class ClientSettingsWrapper implements PlayerSettings {
|
||||
|
||||
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 SkinParts parts;
|
||||
|
@ -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.ServerKickResult;
|
||||
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.ServerPreConnectEvent;
|
||||
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.messages.ChannelIdentifier;
|
||||
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.util.GameProfile;
|
||||
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.backend.VelocityServerConnection;
|
||||
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.ConnectionRequestResults.Impl;
|
||||
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.PluginMessage;
|
||||
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.server.VelocityRegisteredServer;
|
||||
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.Unpooled;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
@ -129,6 +134,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
private final Collection<String> knownChannels;
|
||||
private final CompletableFuture<Void> teardownFuture = new CompletableFuture<>();
|
||||
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,
|
||||
@Nullable InetSocketAddress virtualHost, boolean onlineMode) {
|
||||
@ -269,8 +278,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
ProtocolVersion playerVersion = getProtocolVersion();
|
||||
if (playerVersion.compareTo(ProtocolVersion.MINECRAFT_1_11) >= 0) {
|
||||
// Use the title packet instead.
|
||||
TitlePacket pkt = new TitlePacket();
|
||||
pkt.setAction(TitlePacket.SET_ACTION_BAR);
|
||||
GenericTitlePacket pkt = GenericTitlePacket.constructTitlePacket(
|
||||
GenericTitlePacket.ActionType.SET_ACTION_BAR, playerVersion);
|
||||
pkt.setComponent(ProtocolUtils.getJsonChatSerializer(playerVersion)
|
||||
.serialize(message));
|
||||
connection.write(pkt);
|
||||
@ -321,17 +330,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
GsonComponentSerializer serializer = ProtocolUtils.getJsonChatSerializer(this
|
||||
.getProtocolVersion());
|
||||
|
||||
TitlePacket titlePkt = new TitlePacket();
|
||||
titlePkt.setAction(TitlePacket.SET_TITLE);
|
||||
GenericTitlePacket titlePkt = GenericTitlePacket.constructTitlePacket(
|
||||
GenericTitlePacket.ActionType.SET_TITLE, this.getProtocolVersion());
|
||||
titlePkt.setComponent(serializer.serialize(title.title()));
|
||||
connection.delayedWrite(titlePkt);
|
||||
|
||||
TitlePacket subtitlePkt = new TitlePacket();
|
||||
subtitlePkt.setAction(TitlePacket.SET_SUBTITLE);
|
||||
GenericTitlePacket subtitlePkt = GenericTitlePacket.constructTitlePacket(
|
||||
GenericTitlePacket.ActionType.SET_SUBTITLE, this.getProtocolVersion());
|
||||
subtitlePkt.setComponent(serializer.serialize(title.subtitle()));
|
||||
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();
|
||||
if (times != null) {
|
||||
timesPkt.setFadeIn((int) DurationUtils.toTicks(times.fadeIn()));
|
||||
@ -347,14 +357,16 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player {
|
||||
@Override
|
||||
public void clearTitle() {
|
||||
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
|
||||
public void resetTitle() {
|
||||
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
|
||||
@Deprecated
|
||||
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) {
|
||||
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();
|
||||
request.setUrl(url);
|
||||
request.setHash("");
|
||||
request.setUrl(queued.getUrl());
|
||||
if (queued.getHash() != null) {
|
||||
request.setHash(ByteBufUtil.hexDump(queued.getHash()));
|
||||
} else {
|
||||
request.setHash("");
|
||||
}
|
||||
request.setRequired(queued.getShouldForce());
|
||||
request.setPrompt(queued.getPrompt());
|
||||
|
||||
connection.write(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendResourcePack(String url, byte[] hash) {
|
||||
Preconditions.checkNotNull(url, "url");
|
||||
Preconditions.checkNotNull(hash, "hash");
|
||||
Preconditions.checkArgument(hash.length == 20, "Hash length is not 20");
|
||||
public @Nullable ResourcePackInfo getAppliedResourcePack() {
|
||||
return appliedResourcePack;
|
||||
}
|
||||
|
||||
if (this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) {
|
||||
ResourcePackRequest request = new ResourcePackRequest();
|
||||
request.setUrl(url);
|
||||
request.setHash(ByteBufUtil.hexDump(hash));
|
||||
connection.write(request);
|
||||
@Override
|
||||
public @Nullable ResourcePackInfo getPendingResourcePack() {
|
||||
return pendingResourcePack;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,8 @@ public final class DimensionData {
|
||||
private final @Nullable Boolean createDragonFight;
|
||||
private final @Nullable Double coordinateScale;
|
||||
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.
|
||||
@ -64,6 +66,8 @@ public final class DimensionData {
|
||||
* @param createDragonFight optional. Internal flag used in the end dimension
|
||||
* @param coordinateScale 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,
|
||||
@Nullable Integer dimensionId,
|
||||
@ -75,7 +79,8 @@ public final class DimensionData {
|
||||
int logicalHeight, String burningBehaviourIdentifier,
|
||||
@Nullable Long fixedTime, @Nullable Boolean createDragonFight,
|
||||
@Nullable Double coordinateScale,
|
||||
@Nullable String effects) {
|
||||
@Nullable String effects,
|
||||
@Nullable Integer minY, @Nullable Integer height) {
|
||||
Preconditions.checkNotNull(
|
||||
registryIdentifier, "registryIdentifier cannot be null");
|
||||
Preconditions.checkArgument(registryIdentifier.length() > 0,
|
||||
@ -103,6 +108,8 @@ public final class DimensionData {
|
||||
this.createDragonFight = createDragonFight;
|
||||
this.coordinateScale = coordinateScale;
|
||||
this.effects = effects;
|
||||
this.minY = minY;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public String getRegistryIdentifier() {
|
||||
@ -173,6 +180,14 @@ public final class DimensionData {
|
||||
return coordinateScale;
|
||||
}
|
||||
|
||||
public @Nullable Integer getMinY() {
|
||||
return minY;
|
||||
}
|
||||
|
||||
public @Nullable Integer getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fresh {@link DimensionData} with the specified {@code registryIdentifier}
|
||||
* and {@code dimensionId}.
|
||||
@ -186,7 +201,7 @@ public final class DimensionData {
|
||||
return new DimensionData(registryIdentifier, dimensionId, isNatural, ambientLight, isShrunk,
|
||||
isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork,
|
||||
hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, createDragonFight,
|
||||
coordinateScale, effects);
|
||||
coordinateScale, effects, minY, height);
|
||||
}
|
||||
|
||||
public boolean isUnannotated() {
|
||||
@ -223,11 +238,19 @@ public final class DimensionData {
|
||||
? details.getDouble("coordinate_scale") : null;
|
||||
String effects = details.keySet().contains("effects") ? details.getString("effects")
|
||||
: 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(
|
||||
UNKNOWN_DIMENSION_ID, null, isNatural, ambientLight, isShrunk,
|
||||
isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork,
|
||||
hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, hasEnderdragonFight,
|
||||
coordinateScale, effects);
|
||||
coordinateScale, effects, minY, height);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,6 +329,12 @@ public final class DimensionData {
|
||||
if (effects != null) {
|
||||
ret.putString("effects", effects);
|
||||
}
|
||||
if (minY != null) {
|
||||
ret.putInt("min_y", minY);
|
||||
}
|
||||
if (height != null) {
|
||||
ret.putInt("height", height);
|
||||
}
|
||||
return ret.build();
|
||||
}
|
||||
}
|
||||
|
@ -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_16;
|
||||
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_8;
|
||||
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.TabCompleteRequest;
|
||||
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.IntObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
@ -124,7 +131,8 @@ public enum StateRegistry {
|
||||
map(0x0A, MINECRAFT_1_12, false),
|
||||
map(0x09, MINECRAFT_1_12_1, 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,
|
||||
map(0x00, MINECRAFT_1_7_2, false),
|
||||
map(0x0B, MINECRAFT_1_9, false),
|
||||
@ -132,7 +140,8 @@ public enum StateRegistry {
|
||||
map(0x0B, MINECRAFT_1_12_1, false),
|
||||
map(0x0E, MINECRAFT_1_13, 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,
|
||||
map(0x19, MINECRAFT_1_8, false),
|
||||
map(0x16, MINECRAFT_1_9, false),
|
||||
@ -145,25 +154,29 @@ public enum StateRegistry {
|
||||
clientbound.register(BossBar.class, BossBar::new,
|
||||
map(0x0C, MINECRAFT_1_9, 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,
|
||||
map(0x02, MINECRAFT_1_7_2, true),
|
||||
map(0x0F, MINECRAFT_1_9, true),
|
||||
map(0x0E, MINECRAFT_1_13, 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,
|
||||
map(0x3A, MINECRAFT_1_7_2, false),
|
||||
map(0x0E, MINECRAFT_1_9, false),
|
||||
map(0x10, MINECRAFT_1_13, false),
|
||||
map(0x11, MINECRAFT_1_15, 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,
|
||||
map(0x11, MINECRAFT_1_13, false),
|
||||
map(0x12, MINECRAFT_1_15, 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,
|
||||
map(0x3F, MINECRAFT_1_7_2, false),
|
||||
map(0x18, MINECRAFT_1_9, false),
|
||||
@ -171,7 +184,8 @@ public enum StateRegistry {
|
||||
map(0x18, MINECRAFT_1_14, false),
|
||||
map(0x19, MINECRAFT_1_15, 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,
|
||||
map(0x40, MINECRAFT_1_7_2, false),
|
||||
map(0x1A, MINECRAFT_1_9, false),
|
||||
@ -179,7 +193,8 @@ public enum StateRegistry {
|
||||
map(0x1A, MINECRAFT_1_14, false),
|
||||
map(0x1B, MINECRAFT_1_15, 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,
|
||||
map(0x00, MINECRAFT_1_7_2, false),
|
||||
map(0x1F, MINECRAFT_1_9, false),
|
||||
@ -187,7 +202,8 @@ public enum StateRegistry {
|
||||
map(0x20, MINECRAFT_1_14, false),
|
||||
map(0x21, MINECRAFT_1_15, 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,
|
||||
map(0x01, MINECRAFT_1_7_2, false),
|
||||
map(0x23, MINECRAFT_1_9, false),
|
||||
@ -195,7 +211,8 @@ public enum StateRegistry {
|
||||
map(0x25, MINECRAFT_1_14, false),
|
||||
map(0x26, MINECRAFT_1_15, 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,
|
||||
map(0x07, MINECRAFT_1_7_2, true),
|
||||
map(0x33, MINECRAFT_1_9, true),
|
||||
@ -205,17 +222,19 @@ public enum StateRegistry {
|
||||
map(0x3A, MINECRAFT_1_14, true),
|
||||
map(0x3B, MINECRAFT_1_15, 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,
|
||||
map(0x48, MINECRAFT_1_8, true),
|
||||
map(0x32, MINECRAFT_1_9, true),
|
||||
map(0x33, MINECRAFT_1_12, true),
|
||||
map(0x34, MINECRAFT_1_12_1, true),
|
||||
map(0x37, MINECRAFT_1_13, true),
|
||||
map(0x39, MINECRAFT_1_14, true),
|
||||
map(0x3A, MINECRAFT_1_15, true),
|
||||
map(0x39, MINECRAFT_1_16, true),
|
||||
map(0x38, MINECRAFT_1_16_2, true));
|
||||
map(0x48, MINECRAFT_1_8, false),
|
||||
map(0x32, MINECRAFT_1_9, false),
|
||||
map(0x33, MINECRAFT_1_12, false),
|
||||
map(0x34, MINECRAFT_1_12_1, false),
|
||||
map(0x37, MINECRAFT_1_13, false),
|
||||
map(0x39, MINECRAFT_1_14, false),
|
||||
map(0x3A, MINECRAFT_1_15, false),
|
||||
map(0x39, MINECRAFT_1_16, false),
|
||||
map(0x38, MINECRAFT_1_16_2, false),
|
||||
map(0x3C, MINECRAFT_1_17, false));
|
||||
clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new,
|
||||
map(0x47, MINECRAFT_1_8, true),
|
||||
map(0x48, MINECRAFT_1_9, true),
|
||||
@ -225,8 +244,9 @@ public enum StateRegistry {
|
||||
map(0x4E, MINECRAFT_1_13, true),
|
||||
map(0x53, MINECRAFT_1_14, true),
|
||||
map(0x54, MINECRAFT_1_15, true),
|
||||
map(0x53, MINECRAFT_1_16, true));
|
||||
clientbound.register(TitlePacket.class, TitlePacket::new,
|
||||
map(0x53, MINECRAFT_1_16, true),
|
||||
map(0x5E, MINECRAFT_1_17, true));
|
||||
clientbound.register(LegacyTitlePacket.class, LegacyTitlePacket::new,
|
||||
map(0x45, MINECRAFT_1_8, true),
|
||||
map(0x45, MINECRAFT_1_9, true),
|
||||
map(0x47, MINECRAFT_1_12, true),
|
||||
@ -234,7 +254,17 @@ public enum StateRegistry {
|
||||
map(0x4B, MINECRAFT_1_13, true),
|
||||
map(0x4F, MINECRAFT_1_14, 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,
|
||||
map(0x38, MINECRAFT_1_7_2, false),
|
||||
map(0x2D, MINECRAFT_1_9, false),
|
||||
@ -243,7 +273,8 @@ public enum StateRegistry {
|
||||
map(0x33, MINECRAFT_1_14, false),
|
||||
map(0x34, MINECRAFT_1_15, 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 {
|
||||
@ -311,8 +342,20 @@ public enum StateRegistry {
|
||||
for (int i = 0; i < mappings.length; i++) {
|
||||
PacketMapping current = mappings[i];
|
||||
PacketMapping next = (i + 1 < mappings.length) ? mappings[i + 1] : current;
|
||||
|
||||
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)) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
@ -400,10 +443,13 @@ public enum StateRegistry {
|
||||
private final int id;
|
||||
private final ProtocolVersion protocolVersion;
|
||||
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.protocolVersion = protocolVersion;
|
||||
this.lastValidProtocolVersion = lastValidProtocolVersion;
|
||||
this.encodeOnly = packetDecoding;
|
||||
}
|
||||
|
||||
@ -445,7 +491,21 @@ public enum StateRegistry {
|
||||
* @return PacketMapping with the provided arguments
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,12 +33,13 @@ public class ClientSettings implements MinecraftPacket {
|
||||
private byte difficulty; // 1.7 Protocol
|
||||
private short skinParts;
|
||||
private int mainHand;
|
||||
private boolean chatFilteringEnabled; // Added in 1.17
|
||||
|
||||
public ClientSettings() {
|
||||
}
|
||||
|
||||
public ClientSettings(String locale, byte viewDistance, int chatVisibility, boolean chatColors,
|
||||
short skinParts, int mainHand) {
|
||||
short skinParts, int mainHand, boolean chatFilteringEnabled) {
|
||||
this.locale = locale;
|
||||
this.viewDistance = viewDistance;
|
||||
this.chatVisibility = chatVisibility;
|
||||
@ -98,6 +99,14 @@ public class ClientSettings implements MinecraftPacket {
|
||||
this.mainHand = mainHand;
|
||||
}
|
||||
|
||||
public boolean isChatFilteringEnabled() {
|
||||
return chatFilteringEnabled;
|
||||
}
|
||||
|
||||
public void setChatFilteringEnabled(boolean chatFilteringEnabled) {
|
||||
this.chatFilteringEnabled = chatFilteringEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientSettings{"
|
||||
@ -107,6 +116,7 @@ public class ClientSettings implements MinecraftPacket {
|
||||
+ ", chatColors=" + chatColors
|
||||
+ ", skinParts=" + skinParts
|
||||
+ ", mainHand=" + mainHand
|
||||
+ ", chatFilteringEnabled=" + chatFilteringEnabled
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@ -125,6 +135,10 @@ public class ClientSettings implements MinecraftPacket {
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_9) >= 0) {
|
||||
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) {
|
||||
ProtocolUtils.writeVarInt(buf, mainHand);
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_17) >= 0) {
|
||||
buf.writeBoolean(chatFilteringEnabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
|
||||
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.Nullable;
|
||||
|
||||
@ -30,6 +32,8 @@ public class ResourcePackRequest implements MinecraftPacket {
|
||||
|
||||
private @MonotonicNonNull String url;
|
||||
private @MonotonicNonNull String hash;
|
||||
private boolean isRequired; // 1.17+
|
||||
private @Nullable Component prompt; // 1.17+
|
||||
|
||||
public @Nullable String getUrl() {
|
||||
return url;
|
||||
@ -39,6 +43,10 @@ public class ResourcePackRequest implements MinecraftPacket {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return isRequired;
|
||||
}
|
||||
|
||||
public @Nullable String getHash() {
|
||||
return hash;
|
||||
}
|
||||
@ -47,10 +55,30 @@ public class ResourcePackRequest implements MinecraftPacket {
|
||||
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
|
||||
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
|
||||
this.url = 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
|
||||
@ -60,6 +88,15 @@ public class ResourcePackRequest implements MinecraftPacket {
|
||||
}
|
||||
ProtocolUtils.writeString(buf, url);
|
||||
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
|
||||
@ -72,6 +109,8 @@ public class ResourcePackRequest implements MinecraftPacket {
|
||||
return "ResourcePackRequest{"
|
||||
+ "url='" + url + '\''
|
||||
+ ", hash='" + hash + '\''
|
||||
+ ", isRequired=" + isRequired
|
||||
+ ", prompt='" + prompt + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,10 @@ public class ResourcePackResponse implements MinecraftPacket {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_9_4) <= 0) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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_2;
|
||||
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_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 org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@ -43,8 +47,9 @@ class PacketRegistryTest {
|
||||
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
|
||||
ProtocolUtils.Direction.CLIENTBOUND);
|
||||
registry.register(Handshake.class, Handshake::new,
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, false),
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, false));
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_8, null, false),
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false),
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_15, MINECRAFT_1_16, false));
|
||||
return registry;
|
||||
}
|
||||
|
||||
@ -73,6 +78,8 @@ class PacketRegistryTest {
|
||||
"Registry did not return the correct packet ID");
|
||||
assertNull(registry.getProtocolRegistry(MINECRAFT_1_14_2).createPacket(0x01),
|
||||
"Registry should return a null");
|
||||
assertNull(registry.getProtocolRegistry(MINECRAFT_1_16_2).createPacket(0),
|
||||
"Registry should return null");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -91,12 +98,19 @@ class PacketRegistryTest {
|
||||
ProtocolUtils.Direction.CLIENTBOUND);
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> registry.register(Handshake.class, Handshake::new,
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, false),
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, false)));
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, false),
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_8, null, false)));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> registry.register(Handshake.class, Handshake::new,
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, false),
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, false)));
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_13, null, 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
|
||||
@ -104,13 +118,13 @@ class PacketRegistryTest {
|
||||
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
|
||||
ProtocolUtils.Direction.CLIENTBOUND);
|
||||
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,
|
||||
() -> 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,
|
||||
() -> registry.register(StatusPing.class, StatusPing::new,
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, false)));
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_13, null, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -118,9 +132,9 @@ class PacketRegistryTest {
|
||||
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
|
||||
ProtocolUtils.Direction.CLIENTBOUND);
|
||||
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),
|
||||
false)));
|
||||
null, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -128,9 +142,9 @@ class PacketRegistryTest {
|
||||
StateRegistry.PacketRegistry registry = new StateRegistry.PacketRegistry(
|
||||
ProtocolUtils.Direction.CLIENTBOUND);
|
||||
registry.register(Handshake.class, Handshake::new,
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, false),
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, false),
|
||||
new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, false));
|
||||
new StateRegistry.PacketMapping(0x00, MINECRAFT_1_12, null, false),
|
||||
new StateRegistry.PacketMapping(0x01, MINECRAFT_1_12_1, null, false),
|
||||
new StateRegistry.PacketMapping(0x02, MINECRAFT_1_13, null, false));
|
||||
assertEquals(Handshake.class,
|
||||
registry.getProtocolRegistry(MINECRAFT_1_12).createPacket(0x00).getClass());
|
||||
assertEquals(Handshake.class,
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren