Snapshot 21w15a

This commit is contained in:
FivePB
2021-05-13 17:06:27 +02:00
parent 2220209495
commit 8def411b2b
13 changed files with 540 additions and 48 deletions

View File

@@ -30,6 +30,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;
@@ -45,6 +46,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.network.ConnectionManager;
import com.velocitypowered.proxy.plugin.VelocityEventManager;
@@ -682,4 +684,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);
}
}

View File

@@ -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)) {

View File

@@ -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;
@@ -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

View File

@@ -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.MessagePosition;
@@ -55,6 +57,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;
@@ -76,12 +79,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;
@@ -133,6 +138,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) {
@@ -871,31 +880,133 @@ 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.setRequired(false);
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, boolean isRequired) {
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));
request.setRequired(isRequired);
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) {
CompletableFuture.supplyAsync(() -> {
tickResourcePackQueue();
return true;
});
}
return queued != null && queued.getOrigin() == ResourcePackInfo.Origin.DOWNSTREAM_SERVER;
}
/**

View File

@@ -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;
}
}
}

View File

@@ -226,16 +226,16 @@ public enum StateRegistry {
map(0x39, MINECRAFT_1_16_2, true),
map(0x3C, 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(0x3B, MINECRAFT_1_17, 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(0x3B, MINECRAFT_1_17, false));
clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new,
map(0x47, MINECRAFT_1_8, true),
map(0x48, MINECRAFT_1_9, true),

View File

@@ -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;
@@ -31,6 +33,7 @@ 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;
@@ -56,12 +59,25 @@ public class ResourcePackRequest implements MinecraftPacket {
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;
}
}
}
@@ -74,6 +90,12 @@ public class ResourcePackRequest implements MinecraftPacket {
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);
}
}
}
@@ -87,6 +109,8 @@ public class ResourcePackRequest implements MinecraftPacket {
return "ResourcePackRequest{"
+ "url='" + url + '\''
+ ", hash='" + hash + '\''
+ ", isRequired=" + isRequired
+ ", prompt='" + prompt + '\''
+ '}';
}
}

View File

@@ -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) {