diff --git a/build.gradle b/build.gradle index 0ff6be509..2e57eb060 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ allprojects { ext { // dependency versions - textVersion = '3.0.2' + textVersion = '3.0.3' junitVersion = '5.3.0-M1' slf4jVersion = '1.7.25' log4jVersion = '2.11.2' diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java b/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java index 89ed3ad8e..1ef700f57 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/PingPassthroughMode.java @@ -3,5 +3,6 @@ package com.velocitypowered.proxy.config; public enum PingPassthroughMode { DISABLED, MODS, + DESCRIPTION, ALL } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index 467363daf..806ef44ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -90,14 +90,17 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @Comment({ "Should Velocity pass server list ping requests to a backend server?", "Available options:", - "- \"disabled\": No pass-through will be done. The velocity.toml and server-icon.png", - " will determine the initial server list ping response.", - "- \"mods\": Passes only the mod list from your backend server into the response.", - " The first server in your try list (or forced host) with a mod list will be", - " used. If no backend servers can be contacted, Velocity will not display any", - " mod information.", - "- \"all\": Passes everything from the backend server into the response. The Velocity", - " configuration is used if no servers could be contacted." + "- \"disabled\": No pass-through will be done. The velocity.toml and server-icon.png", + " will determine the initial server list ping response.", + "- \"mods\": Passes only the mod list from your backend server into the response.", + " The first server in your try list (or forced host) with a mod list will be", + " used. If no backend servers can be contacted, Velocity won't display any", + " mod information.", + "- \"description\": Uses the description and mod list from the backend server. The first", + " server in the try (or forced host) list that responds is used for the", + " description and mod list.", + "- \"all\": Uses the backend server's response as the proxy response. The Velocity", + " configuration is used if no servers could be contacted." }) @ConfigKey("ping-passthrough") private PingPassthroughMode pingPassthrough = PingPassthroughMode.DISABLED; @@ -505,7 +508,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi Map servers = new HashMap<>(); for (Map.Entry entry : toml.entrySet()) { if (entry.getValue() instanceof String) { - servers.put(entry.getKey(), (String) entry.getValue()); + servers.put(cleanServerName(entry.getKey()), (String) entry.getValue()); } else { if (!entry.getKey().equalsIgnoreCase("try")) { throw new IllegalArgumentException( @@ -539,6 +542,19 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi this.attemptConnectionOrder = attemptConnectionOrder; } + /** + * TOML requires keys to match a regex of {@code [A-Za-z0-9_-]} unless it is wrapped in + * quotes; however, the TOML parser returns the key with the quotes so we need to clean the + * server name before we pass it onto server registration to keep proper server name behavior. + * + * @param name the server name to clean + * + * @return the cleaned server name + */ + private String cleanServerName(String name) { + return name.replace("\"", ""); + } + @Override public String toString() { return "Servers{" diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java index 613271733..5fbbac0de 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/StatusSessionHandler.java @@ -97,6 +97,24 @@ public class StatusSessionHandler implements MinecraftSessionHandler { } return fallback; }); + case DESCRIPTION: + return pingResponses.thenApply(responses -> { + // Find the first non-fallback. If it includes a modlist, add it too. + for (ServerPing response : responses) { + if (response == fallback) { + continue; + } + + return new ServerPing( + fallback.getVersion(), + fallback.getPlayers().orElse(null), + response.getDescription(), + fallback.getFavicon().orElse(null), + response.getModinfo().orElse(null) + ); + } + return fallback; + }); default: // Not possible, but covered for completeness. return CompletableFuture.completedFuture(fallback); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java index 55da0dd78..0204aefb3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java @@ -2,17 +2,14 @@ package com.velocitypowered.proxy.protocol.netty; import com.google.common.base.Preconditions; import com.velocitypowered.api.network.ProtocolVersion; -import com.velocitypowered.proxy.network.Connections; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.util.except.QuietException; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.util.ReferenceCountUtil; import java.util.List; public class MinecraftDecoder extends MessageToMessageDecoder { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java index 0bd4bcd3d..1bec0d119 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/scheduler/VelocityScheduler.java @@ -3,7 +3,6 @@ package com.velocitypowered.proxy.scheduler; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -187,6 +186,9 @@ public class VelocityScheduler implements Scheduler { } catch (Exception e) { Log.logger.error("Exception in task {} by plugin {}", runnable, plugin, e); } finally { + if (repeat == 0) { + onFinish(); + } currentTaskThread = null; } }); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java index b1e5c2610..8e5eecf43 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/tablist/VelocityTabListLegacy.java @@ -60,29 +60,25 @@ public class VelocityTabListLegacy extends VelocityTabList { Item item = packet.getItems().get(0); // Only one item per packet in 1.7 - Component displayName = LegacyComponentSerializer.legacy().deserialize(item.getName()); - String strippedName = PlainComponentSerializer.INSTANCE.serialize(displayName); - switch (packet.getAction()) { case PlayerListItem.ADD_PLAYER: - if (nameMapping.containsKey(strippedName)) { // ADD_PLAYER also used for updating ping - VelocityTabListEntry entry = entries.get(nameMapping.get(strippedName)); + if (nameMapping.containsKey(item.getName())) { // ADD_PLAYER also used for updating ping + VelocityTabListEntry entry = entries.get(nameMapping.get(item.getName())); if (entry != null) { - entry.setLatency(item.getLatency()); + entry.setLatencyInternal(item.getLatency()); } } else { UUID uuid = UUID.randomUUID(); // Use a fake uuid to preserve function of custom entries - nameMapping.put(strippedName, uuid); + nameMapping.put(item.getName(), uuid); entries.put(uuid, (VelocityTabListEntry) TabListEntry.builder() .tabList(this) - .profile(new GameProfile(uuid, strippedName, ImmutableList.of())) - .displayName(displayName) + .profile(new GameProfile(uuid, item.getName(), ImmutableList.of())) .latency(item.getLatency()) .build()); } break; case PlayerListItem.REMOVE_PLAYER: - UUID removedUuid = nameMapping.remove(strippedName); + UUID removedUuid = nameMapping.remove(item.getName()); if (removedUuid != null) { entries.remove(removedUuid); }