13
0
geforkt von Mirrors/Velocity

Do not apply a resource pack that has already been applied (#1236)

* Do not apply a resource pack that has already been applied

* Throw IllegalStateException in case of applying a resource pack already applied from the API

* Updated some dependencies

* Added support for ServerResourcePackSendEvent resource pack modification
Dieser Commit ist enthalten in:
Adrian 2024-02-11 12:00:07 -05:00 committet von GitHub
Ursprung 15897c5258
Commit ef861819e3
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
9 geänderte Dateien mit 93 neuen und 32 gelöschten Zeilen

Datei anzeigen

@ -54,8 +54,7 @@ public interface ResourcePackInfo extends ResourcePackRequestLike {
* *
* @return the hash if present or null otherwise * @return the hash if present or null otherwise
*/ */
@Nullable byte @Nullable [] getHash();
byte[] getHash();
/** /**
* Gets the {@link Origin} of this resource-pack. * Gets the {@link Origin} of this resource-pack.

Datei anzeigen

@ -2,26 +2,26 @@
configurate3 = "3.7.3" configurate3 = "3.7.3"
configurate4 = "4.1.2" configurate4 = "4.1.2"
flare = "2.0.1" flare = "2.0.1"
log4j = "2.20.0" log4j = "2.22.1"
netty = "4.1.106.Final" netty = "4.1.106.Final"
[plugins] [plugins]
indra-publishing = "net.kyori.indra.publishing:2.0.6" indra-publishing = "net.kyori.indra.publishing:2.0.6"
shadow = "com.github.johnrengelman.shadow:8.1.0" shadow = "com.github.johnrengelman.shadow:8.1.1"
spotless = "com.diffplug.spotless:6.12.0" spotless = "com.diffplug.spotless:6.12.0"
[libraries] [libraries]
adventure-bom = "net.kyori:adventure-bom:4.15.0" adventure-bom = "net.kyori:adventure-bom:4.15.0"
adventure-facet = "net.kyori:adventure-platform-facet:4.3.0" adventure-facet = "net.kyori:adventure-platform-facet:4.3.2"
asm = "org.ow2.asm:asm:9.5" asm = "org.ow2.asm:asm:9.6"
auto-service = "com.google.auto.service:auto-service:1.0.1" auto-service = "com.google.auto.service:auto-service:1.0.1"
auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1" auto-service-annotations = "com.google.auto.service:auto-service-annotations:1.0.1"
brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT" brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT"
bstats = "org.bstats:bstats-base:3.0.1" bstats = "org.bstats:bstats-base:3.0.2"
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.5" caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.5"
checker-qual = "org.checkerframework:checker-qual:3.28.0" checker-qual = "org.checkerframework:checker-qual:3.42.0"
checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3" checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3"
completablefutures = "com.spotify:completable-futures:0.3.5" completablefutures = "com.spotify:completable-futures:0.3.6"
configurate3-hocon = { module = "org.spongepowered:configurate-hocon", version.ref = "configurate3" } configurate3-hocon = { module = "org.spongepowered:configurate-hocon", version.ref = "configurate3" }
configurate3-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate3" } configurate3-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate3" }
configurate3-gson = { module = "org.spongepowered:configurate-gson", version.ref = "configurate3" } configurate3-gson = { module = "org.spongepowered:configurate-gson", version.ref = "configurate3" }
@ -34,7 +34,7 @@ flare-core = { module = "space.vectrix.flare:flare", version.ref = "flare" }
flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = "flare" } flare-fastutil = { module = "space.vectrix.flare:flare-fastutil", version.ref = "flare" }
jline = "org.jline:jline-terminal-jansi:3.23.0" jline = "org.jline:jline-terminal-jansi:3.23.0"
jopt = "net.sf.jopt-simple:jopt-simple:5.0.4" jopt = "net.sf.jopt-simple:jopt-simple:5.0.4"
junit = "org.junit.jupiter:junit-jupiter:5.9.0" junit = "org.junit.jupiter:junit-jupiter:5.10.2"
jspecify = "org.jspecify:jspecify:0.3.0" jspecify = "org.jspecify:jspecify:0.3.0"
kyori-ansi = "net.kyori:ansi:1.0.3" kyori-ansi = "net.kyori:ansi:1.0.3"
guava = "com.google.guava:guava:25.1-jre" guava = "com.google.guava:guava:25.1-jre"
@ -46,7 +46,7 @@ log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "lo
log4j-slf4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" } log4j-slf4j-impl = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", version.ref = "log4j" }
log4j-iostreams = { module = "org.apache.logging.log4j:log4j-iostreams", version.ref = "log4j" } log4j-iostreams = { module = "org.apache.logging.log4j:log4j-iostreams", version.ref = "log4j" }
log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" } log4j-jul = { module = "org.apache.logging.log4j:log4j-jul", version.ref = "log4j" }
mockito = "org.mockito:mockito-core:5.2.0" mockito = "org.mockito:mockito-core:5.10.0"
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" } netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }
netty-codec-haproxy = { module = "io.netty:netty-codec-haproxy", version.ref = "netty" } netty-codec-haproxy = { module = "io.netty:netty-codec-haproxy", version.ref = "netty" }
netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } netty-codec-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
@ -54,7 +54,7 @@ netty-handler = { module = "io.netty:netty-handler", version.ref = "netty" }
netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" } netty-transport-native-epoll = { module = "io.netty:netty-transport-native-epoll", version.ref = "netty" }
netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" } netty-transport-native-kqueue = { module = "io.netty:netty-transport-native-kqueue", version.ref = "netty" }
nightconfig = "com.electronwill.night-config:toml:3.6.7" nightconfig = "com.electronwill.night-config:toml:3.6.7"
slf4j = "org.slf4j:slf4j-api:2.0.7" slf4j = "org.slf4j:slf4j-api:2.0.12"
snakeyaml = "org.yaml:snakeyaml:1.33" snakeyaml = "org.yaml:snakeyaml:1.33"
spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3" spotbugs-annotations = "com.github.spotbugs:spotbugs-annotations:4.7.3"
terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0" terminalconsoleappender = "net.minecrell:terminalconsoleappender:1.3.0"

Datei anzeigen

@ -174,36 +174,49 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
} }
@Override @Override
public boolean handle(ResourcePackRequestPacket packet) { public boolean handle(final ResourcePackRequestPacket packet) {
ResourcePackInfo.Builder builder = new VelocityResourcePackInfo.BuilderImpl( final ResourcePackInfo.Builder builder = new VelocityResourcePackInfo.BuilderImpl(
Preconditions.checkNotNull(packet.getUrl())) Preconditions.checkNotNull(packet.getUrl()))
.setId(packet.getId()) .setId(packet.getId())
.setPrompt(packet.getPrompt() == null ? null : packet.getPrompt().getComponent()) .setPrompt(packet.getPrompt() == null ? null : packet.getPrompt().getComponent())
.setShouldForce(packet.isRequired()) .setShouldForce(packet.isRequired())
.setOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER); .setOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER);
String hash = packet.getHash(); final String hash = packet.getHash();
if (hash != null && !hash.isEmpty()) { if (hash != null && !hash.isEmpty()) {
if (PLAUSIBLE_SHA1_HASH.matcher(hash).matches()) { if (PLAUSIBLE_SHA1_HASH.matcher(hash).matches()) {
builder.setHash(ByteBufUtil.decodeHexDump(hash)); builder.setHash(ByteBufUtil.decodeHexDump(hash));
} }
} }
ServerResourcePackSendEvent event = new ServerResourcePackSendEvent( final ResourcePackInfo resourcePackInfo = builder.build();
builder.build(), this.serverConn); final ServerResourcePackSendEvent event = new ServerResourcePackSendEvent(
resourcePackInfo, this.serverConn);
server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackSendEvent -> { server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackSendEvent -> {
if (playerConnection.isClosed()) { if (playerConnection.isClosed()) {
return; return;
} }
if (serverResourcePackSendEvent.getResult().isAllowed()) { if (serverResourcePackSendEvent.getResult().isAllowed()) {
final ResourcePackInfo toSend = serverResourcePackSendEvent.getProvidedResourcePack(); final ResourcePackInfo toSend = serverResourcePackSendEvent.getProvidedResourcePack();
boolean modifiedPack = false;
if (toSend != serverResourcePackSendEvent.getReceivedResourcePack()) { if (toSend != serverResourcePackSendEvent.getReceivedResourcePack()) {
((VelocityResourcePackInfo) toSend) ((VelocityResourcePackInfo) toSend)
.setOriginalOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER); .setOriginalOrigin(ResourcePackInfo.Origin.DOWNSTREAM_SERVER);
modifiedPack = true;
}
if (serverConn.getPlayer().resourcePackHandler().hasPackAppliedByHash(toSend.getHash())) {
// Do not apply a resource pack that has already been applied
if (serverConn.getConnection() != null) {
serverConn.getConnection().write(new ResourcePackResponsePacket(
packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.ACCEPTED));
}
if (modifiedPack) {
logger.warn("A plugin has tried to modify a ResourcePack provided by the backend server "
+ "with a ResourcePack already applied, the applying of the resource pack will be skipped.");
}
} else {
serverConn.getPlayer().resourcePackHandler().queueResourcePack(toSend);
} }
serverConn.getPlayer().resourcePackHandler().queueResourcePack(toSend);
} else if (serverConn.getConnection() != null) { } else if (serverConn.getConnection() != null) {
serverConn.getConnection().write(new ResourcePackResponsePacket( serverConn.getConnection().write(new ResourcePackResponsePacket(
packet.getId(), packet.getId(),

Datei anzeigen

@ -45,7 +45,6 @@ import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket;
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -54,8 +53,6 @@ import org.apache.logging.log4j.Logger;
* 1.20.2+ switching. Yes, some of this is exceptionally stupid. * 1.20.2+ switching. Yes, some of this is exceptionally stupid.
*/ */
public class ConfigSessionHandler implements MinecraftSessionHandler { public class ConfigSessionHandler implements MinecraftSessionHandler {
private static final Pattern PLAUSIBLE_SHA1_HASH = Pattern.compile("^[a-z0-9]{40}$");
private static final Logger logger = LogManager.getLogger(ConfigSessionHandler.class); private static final Logger logger = LogManager.getLogger(ConfigSessionHandler.class);
private final VelocityServer server; private final VelocityServer server;
private final VelocityServerConnection serverConn; private final VelocityServerConnection serverConn;
@ -118,25 +115,39 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
} }
@Override @Override
public boolean handle(ResourcePackRequestPacket packet) { public boolean handle(final ResourcePackRequestPacket packet) {
final MinecraftConnection playerConnection = serverConn.getPlayer().getConnection(); final MinecraftConnection playerConnection = serverConn.getPlayer().getConnection();
ServerResourcePackSendEvent event = final ResourcePackInfo resourcePackInfo = packet.toServerPromptedPack();
new ServerResourcePackSendEvent(packet.toServerPromptedPack(), this.serverConn); final ServerResourcePackSendEvent event =
new ServerResourcePackSendEvent(resourcePackInfo, this.serverConn);
server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackSendEvent -> { server.getEventManager().fire(event).thenAcceptAsync(serverResourcePackSendEvent -> {
if (playerConnection.isClosed()) { if (playerConnection.isClosed()) {
return; return;
} }
if (serverResourcePackSendEvent.getResult().isAllowed()) { if (serverResourcePackSendEvent.getResult().isAllowed()) {
ResourcePackInfo toSend = serverResourcePackSendEvent.getProvidedResourcePack(); final ResourcePackInfo toSend = serverResourcePackSendEvent.getProvidedResourcePack();
boolean modifiedPack = false;
if (toSend != serverResourcePackSendEvent.getReceivedResourcePack()) { if (toSend != serverResourcePackSendEvent.getReceivedResourcePack()) {
((VelocityResourcePackInfo) toSend).setOriginalOrigin( ((VelocityResourcePackInfo) toSend).setOriginalOrigin(
ResourcePackInfo.Origin.DOWNSTREAM_SERVER); ResourcePackInfo.Origin.DOWNSTREAM_SERVER);
modifiedPack = true;
}
if (serverConn.getPlayer().resourcePackHandler().hasPackAppliedByHash(toSend.getHash())) {
// Do not apply a resource pack that has already been applied
if (serverConn.getConnection() != null) {
serverConn.getConnection().write(new ResourcePackResponsePacket(
packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.ACCEPTED));
}
if (modifiedPack) {
logger.warn("A plugin has tried to modify a ResourcePack provided by the backend server "
+ "with a ResourcePack already applied, the applying of the resource pack will be skipped.");
}
} else {
resourcePackToApply = null;
serverConn.getPlayer().resourcePackHandler().queueResourcePack(toSend);
} }
resourcePackToApply = null;
serverConn.getPlayer().resourcePackHandler().queueResourcePack(toSend);
} else if (serverConn.getConnection() != null) { } else if (serverConn.getConnection() != null) {
serverConn.getConnection().write(new ResourcePackResponsePacket( serverConn.getConnection().write(new ResourcePackResponsePacket(
packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.DECLINED)); packet.getId(), packet.getHash(), PlayerResourcePackStatusEvent.Status.DECLINED));

Datei anzeigen

@ -1027,6 +1027,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override @Override
public void sendResourcePackOffer(ResourcePackInfo packInfo) { public void sendResourcePackOffer(ResourcePackInfo packInfo) {
this.resourcePackHandler.checkAlreadyAppliedPack(packInfo.getHash());
if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) { if (this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
Preconditions.checkNotNull(packInfo, "packInfo"); Preconditions.checkNotNull(packInfo, "packInfo");
this.resourcePackHandler.queueResourcePack(packInfo); this.resourcePackHandler.queueResourcePack(packInfo);

Datei anzeigen

@ -23,6 +23,7 @@ import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
@ -169,6 +170,12 @@ public sealed class LegacyResourcePackHandler extends ResourcePackHandler
return handleResponseResult(queued, bundle); return handleResponseResult(queued, bundle);
} }
@Override
public boolean hasPackAppliedByHash(final byte[] hash) {
return this.appliedResourcePack != null
&& Arrays.equals(this.appliedResourcePack.getHash(), hash);
}
protected boolean shouldDisconnectForForcePack(final PlayerResourcePackStatusEvent event) { protected boolean shouldDisconnectForForcePack(final PlayerResourcePackStatusEvent event) {
return event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED return event.getStatus() == PlayerResourcePackStatusEvent.Status.DECLINED
&& event.getPackInfo() != null && event.getPackInfo().getShouldForce(); && event.getPackInfo() != null && event.getPackInfo().getShouldForce();

Datei anzeigen

@ -23,6 +23,7 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -167,4 +168,17 @@ public final class ModernResourcePackHandler extends ResourcePackHandler {
return handleResponseResult(queued, bundle); return handleResponseResult(queued, bundle);
} }
@Override
public boolean hasPackAppliedByHash(final byte[] hash) {
if (hash == null) {
return false;
}
for (final Map.Entry<UUID, ResourcePackInfo> appliedPack : appliedResourcePacks.entrySet()) {
if (Arrays.equals(appliedPack.getValue().getHash(), hash)) {
return true;
}
}
return false;
}
} }

Datei anzeigen

@ -92,7 +92,9 @@ public abstract sealed class ResourcePackHandler
*/ */
public void queueResourcePack(final @NotNull ResourcePackRequest request) { public void queueResourcePack(final @NotNull ResourcePackRequest request) {
for (final net.kyori.adventure.resource.ResourcePackInfo pack : request.packs()) { for (final net.kyori.adventure.resource.ResourcePackInfo pack : request.packs()) {
queueResourcePack(VelocityResourcePackInfo.fromAdventureRequest(request, pack)); final ResourcePackInfo resourcePackInfo = VelocityResourcePackInfo.fromAdventureRequest(request, pack);
this.checkAlreadyAppliedPack(resourcePackInfo.getHash());
queueResourcePack(resourcePackInfo);
} }
} }
@ -156,4 +158,18 @@ public abstract sealed class ResourcePackHandler
} }
return handled; return handled;
} }
/**
* Check if a pack has already been applied.
*
* @param hash the resource pack hash
*/
public abstract boolean hasPackAppliedByHash(final byte[] hash);
@SuppressWarnings("checkstyle:MissingJavadocMethod")
public void checkAlreadyAppliedPack(final byte[] hash) {
if (this.hasPackAppliedByHash(hash)) {
throw new IllegalStateException("Cannot apply a resource pack already applied");
}
}
} }

Datei anzeigen

@ -17,7 +17,7 @@ pluginManagement {
} }
plugins { plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.4.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
} }
rootProject.name = "velocity" rootProject.name = "velocity"