Merge remote-tracking branch 'github/dev/3.0.0'
All checks were successful
SteamWarCI Build successful
All checks were successful
SteamWarCI Build successful
# Conflicts: # proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java
This commit is contained in:
7
.github/workflows/gradle.yml
vendored
7
.github/workflows/gradle.yml
vendored
@ -10,13 +10,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Validate Gradle Wrapper
|
with:
|
||||||
uses: gradle/actions/wrapper-validation@v3
|
persist-credentials: false
|
||||||
|
- name: Set up Gradle
|
||||||
|
uses: gradle/actions/setup-gradle@v4
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
cache: 'gradle'
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|||||||
@ -61,6 +61,7 @@ tasks {
|
|||||||
o.encoding = "UTF-8"
|
o.encoding = "UTF-8"
|
||||||
o.source = "17"
|
o.source = "17"
|
||||||
|
|
||||||
|
o.use()
|
||||||
o.links(
|
o.links(
|
||||||
"https://www.slf4j.org/apidocs/",
|
"https://www.slf4j.org/apidocs/",
|
||||||
"https://guava.dev/releases/${libs.guava.get().version}/api/docs/",
|
"https://guava.dev/releases/${libs.guava.get().version}/api/docs/",
|
||||||
|
|||||||
@ -27,7 +27,7 @@ public interface CommandSource extends Audience, PermissionSubject {
|
|||||||
* for more information on the format.
|
* for more information on the format.
|
||||||
**/
|
**/
|
||||||
default void sendRichMessage(final @NotNull String message) {
|
default void sendRichMessage(final @NotNull String message) {
|
||||||
this.sendMessage(MiniMessage.miniMessage().deserialize(message));
|
this.sendMessage(MiniMessage.miniMessage().deserialize(message, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,7 +43,7 @@ public interface CommandSource extends Audience, PermissionSubject {
|
|||||||
final @NotNull String message,
|
final @NotNull String message,
|
||||||
final @NotNull TagResolver @NotNull... resolvers
|
final @NotNull TagResolver @NotNull... resolvers
|
||||||
) {
|
) {
|
||||||
this.sendMessage(MiniMessage.miniMessage().deserialize(message, resolvers));
|
this.sendMessage(MiniMessage.miniMessage().deserialize(message, this, resolvers));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -12,6 +12,14 @@ package com.velocitypowered.api.event;
|
|||||||
*/
|
*/
|
||||||
public enum PostOrder {
|
public enum PostOrder {
|
||||||
|
|
||||||
FIRST, EARLY, NORMAL, LATE, LAST, CUSTOM
|
FIRST, EARLY, NORMAL, LATE, LAST,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Previously used to specify that {@link Subscribe#priority()} should be used.
|
||||||
|
*
|
||||||
|
* @deprecated No longer required, you only need to specify {@link Subscribe#priority()}.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
CUSTOM
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,12 +32,9 @@ public @interface Subscribe {
|
|||||||
* The priority of this event handler. Priorities are used to determine the order in which event
|
* The priority of this event handler. Priorities are used to determine the order in which event
|
||||||
* handlers are called. The higher the priority, the earlier the event handler will be called.
|
* handlers are called. The higher the priority, the earlier the event handler will be called.
|
||||||
*
|
*
|
||||||
* <p>Note that due to compatibility constraints, you must specify {@link PostOrder#CUSTOM}
|
|
||||||
* in order to use this field.</p>
|
|
||||||
*
|
|
||||||
* @return the priority
|
* @return the priority
|
||||||
*/
|
*/
|
||||||
short priority() default Short.MIN_VALUE;
|
short priority() default 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the handler must be called asynchronously. By default, all event handlers are called
|
* Whether the handler must be called asynchronously. By default, all event handlers are called
|
||||||
|
|||||||
@ -9,7 +9,6 @@ package com.velocitypowered.api.event.command;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
import com.mojang.brigadier.tree.RootCommandNode;
|
import com.mojang.brigadier.tree.RootCommandNode;
|
||||||
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
import com.velocitypowered.api.event.annotation.AwaitingEvent;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
@ -21,7 +20,6 @@ import com.velocitypowered.api.proxy.Player;
|
|||||||
* client.
|
* client.
|
||||||
*/
|
*/
|
||||||
@AwaitingEvent
|
@AwaitingEvent
|
||||||
@Beta
|
|
||||||
public class PlayerAvailableCommandsEvent {
|
public class PlayerAvailableCommandsEvent {
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.player;
|
package com.velocitypowered.api.event.player;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
@ -18,7 +17,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
* available in {@link Player#getCurrentServer()}. Velocity will not wait on this event to finish
|
* available in {@link Player#getCurrentServer()}. Velocity will not wait on this event to finish
|
||||||
* firing.
|
* firing.
|
||||||
*/
|
*/
|
||||||
@Beta
|
|
||||||
public class ServerPostConnectEvent {
|
public class ServerPostConnectEvent {
|
||||||
private final Player player;
|
private final Player player;
|
||||||
private final RegisteredServer previousServer;
|
private final RegisteredServer previousServer;
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.proxy.server;
|
package com.velocitypowered.api.event.proxy.server;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
@ -23,7 +22,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param registeredServer A {@link RegisteredServer} that has been registered.
|
* @param registeredServer A {@link RegisteredServer} that has been registered.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
*/
|
*/
|
||||||
@Beta
|
|
||||||
public record ServerRegisteredEvent(@NotNull RegisteredServer registeredServer) {
|
public record ServerRegisteredEvent(@NotNull RegisteredServer registeredServer) {
|
||||||
public ServerRegisteredEvent {
|
public ServerRegisteredEvent {
|
||||||
Preconditions.checkNotNull(registeredServer, "registeredServer");
|
Preconditions.checkNotNull(registeredServer, "registeredServer");
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.event.proxy.server;
|
package com.velocitypowered.api.event.proxy.server;
|
||||||
|
|
||||||
import com.google.common.annotations.Beta;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
@ -23,7 +22,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param unregisteredServer A {@link RegisteredServer} that has been unregistered.
|
* @param unregisteredServer A {@link RegisteredServer} that has been unregistered.
|
||||||
* @since 3.3.0
|
* @since 3.3.0
|
||||||
*/
|
*/
|
||||||
@Beta
|
|
||||||
public record ServerUnregisteredEvent(@NotNull RegisteredServer unregisteredServer) {
|
public record ServerUnregisteredEvent(@NotNull RegisteredServer unregisteredServer) {
|
||||||
public ServerUnregisteredEvent {
|
public ServerUnregisteredEvent {
|
||||||
Preconditions.checkNotNull(unregisteredServer, "unregisteredServer");
|
Preconditions.checkNotNull(unregisteredServer, "unregisteredServer");
|
||||||
|
|||||||
@ -89,7 +89,8 @@ public enum ProtocolVersion implements Ordered<ProtocolVersion> {
|
|||||||
MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"),
|
MINECRAFT_1_20_5(766, "1.20.5", "1.20.6"),
|
||||||
MINECRAFT_1_21(767, "1.21", "1.21.1"),
|
MINECRAFT_1_21(767, "1.21", "1.21.1"),
|
||||||
MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"),
|
MINECRAFT_1_21_2(768, "1.21.2", "1.21.3"),
|
||||||
MINECRAFT_1_21_4(769, "1.21.4");
|
MINECRAFT_1_21_4(769, "1.21.4"),
|
||||||
|
MINECRAFT_1_21_5(770, /*1073742067,*/ "1.21.5");
|
||||||
|
|
||||||
private static final int SNAPSHOT_BIT = 30;
|
private static final int SNAPSHOT_BIT = 30;
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.api.proxy;
|
package com.velocitypowered.api.proxy;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.HandshakeIntent;
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@ -60,4 +61,11 @@ public interface InboundConnection {
|
|||||||
* @return the protocol state of the connection
|
* @return the protocol state of the connection
|
||||||
*/
|
*/
|
||||||
ProtocolState getProtocolState();
|
ProtocolState getProtocolState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the initial intent for the connection.
|
||||||
|
*
|
||||||
|
* @return the intent of the connection
|
||||||
|
*/
|
||||||
|
HandshakeIntent getHandshakeIntent();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,13 @@ public interface ProxyServer extends Audience {
|
|||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the proxy is currently shutting down.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the proxy is shutting down, {@code false} otherwise
|
||||||
|
*/
|
||||||
|
boolean isShuttingDown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes all listening endpoints for this server.
|
* Closes all listening endpoints for this server.
|
||||||
* This includes the main minecraft listener and query channel.
|
* This includes the main minecraft listener and query channel.
|
||||||
|
|||||||
@ -40,6 +40,7 @@ public interface TabList {
|
|||||||
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
|
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
|
||||||
*
|
*
|
||||||
* @param entry to add to the tab list
|
* @param entry to add to the tab list
|
||||||
|
* @throws IllegalArgumentException on versions below 1.19.3, if an entry with the same UUID already exists
|
||||||
*/
|
*/
|
||||||
void addEntry(TabListEntry entry);
|
void addEntry(TabListEntry entry);
|
||||||
|
|
||||||
@ -47,6 +48,7 @@ public interface TabList {
|
|||||||
* Adds a {@link Iterable} of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
* Adds a {@link Iterable} of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
||||||
*
|
*
|
||||||
* @param entries to add to the tab list
|
* @param entries to add to the tab list
|
||||||
|
* @throws IllegalArgumentException on versions below 1.19.3, if an entry with the same UUID already exists
|
||||||
*/
|
*/
|
||||||
default void addEntries(Iterable<TabListEntry> entries) {
|
default void addEntries(Iterable<TabListEntry> entries) {
|
||||||
for (TabListEntry entry : entries) {
|
for (TabListEntry entry : entries) {
|
||||||
@ -58,6 +60,7 @@ public interface TabList {
|
|||||||
* Adds an array of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
* Adds an array of {@link TabListEntry}'s to the {@link Player}'s tab list.
|
||||||
*
|
*
|
||||||
* @param entries to add to the tab list
|
* @param entries to add to the tab list
|
||||||
|
* @throws IllegalArgumentException on versions below 1.19.3, if an entry with the same UUID already exists
|
||||||
*/
|
*/
|
||||||
default void addEntries(TabListEntry... entries) {
|
default void addEntries(TabListEntry... entries) {
|
||||||
for (TabListEntry entry : entries) {
|
for (TabListEntry entry : entries) {
|
||||||
@ -187,6 +190,26 @@ public interface TabList {
|
|||||||
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
default TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
|
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder) {
|
||||||
|
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an entry in a {@link Player}'s tab list.
|
||||||
|
*
|
||||||
|
* @param profile the profile
|
||||||
|
* @param displayName the display name
|
||||||
|
* @param latency the latency
|
||||||
|
* @param gameMode the game mode
|
||||||
|
* @param chatSession the chat session
|
||||||
|
* @param listed the visible status of entry
|
||||||
|
* @param listOrder the order/priority of entry in the tab list
|
||||||
|
* @param showHat the visibility of this entry's hat layer
|
||||||
|
* @return the entry
|
||||||
|
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder);
|
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder, boolean showHat);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
* <li>150-300 will display 4 bars</li>
|
* <li>150-300 will display 4 bars</li>
|
||||||
* <li>300-600 will display 3 bars</li>
|
* <li>300-600 will display 3 bars</li>
|
||||||
* <li>600-1000 will display 2 bars</li>
|
* <li>600-1000 will display 2 bars</li>
|
||||||
* <li>A latency move than 1 second will display 1 bar</li>
|
* <li>A latency greater than 1 second will display 1 bar</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @return latency set for {@code this} entry
|
* @return latency set for {@code this} entry
|
||||||
@ -160,6 +160,27 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this entry's hat layer is shown in the tab list.
|
||||||
|
*
|
||||||
|
* @return whether to show this entry's hat layer
|
||||||
|
* @sinceMinecraft 1.21.4
|
||||||
|
*/
|
||||||
|
default boolean isShowHat() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to show this entry's hat layer in the tab list.
|
||||||
|
*
|
||||||
|
* @param showHat whether to show this entry's hat layer
|
||||||
|
* @return {@code this}, for chaining
|
||||||
|
* @sinceMinecraft 1.21.4
|
||||||
|
*/
|
||||||
|
default TabListEntry setShowHat(boolean showHat) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Builder} to create a {@link TabListEntry}.
|
* Returns a {@link Builder} to create a {@link TabListEntry}.
|
||||||
*
|
*
|
||||||
@ -183,6 +204,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
private int gameMode = 0;
|
private int gameMode = 0;
|
||||||
private boolean listed = true;
|
private boolean listed = true;
|
||||||
private int listOrder = 0;
|
private int listOrder = 0;
|
||||||
|
private boolean showHat;
|
||||||
|
|
||||||
private @Nullable ChatSession chatSession;
|
private @Nullable ChatSession chatSession;
|
||||||
|
|
||||||
@ -268,7 +290,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
* Sets whether this entry should be visible.
|
* Sets whether this entry should be visible.
|
||||||
*
|
*
|
||||||
* @param listed to set
|
* @param listed to set
|
||||||
* @return ${code this}, for chaining
|
* @return {@code this}, for chaining
|
||||||
* @see TabListEntry#isListed()
|
* @see TabListEntry#isListed()
|
||||||
*/
|
*/
|
||||||
public Builder listed(boolean listed) {
|
public Builder listed(boolean listed) {
|
||||||
@ -280,7 +302,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
* Sets the order/priority of this entry in the tab list.
|
* Sets the order/priority of this entry in the tab list.
|
||||||
*
|
*
|
||||||
* @param order to set
|
* @param order to set
|
||||||
* @return ${code this}, for chaining
|
* @return {@code this}, for chaining
|
||||||
* @sinceMinecraft 1.21.2
|
* @sinceMinecraft 1.21.2
|
||||||
* @see TabListEntry#getListOrder()
|
* @see TabListEntry#getListOrder()
|
||||||
*/
|
*/
|
||||||
@ -289,6 +311,18 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether this entry's hat layer should be shown in the tab list.
|
||||||
|
*
|
||||||
|
* @param showHat to set
|
||||||
|
* @return {@code this}, for chaining
|
||||||
|
* @see TabListEntry#isShowHat()
|
||||||
|
*/
|
||||||
|
public Builder showHat(boolean showHat) {
|
||||||
|
this.showHat = showHat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
|
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
|
||||||
*
|
*
|
||||||
@ -301,7 +335,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
|||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
|
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
|
||||||
}
|
}
|
||||||
return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder);
|
return tabList.buildEntry(profile, displayName, latency, gameMode, chatSession, listed, listOrder, showHat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import java.util.Objects;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import net.kyori.adventure.builder.AbstractBuilder;
|
import net.kyori.adventure.builder.AbstractBuilder;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains the parameters used to ping a {@link RegisteredServer}.
|
* Contains the parameters used to ping a {@link RegisteredServer}.
|
||||||
@ -30,10 +31,12 @@ public final class PingOptions {
|
|||||||
public static final PingOptions DEFAULT = PingOptions.builder().build();
|
public static final PingOptions DEFAULT = PingOptions.builder().build();
|
||||||
private final ProtocolVersion protocolVersion;
|
private final ProtocolVersion protocolVersion;
|
||||||
private final long timeout;
|
private final long timeout;
|
||||||
|
private final String virtualHost;
|
||||||
|
|
||||||
private PingOptions(final Builder builder) {
|
private PingOptions(final Builder builder) {
|
||||||
this.protocolVersion = builder.protocolVersion;
|
this.protocolVersion = builder.protocolVersion;
|
||||||
this.timeout = builder.timeout;
|
this.timeout = builder.timeout;
|
||||||
|
this.virtualHost = builder.virtualHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,6 +57,16 @@ public final class PingOptions {
|
|||||||
return this.timeout;
|
return this.timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The virtual host to pass to the server for the ping.
|
||||||
|
*
|
||||||
|
* @return the virtual hostname to pass to the server for the ping
|
||||||
|
* @since 3.4.0
|
||||||
|
*/
|
||||||
|
public @Nullable String getVirtualHost() {
|
||||||
|
return this.virtualHost;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new builder to assign values to a new PingOptions.
|
* Create a new builder to assign values to a new PingOptions.
|
||||||
*
|
*
|
||||||
@ -68,10 +81,9 @@ public final class PingOptions {
|
|||||||
if (o == null) {
|
if (o == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!(o instanceof PingOptions)) {
|
if (!(o instanceof final PingOptions other)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final PingOptions other = (PingOptions) o;
|
|
||||||
return Objects.equals(this.protocolVersion, other.protocolVersion)
|
return Objects.equals(this.protocolVersion, other.protocolVersion)
|
||||||
&& Objects.equals(this.timeout, other.timeout);
|
&& Objects.equals(this.timeout, other.timeout);
|
||||||
}
|
}
|
||||||
@ -97,6 +109,7 @@ public final class PingOptions {
|
|||||||
public static final class Builder implements AbstractBuilder<PingOptions> {
|
public static final class Builder implements AbstractBuilder<PingOptions> {
|
||||||
private ProtocolVersion protocolVersion = ProtocolVersion.UNKNOWN;
|
private ProtocolVersion protocolVersion = ProtocolVersion.UNKNOWN;
|
||||||
private long timeout = 0;
|
private long timeout = 0;
|
||||||
|
private String virtualHost = null;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
}
|
}
|
||||||
@ -146,6 +159,18 @@ public final class PingOptions {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the virtual host to pass to the server for the ping.
|
||||||
|
*
|
||||||
|
* @param virtualHost the virtual hostname to pass to the server for the ping
|
||||||
|
* @return this builder
|
||||||
|
* @since 3.4.0
|
||||||
|
*/
|
||||||
|
public Builder virtualHost(final @Nullable String virtualHost) {
|
||||||
|
this.virtualHost = virtualHost;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link PingOptions} with the values of this Builder.
|
* Create a new {@link PingOptions} with the values of this Builder.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import com.velocitypowered.api.util.Favicon;
|
|||||||
import com.velocitypowered.api.util.ModInfo;
|
import com.velocitypowered.api.util.ModInfo;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@ -160,31 +161,79 @@ public final class ServerPing {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code version} info in the response.
|
||||||
|
*
|
||||||
|
* @param version version info to set
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder version(Version version) {
|
public Builder version(Version version) {
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
this.version = Preconditions.checkNotNull(version, "version");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code onlinePlayers} number in the response.
|
||||||
|
*
|
||||||
|
* @param onlinePlayers number for online players to set
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder onlinePlayers(int onlinePlayers) {
|
public Builder onlinePlayers(int onlinePlayers) {
|
||||||
this.onlinePlayers = onlinePlayers;
|
this.onlinePlayers = onlinePlayers;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code maximumPlayers} number in the response.
|
||||||
|
* <b>This will not modify the actual maximum players that can join the server.</b>
|
||||||
|
*
|
||||||
|
* @param maximumPlayers number for maximum players to set
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder maximumPlayers(int maximumPlayers) {
|
public Builder maximumPlayers(int maximumPlayers) {
|
||||||
this.maximumPlayers = maximumPlayers;
|
this.maximumPlayers = maximumPlayers;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code players} array in the response.
|
||||||
|
*
|
||||||
|
* @param players array of SamplePlayers to add
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder samplePlayers(SamplePlayer... players) {
|
public Builder samplePlayers(SamplePlayer... players) {
|
||||||
this.samplePlayers.addAll(Arrays.asList(players));
|
this.samplePlayers.addAll(Arrays.asList(players));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code players} collection in the response.
|
||||||
|
*
|
||||||
|
* @param players collection of SamplePlayers to add
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
|
public Builder samplePlayers(Collection<SamplePlayer> players) {
|
||||||
|
this.samplePlayers.addAll(players);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code modType} in the response.
|
||||||
|
*
|
||||||
|
* @param modType the mod type to set
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder modType(String modType) {
|
public Builder modType(String modType) {
|
||||||
this.modType = Preconditions.checkNotNull(modType, "modType");
|
this.modType = Preconditions.checkNotNull(modType, "modType");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the modified {@code mods} array in the response.
|
||||||
|
*
|
||||||
|
* @param mods array of mods to use
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder mods(ModInfo.Mod... mods) {
|
public Builder mods(ModInfo.Mod... mods) {
|
||||||
this.mods.addAll(Arrays.asList(mods));
|
this.mods.addAll(Arrays.asList(mods));
|
||||||
return this;
|
return this;
|
||||||
@ -194,7 +243,7 @@ public final class ServerPing {
|
|||||||
* Uses the modified {@code mods} list in the response.
|
* Uses the modified {@code mods} list in the response.
|
||||||
*
|
*
|
||||||
* @param mods the mods list to use
|
* @param mods the mods list to use
|
||||||
* @return this build, for chaining
|
* @return this builder, for chaining
|
||||||
*/
|
*/
|
||||||
public Builder mods(ModInfo mods) {
|
public Builder mods(ModInfo mods) {
|
||||||
Preconditions.checkNotNull(mods, "mods");
|
Preconditions.checkNotNull(mods, "mods");
|
||||||
@ -204,36 +253,74 @@ public final class ServerPing {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current list of mods to use in the response.
|
||||||
|
*
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder clearMods() {
|
public Builder clearMods() {
|
||||||
this.mods.clear();
|
this.mods.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current list of PlayerSamples to use in the response.
|
||||||
|
*
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder clearSamplePlayers() {
|
public Builder clearSamplePlayers() {
|
||||||
this.samplePlayers.clear();
|
this.samplePlayers.clear();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the server as mod incompatible in the response.
|
||||||
|
*
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder notModCompatible() {
|
public Builder notModCompatible() {
|
||||||
this.nullOutModinfo = true;
|
this.nullOutModinfo = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables nulling Players in the response.
|
||||||
|
* This will display the player count as {@code ???}.
|
||||||
|
*
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder nullPlayers() {
|
public Builder nullPlayers() {
|
||||||
this.nullOutPlayers = true;
|
this.nullOutPlayers = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the {@code description} Component in the response.
|
||||||
|
*
|
||||||
|
* @param description Component to use as the description.
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder description(net.kyori.adventure.text.Component description) {
|
public Builder description(net.kyori.adventure.text.Component description) {
|
||||||
this.description = Preconditions.checkNotNull(description, "description");
|
this.description = Preconditions.checkNotNull(description, "description");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the {@code favicon} in the response.
|
||||||
|
*
|
||||||
|
* @param favicon Favicon instance to use.
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder favicon(Favicon favicon) {
|
public Builder favicon(Favicon favicon) {
|
||||||
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
|
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the current favicon used in the response.
|
||||||
|
*
|
||||||
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
public Builder clearFavicon() {
|
public Builder clearFavicon() {
|
||||||
this.favicon = null;
|
this.favicon = null;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@ -76,9 +76,17 @@ public final class ModInfo {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final String version;
|
private final String version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new mod info.
|
||||||
|
*
|
||||||
|
* @param id the mod identifier
|
||||||
|
* @param version the mod version
|
||||||
|
*/
|
||||||
public Mod(String id, String version) {
|
public Mod(String id, String version) {
|
||||||
this.id = Preconditions.checkNotNull(id, "id");
|
this.id = Preconditions.checkNotNull(id, "id");
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
this.version = Preconditions.checkNotNull(version, "version");
|
||||||
|
Preconditions.checkArgument(id.length() < 128, "mod id is too long");
|
||||||
|
Preconditions.checkArgument(version.length() < 128, "mod version is too long");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ configurate3 = "3.7.3"
|
|||||||
configurate4 = "4.1.2"
|
configurate4 = "4.1.2"
|
||||||
flare = "2.0.1"
|
flare = "2.0.1"
|
||||||
log4j = "2.24.1"
|
log4j = "2.24.1"
|
||||||
netty = "4.1.114.Final"
|
netty = "4.1.119.Final"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
indra-publishing = "net.kyori.indra.publishing:2.0.6"
|
indra-publishing = "net.kyori.indra.publishing:2.0.6"
|
||||||
@ -11,14 +11,14 @@ shadow = "io.github.goooler.shadow:8.1.5"
|
|||||||
spotless = "com.diffplug.spotless:6.25.0"
|
spotless = "com.diffplug.spotless:6.25.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
adventure-bom = "net.kyori:adventure-bom:4.17.0"
|
adventure-bom = "net.kyori:adventure-bom:4.19.0"
|
||||||
adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.17.0"
|
adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.19.0"
|
||||||
adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
|
adventure-facet = "net.kyori:adventure-platform-facet:4.3.4"
|
||||||
asm = "org.ow2.asm:asm:9.6"
|
asm = "org.ow2.asm:asm:9.7.1"
|
||||||
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.2"
|
bstats = "org.bstats:bstats-base:3.0.3"
|
||||||
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8"
|
caffeine = "com.github.ben-manes.caffeine:caffeine:3.1.8"
|
||||||
checker-qual = "org.checkerframework:checker-qual:3.42.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"
|
||||||
@ -37,7 +37,7 @@ jline = "org.jline:jline-terminal-jansi:3.27.1"
|
|||||||
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.10.2"
|
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.1.0"
|
kyori-ansi = "net.kyori:ansi:1.1.1"
|
||||||
guava = "com.google.guava:guava:25.1-jre"
|
guava = "com.google.guava:guava:25.1-jre"
|
||||||
gson = "com.google.code.gson:gson:2.10.1"
|
gson = "com.google.code.gson:gson:2.10.1"
|
||||||
guice = "com.google.inject:guice:6.0.0"
|
guice = "com.google.inject:guice:6.0.0"
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
9
gradlew
vendored
9
gradlew
vendored
@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@ -84,7 +86,8 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||||
|
' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@ -200,7 +203,7 @@ fi
|
|||||||
|
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command:
|
# Collect all arguments for the java command:
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
|
|||||||
4
gradlew.bat
vendored
4
gradlew.bat
vendored
@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@ -34,7 +36,7 @@ set APP_HOME=%DIRNAME%
|
|||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 "-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|||||||
@ -26,6 +26,15 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
|
||||||
|
// the IV defeats the entire point - we might as well just initialize it to all zeroes.
|
||||||
|
//
|
||||||
|
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
||||||
|
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
||||||
|
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
||||||
|
// most serious.
|
||||||
|
//
|
||||||
|
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
||||||
CCCryptorRef cryptor = NULL;
|
CCCryptorRef cryptor = NULL;
|
||||||
CCCryptorStatus result = CCCryptorCreateWithMode(encrypt ? kCCEncrypt : kCCDecrypt,
|
CCCryptorStatus result = CCCryptorCreateWithMode(encrypt ? kCCEncrypt : kCCDecrypt,
|
||||||
kCCModeCFB8,
|
kCCModeCFB8,
|
||||||
|
|||||||
@ -32,6 +32,15 @@ Java_com_velocitypowered_natives_encryption_OpenSslCipherImpl_init(JNIEnv *env,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
|
||||||
|
// the IV defeats the entire point - we might as well just initialize it to all zeroes.
|
||||||
|
//
|
||||||
|
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
||||||
|
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
||||||
|
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
||||||
|
// most serious.
|
||||||
|
//
|
||||||
|
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
||||||
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
|
int result = EVP_CipherInit(ctx, EVP_aes_128_cfb8(), (byte*) keyBytes, (byte*) keyBytes,
|
||||||
encrypt);
|
encrypt);
|
||||||
if (result != 1) {
|
if (result != 1) {
|
||||||
|
|||||||
@ -57,7 +57,8 @@ public class JavaVelocityCompressor implements VelocityCompressor {
|
|||||||
inflater.setInput(source.nioBuffer());
|
inflater.setInput(source.nioBuffer());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!inflater.finished() && inflater.getBytesWritten() < uncompressedSize) {
|
final int readable = source.readableBytes();
|
||||||
|
while (!inflater.finished() && inflater.getBytesRead() < readable) {
|
||||||
if (!destination.isWritable()) {
|
if (!destination.isWritable()) {
|
||||||
destination.ensureWritable(ZLIB_BUFFER_SIZE);
|
destination.ensureWritable(ZLIB_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,6 +48,15 @@ public class JavaVelocityCipher implements VelocityCipher {
|
|||||||
|
|
||||||
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
||||||
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
|
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
|
||||||
|
// But, you're saying, *why* are we using the key as the IV? After all, reusing the key as
|
||||||
|
// the IV defeats the entire point - we might as well just initialize it to all zeroes.
|
||||||
|
//
|
||||||
|
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
||||||
|
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
||||||
|
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
||||||
|
// most serious.
|
||||||
|
//
|
||||||
|
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
||||||
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
|
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
|
||||||
new IvParameterSpec(key.getEncoded()));
|
new IvParameterSpec(key.getEncoded()));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,11 @@ tasks {
|
|||||||
exclude("it/unimi/dsi/fastutil/ints/*Int2Short*")
|
exclude("it/unimi/dsi/fastutil/ints/*Int2Short*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/*Int2Reference*")
|
exclude("it/unimi/dsi/fastutil/ints/*Int2Reference*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntAVL*")
|
exclude("it/unimi/dsi/fastutil/ints/IntAVL*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntArray*")
|
exclude("it/unimi/dsi/fastutil/ints/IntArrayF*")
|
||||||
|
exclude("it/unimi/dsi/fastutil/ints/IntArrayI*")
|
||||||
|
exclude("it/unimi/dsi/fastutil/ints/IntArrayL*")
|
||||||
|
exclude("it/unimi/dsi/fastutil/ints/IntArrayP*")
|
||||||
|
exclude("it/unimi/dsi/fastutil/ints/IntArraySet*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/*IntBi*")
|
exclude("it/unimi/dsi/fastutil/ints/*IntBi*")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/Int*Pair")
|
exclude("it/unimi/dsi/fastutil/ints/Int*Pair")
|
||||||
exclude("it/unimi/dsi/fastutil/ints/IntLinked*")
|
exclude("it/unimi/dsi/fastutil/ints/IntLinked*")
|
||||||
|
|||||||
@ -65,7 +65,8 @@ public class Metrics {
|
|||||||
logger::info,
|
logger::info,
|
||||||
config.isLogErrorsEnabled(),
|
config.isLogErrorsEnabled(),
|
||||||
config.isLogSentDataEnabled(),
|
config.isLogSentDataEnabled(),
|
||||||
config.isLogResponseStatusTextEnabled()
|
config.isLogResponseStatusTextEnabled(),
|
||||||
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!config.didExistBefore()) {
|
if (!config.didExistBefore()) {
|
||||||
|
|||||||
@ -236,6 +236,15 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
|
|
||||||
registerTranslations();
|
registerTranslations();
|
||||||
|
|
||||||
|
// Yes, you're reading that correctly. We're generating a 1024-bit RSA keypair. Sounds
|
||||||
|
// dangerous, right? We're well within the realm of factoring such a key...
|
||||||
|
//
|
||||||
|
// You can blame Mojang. For the record, we also don't consider the Minecraft protocol
|
||||||
|
// encryption scheme to be secure, and it has reached the point where any serious cryptographic
|
||||||
|
// protocol needs a refresh. There are multiple obvious weaknesses, and this is far from the
|
||||||
|
// most serious.
|
||||||
|
//
|
||||||
|
// If you are using Minecraft in a security-sensitive application, *I don't know what to say.*
|
||||||
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
||||||
|
|
||||||
cm.logChannelInformation();
|
cm.logChannelInformation();
|
||||||
@ -795,6 +804,11 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
|||||||
return channelRegistrar;
|
return channelRegistrar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShuttingDown() {
|
||||||
|
return shutdownInProgress.get();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getBoundAddress() {
|
public InetSocketAddress getBoundAddress() {
|
||||||
if (configuration == null) {
|
if (configuration == null) {
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.command.builtin;
|
package com.velocitypowered.proxy.command.builtin;
|
||||||
|
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
@ -25,8 +26,9 @@ import com.velocitypowered.api.command.BrigadierCommand;
|
|||||||
import com.velocitypowered.api.command.CommandSource;
|
import com.velocitypowered.api.command.CommandSource;
|
||||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shuts down the proxy.
|
* Shuts down the proxy.
|
||||||
@ -53,11 +55,22 @@ public final class ShutdownCommand {
|
|||||||
StringArgumentType.greedyString())
|
StringArgumentType.greedyString())
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
String reason = context.getArgument("reason", String.class);
|
String reason = context.getArgument("reason", String.class);
|
||||||
server.shutdown(true, MiniMessage.miniMessage().deserialize(
|
Component reasonComponent = null;
|
||||||
MiniMessage.miniMessage().serialize(
|
|
||||||
LegacyComponentSerializer.legacy('&').deserialize(reason)
|
if (reason.startsWith("{") || reason.startsWith("[") || reason.startsWith("\"")) {
|
||||||
)
|
try {
|
||||||
));
|
reasonComponent = GsonComponentSerializer.gson()
|
||||||
|
.deserializeOrNull(reason);
|
||||||
|
} catch (JsonSyntaxException expected) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reasonComponent == null) {
|
||||||
|
reasonComponent = MiniMessage.miniMessage().deserialize(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.shutdown(true, reasonComponent);
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
})
|
})
|
||||||
).build());
|
).build());
|
||||||
|
|||||||
@ -82,7 +82,7 @@ public final class VelocityCommand {
|
|||||||
.executes(new Heap())
|
.executes(new Heap())
|
||||||
.build();
|
.build();
|
||||||
final LiteralCommandNode<CommandSource> info = BrigadierCommand.literalArgumentBuilder("info")
|
final LiteralCommandNode<CommandSource> info = BrigadierCommand.literalArgumentBuilder("info")
|
||||||
.requires(source -> source.getPermissionValue("velocity.command.info") != Tristate.FALSE)
|
.requires(source -> source.getPermissionValue("velocity.command.info") == Tristate.TRUE)
|
||||||
.executes(new Info(server))
|
.executes(new Info(server))
|
||||||
.build();
|
.build();
|
||||||
final LiteralCommandNode<CommandSource> plugins = BrigadierCommand
|
final LiteralCommandNode<CommandSource> plugins = BrigadierCommand
|
||||||
|
|||||||
@ -407,6 +407,10 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return forceKeyAuthentication;
|
return forceKeyAuthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnableReusePort() {
|
||||||
|
return advanced.isEnableReusePort();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return MoreObjects.toStringHelper(this)
|
return MoreObjects.toStringHelper(this)
|
||||||
@ -716,6 +720,8 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
private boolean logPlayerConnections = true;
|
private boolean logPlayerConnections = true;
|
||||||
@Expose
|
@Expose
|
||||||
private boolean acceptTransfers = false;
|
private boolean acceptTransfers = false;
|
||||||
|
@Expose
|
||||||
|
private boolean enableReusePort = false;
|
||||||
|
|
||||||
private Advanced() {
|
private Advanced() {
|
||||||
}
|
}
|
||||||
@ -741,6 +747,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
this.logCommandExecutions = config.getOrElse("log-command-executions", false);
|
this.logCommandExecutions = config.getOrElse("log-command-executions", false);
|
||||||
this.logPlayerConnections = config.getOrElse("log-player-connections", true);
|
this.logPlayerConnections = config.getOrElse("log-player-connections", true);
|
||||||
this.acceptTransfers = config.getOrElse("accepts-transfers", false);
|
this.acceptTransfers = config.getOrElse("accepts-transfers", false);
|
||||||
|
this.enableReusePort = config.getOrElse("enable-reuse-port", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -804,6 +811,10 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
return this.acceptTransfers;
|
return this.acceptTransfers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEnableReusePort() {
|
||||||
|
return enableReusePort;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Advanced{"
|
return "Advanced{"
|
||||||
@ -821,6 +832,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
|||||||
+ ", logCommandExecutions=" + logCommandExecutions
|
+ ", logCommandExecutions=" + logCommandExecutions
|
||||||
+ ", logPlayerConnections=" + logPlayerConnections
|
+ ", logPlayerConnections=" + logPlayerConnections
|
||||||
+ ", acceptTransfers=" + acceptTransfers
|
+ ", acceptTransfers=" + acceptTransfers
|
||||||
|
+ ", enableReusePort=" + enableReusePort
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -301,6 +301,21 @@ public class BungeeCordMessageResponder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processGetPlayerServer(ByteBufDataInput in) {
|
||||||
|
proxy.getPlayer(in.readUTF()).ifPresent(player -> {
|
||||||
|
player.getCurrentServer().ifPresent(server -> {
|
||||||
|
ByteBuf buf = Unpooled.buffer();
|
||||||
|
ByteBufDataOutput out = new ByteBufDataOutput(buf);
|
||||||
|
|
||||||
|
out.writeUTF("GetPlayerServer");
|
||||||
|
out.writeUTF(player.getUsername());
|
||||||
|
out.writeUTF(server.getServerInfo().getName());
|
||||||
|
|
||||||
|
sendResponseOnConnection(buf);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static String getBungeeCordChannel(ProtocolVersion version) {
|
static String getBungeeCordChannel(ProtocolVersion version) {
|
||||||
return version.noLessThan(ProtocolVersion.MINECRAFT_1_13) ? MODERN_CHANNEL.getId()
|
return version.noLessThan(ProtocolVersion.MINECRAFT_1_13) ? MODERN_CHANNEL.getId()
|
||||||
: LEGACY_CHANNEL.getId();
|
: LEGACY_CHANNEL.getId();
|
||||||
@ -331,6 +346,9 @@ public class BungeeCordMessageResponder {
|
|||||||
ByteBufDataInput in = new ByteBufDataInput(message.content());
|
ByteBufDataInput in = new ByteBufDataInput(message.content());
|
||||||
String subChannel = in.readUTF();
|
String subChannel = in.readUTF();
|
||||||
switch (subChannel) {
|
switch (subChannel) {
|
||||||
|
case "GetPlayerServer":
|
||||||
|
this.processGetPlayerServer(in);
|
||||||
|
break;
|
||||||
case "ForwardToPlayer":
|
case "ForwardToPlayer":
|
||||||
this.processForwardToPlayer(in);
|
this.processForwardToPlayer(in);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.backend;
|
package com.velocitypowered.proxy.connection.backend;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||||
import com.velocitypowered.api.event.connection.PreTransferEvent;
|
import com.velocitypowered.api.event.connection.PreTransferEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
import com.velocitypowered.api.event.player.CookieRequestEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
import com.velocitypowered.api.event.player.CookieStoreEvent;
|
||||||
@ -24,6 +25,7 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
|
|||||||
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
|
import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||||
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.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
@ -54,6 +56,8 @@ import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
|
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
|
||||||
import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket;
|
import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@ -261,7 +265,29 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(),
|
PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(),
|
||||||
serverConn.getPlayer().getProtocolVersion()));
|
serverConn.getPlayer().getProtocolVersion()));
|
||||||
} else {
|
} else {
|
||||||
serverConn.getPlayer().getConnection().write(packet.retain());
|
byte[] bytes = ByteBufUtil.getBytes(packet.content());
|
||||||
|
ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
serverConn.getPlayer().getConnection().write(packet.retain());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling this stuff async means that we should probably pause
|
||||||
|
// the connection while we toss this off into another pool
|
||||||
|
this.serverConn.getConnection().setAutoReading(false);
|
||||||
|
this.server.getEventManager()
|
||||||
|
.fire(new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, bytes))
|
||||||
|
.thenAcceptAsync(pme -> {
|
||||||
|
if (pme.getResult().isAllowed() && !serverConn.getPlayer().getConnection().isClosed()) {
|
||||||
|
serverConn.getPlayer().getConnection().write(new PluginMessagePacket(
|
||||||
|
pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes)));
|
||||||
|
}
|
||||||
|
this.serverConn.getConnection().setAutoReading(true);
|
||||||
|
}, serverConn.ensureConnected().eventLoop()).exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling plugin message {}", packet, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,7 +97,7 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
// Initiate a regular connection and move over to it.
|
// Initiate a regular connection and move over to it.
|
||||||
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
|
ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
|
||||||
mcConnection, inbound.getVirtualHost().orElse(null), inbound.getRawVirtualHost().orElse(null), onlineMode,
|
mcConnection, inbound.getVirtualHost().orElse(null), inbound.getRawVirtualHost().orElse(null), onlineMode,
|
||||||
inbound.getIdentifiedKey());
|
inbound.getHandshakeIntent(), inbound.getIdentifiedKey());
|
||||||
this.connectedPlayer = player;
|
this.connectedPlayer = player;
|
||||||
if (!server.canRegisterConnection(player)) {
|
if (!server.canRegisterConnection(player)) {
|
||||||
player.disconnect0(
|
player.disconnect0(
|
||||||
@ -106,7 +106,9 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
|||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("{} has connected", player);
|
if (server.getConfiguration().isLogPlayerConnections()) {
|
||||||
|
logger.info("{} has connected", player);
|
||||||
|
}
|
||||||
|
|
||||||
return server.getEventManager()
|
return server.getEventManager()
|
||||||
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
|
.fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
|
||||||
|
|||||||
@ -17,14 +17,17 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||||
import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
import com.velocitypowered.api.event.player.CookieReceiveEvent;
|
||||||
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
|
||||||
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||||
import com.velocitypowered.proxy.VelocityServer;
|
import com.velocitypowered.proxy.VelocityServer;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||||
|
import com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder;
|
||||||
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
|
||||||
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
|
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
|
||||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||||
@ -41,6 +44,7 @@ import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket;
|
|||||||
import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket;
|
import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket;
|
||||||
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
import com.velocitypowered.proxy.protocol.util.PluginMessageUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -123,8 +127,32 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
|
|||||||
brandChannel = packet.getChannel();
|
brandChannel = packet.getChannel();
|
||||||
// Client sends `minecraft:brand` packet immediately after Login,
|
// Client sends `minecraft:brand` packet immediately after Login,
|
||||||
// but at this time the backend server may not be ready
|
// but at this time the backend server may not be ready
|
||||||
|
} else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) {
|
||||||
|
return true;
|
||||||
} else if (serverConn != null) {
|
} else if (serverConn != null) {
|
||||||
serverConn.ensureConnected().write(packet.retain());
|
byte[] bytes = ByteBufUtil.getBytes(packet.content());
|
||||||
|
ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||||
|
|
||||||
|
if (id == null) {
|
||||||
|
serverConn.ensureConnected().write(packet.retain());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling this stuff async means that we should probably pause
|
||||||
|
// the connection while we toss this off into another pool
|
||||||
|
serverConn.getPlayer().getConnection().setAutoReading(false);
|
||||||
|
this.server.getEventManager()
|
||||||
|
.fire(new PluginMessageEvent(serverConn.getPlayer(), serverConn, id, bytes))
|
||||||
|
.thenAcceptAsync(pme -> {
|
||||||
|
if (pme.getResult().isAllowed() && serverConn.getConnection() != null) {
|
||||||
|
serverConn.ensureConnected().write(new PluginMessagePacket(
|
||||||
|
pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes)));
|
||||||
|
}
|
||||||
|
serverConn.getPlayer().getConnection().setAutoReading(true);
|
||||||
|
}, player.getConnection().eventLoop()).exceptionally((ex) -> {
|
||||||
|
logger.error("Exception while handling plugin message packet for {}", player, ex);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -170,6 +170,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deactivated() {
|
public void deactivated() {
|
||||||
|
player.discardChatQueue();
|
||||||
for (PluginMessagePacket message : loginPluginMessages) {
|
for (PluginMessagePacket message : loginPluginMessages) {
|
||||||
ReferenceCountUtil.release(message);
|
ReferenceCountUtil.release(message);
|
||||||
}
|
}
|
||||||
@ -426,6 +427,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handle(JoinGamePacket packet) {
|
||||||
|
// Forward the packet as normal, but discard any chat state we have queued - the client will do this too
|
||||||
|
player.discardChatQueue();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleGeneric(MinecraftPacket packet) {
|
public void handleGeneric(MinecraftPacket packet) {
|
||||||
VelocityServerConnection serverConnection = player.getConnectedServer();
|
VelocityServerConnection serverConnection = player.getConnectedServer();
|
||||||
|
|||||||
@ -38,6 +38,7 @@ import com.velocitypowered.api.event.player.PlayerModInfoEvent;
|
|||||||
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
|
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
|
||||||
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
|
||||||
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
|
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
|
||||||
|
import com.velocitypowered.api.network.HandshakeIntent;
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.permission.PermissionFunction;
|
import com.velocitypowered.api.permission.PermissionFunction;
|
||||||
@ -156,6 +157,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
private final MinecraftConnection connection;
|
private final MinecraftConnection connection;
|
||||||
private final @Nullable InetSocketAddress virtualHost;
|
private final @Nullable InetSocketAddress virtualHost;
|
||||||
private final @Nullable String rawVirtualHost;
|
private final @Nullable String rawVirtualHost;
|
||||||
|
private final HandshakeIntent handshakeIntent;
|
||||||
private GameProfile profile;
|
private GameProfile profile;
|
||||||
private PermissionFunction permissionFunction;
|
private PermissionFunction permissionFunction;
|
||||||
private int tryIndex = 0;
|
private int tryIndex = 0;
|
||||||
@ -188,17 +190,18 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
private @Nullable Locale effectiveLocale;
|
private @Nullable Locale effectiveLocale;
|
||||||
private final @Nullable IdentifiedKey playerKey;
|
private final @Nullable IdentifiedKey playerKey;
|
||||||
private @Nullable ClientSettingsPacket clientSettingsPacket;
|
private @Nullable ClientSettingsPacket clientSettingsPacket;
|
||||||
private final ChatQueue chatQueue;
|
private volatile ChatQueue chatQueue;
|
||||||
private final ChatBuilderFactory chatBuilderFactory;
|
private final ChatBuilderFactory chatBuilderFactory;
|
||||||
|
|
||||||
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
|
ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection,
|
||||||
@Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode,
|
@Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode,
|
||||||
@Nullable IdentifiedKey playerKey) {
|
HandshakeIntent handshakeIntent, @Nullable IdentifiedKey playerKey) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.virtualHost = virtualHost;
|
this.virtualHost = virtualHost;
|
||||||
this.rawVirtualHost = rawVirtualHost;
|
this.rawVirtualHost = rawVirtualHost;
|
||||||
|
this.handshakeIntent = handshakeIntent;
|
||||||
this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED;
|
this.permissionFunction = PermissionFunction.ALWAYS_UNDEFINED;
|
||||||
this.connectionPhase = connection.getType().getInitialClientPhase();
|
this.connectionPhase = connection.getType().getInitialClientPhase();
|
||||||
this.onlineMode = onlineMode;
|
this.onlineMode = onlineMode;
|
||||||
@ -233,6 +236,17 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
return chatQueue;
|
return chatQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discards any messages still being processed by the {@link ChatQueue}, and creates a fresh state for future packets.
|
||||||
|
* This should be used on server switches, or whenever the client resets its own 'last seen' state.
|
||||||
|
*/
|
||||||
|
public void discardChatQueue() {
|
||||||
|
// No need for atomic swap, should only be called from event loop
|
||||||
|
final ChatQueue oldChatQueue = chatQueue;
|
||||||
|
chatQueue = new ChatQueue(this);
|
||||||
|
oldChatQueue.close();
|
||||||
|
}
|
||||||
|
|
||||||
public BundleDelimiterHandler getBundleHandler() {
|
public BundleDelimiterHandler getBundleHandler() {
|
||||||
return this.bundleHandler;
|
return this.bundleHandler;
|
||||||
}
|
}
|
||||||
@ -1087,8 +1101,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
throw new IllegalStateException("Can only send server links in CONFIGURATION or PLAY protocol");
|
throw new IllegalStateException("Can only send server links in CONFIGURATION or PLAY protocol");
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.write(new ClientboundServerLinksPacket(List.copyOf(links).stream()
|
connection.write(new ClientboundServerLinksPacket(links.stream()
|
||||||
.map(l -> new ClientboundServerLinksPacket.ServerLink(l, getProtocolVersion())).toList()));
|
.map(l -> new ClientboundServerLinksPacket.ServerLink(
|
||||||
|
l.getBuiltInType().map(Enum::ordinal).orElse(-1),
|
||||||
|
l.getCustomLabel()
|
||||||
|
.map(c -> new ComponentHolder(getProtocolVersion(), translateMessage(c)))
|
||||||
|
.orElse(null),
|
||||||
|
l.getUrl().toString()))
|
||||||
|
.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1284,6 +1304,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
public void switchToConfigState() {
|
public void switchToConfigState() {
|
||||||
server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
|
server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
|
||||||
.completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
|
.completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
|
||||||
|
// if the connection was closed earlier, there is a risk that the player is no longer connected
|
||||||
|
if (!connection.getChannel().isActive()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (bundleHandler.isInBundleSession()) {
|
if (bundleHandler.isInBundleSession()) {
|
||||||
bundleHandler.toggleBundleSession();
|
bundleHandler.toggleBundleSession();
|
||||||
connection.write(BundleDelimiterPacket.INSTANCE);
|
connection.write(BundleDelimiterPacket.INSTANCE);
|
||||||
@ -1335,6 +1360,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
|||||||
return connection.getState().toProtocolState();
|
return connection.getState().toProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandshakeIntent getHandshakeIntent() {
|
||||||
|
return handshakeIntent;
|
||||||
|
}
|
||||||
|
|
||||||
private final class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
|
private final class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder {
|
||||||
|
|
||||||
private final RegisteredServer toConnect;
|
private final RegisteredServer toConnect;
|
||||||
|
|||||||
@ -277,5 +277,10 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
|
|||||||
public ProtocolState getProtocolState() {
|
public ProtocolState getProtocolState() {
|
||||||
return connection.getState().toProtocolState();
|
return connection.getState().toProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandshakeIntent getHandshakeIntent() {
|
||||||
|
return HandshakeIntent.STATUS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.HandshakeIntent;
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.InboundConnection;
|
import com.velocitypowered.api.proxy.InboundConnection;
|
||||||
@ -98,6 +99,11 @@ public final class InitialInboundConnection implements VelocityInboundConnection
|
|||||||
return connection.getState().toProtocolState();
|
return connection.getState().toProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandshakeIntent getHandshakeIntent() {
|
||||||
|
return handshake.getIntent();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnects the connection from the server.
|
* Disconnects the connection from the server.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.connection.client;
|
package com.velocitypowered.proxy.connection.client;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.HandshakeIntent;
|
||||||
import com.velocitypowered.api.network.ProtocolState;
|
import com.velocitypowered.api.network.ProtocolState;
|
||||||
import com.velocitypowered.api.network.ProtocolVersion;
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.proxy.LoginPhaseConnection;
|
import com.velocitypowered.api.proxy.LoginPhaseConnection;
|
||||||
@ -177,4 +178,9 @@ public class LoginInboundConnection implements LoginPhaseConnection, KeyIdentifi
|
|||||||
public ProtocolState getProtocolState() {
|
public ProtocolState getProtocolState() {
|
||||||
return delegate.getProtocolState();
|
return delegate.getProtocolState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandshakeIntent getHandshakeIntent() {
|
||||||
|
return delegate.getHandshakeIntent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,6 +64,7 @@ class LegacyForgeUtil {
|
|||||||
if (discriminator == MOD_LIST_DISCRIMINATOR) {
|
if (discriminator == MOD_LIST_DISCRIMINATOR) {
|
||||||
ImmutableList.Builder<ModInfo.Mod> mods = ImmutableList.builder();
|
ImmutableList.Builder<ModInfo.Mod> mods = ImmutableList.builder();
|
||||||
int modCount = ProtocolUtils.readVarInt(contents);
|
int modCount = ProtocolUtils.readVarInt(contents);
|
||||||
|
Preconditions.checkArgument(modCount < 1024, "Oversized mods list");
|
||||||
|
|
||||||
for (int index = 0; index < modCount; index++) {
|
for (int index = 0; index < modCount; index++) {
|
||||||
String id = ProtocolUtils.readString(contents);
|
String id = ProtocolUtils.readString(contents);
|
||||||
|
|||||||
@ -63,7 +63,7 @@ public class ServerListPingHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<ServerPing> attemptPingPassthrough(VelocityInboundConnection connection,
|
private CompletableFuture<ServerPing> attemptPingPassthrough(VelocityInboundConnection connection,
|
||||||
PingPassthroughMode mode, List<String> servers, ProtocolVersion responseProtocolVersion) {
|
PingPassthroughMode mode, List<String> servers, ProtocolVersion responseProtocolVersion, String virtualHostStr) {
|
||||||
ServerPing fallback = constructLocalPing(connection.getProtocolVersion());
|
ServerPing fallback = constructLocalPing(connection.getProtocolVersion());
|
||||||
List<CompletableFuture<ServerPing>> pings = new ArrayList<>();
|
List<CompletableFuture<ServerPing>> pings = new ArrayList<>();
|
||||||
for (String s : servers) {
|
for (String s : servers) {
|
||||||
@ -73,7 +73,7 @@ public class ServerListPingHandler {
|
|||||||
}
|
}
|
||||||
VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs.get();
|
VelocityRegisteredServer vrs = (VelocityRegisteredServer) rs.get();
|
||||||
pings.add(vrs.ping(connection.getConnection().eventLoop(), PingOptions.builder()
|
pings.add(vrs.ping(connection.getConnection().eventLoop(), PingOptions.builder()
|
||||||
.version(responseProtocolVersion).build()));
|
.version(responseProtocolVersion).virtualHost(virtualHostStr).build()));
|
||||||
}
|
}
|
||||||
if (pings.isEmpty()) {
|
if (pings.isEmpty()) {
|
||||||
return CompletableFuture.completedFuture(fallback);
|
return CompletableFuture.completedFuture(fallback);
|
||||||
@ -155,7 +155,7 @@ public class ServerListPingHandler {
|
|||||||
.orElse("");
|
.orElse("");
|
||||||
List<String> serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(
|
List<String> serversToTry = server.getConfiguration().getForcedHosts().getOrDefault(
|
||||||
virtualHostStr, server.getConfiguration().getAttemptConnectionOrder());
|
virtualHostStr, server.getConfiguration().getAttemptConnectionOrder());
|
||||||
return attemptPingPassthrough(connection, passthroughMode, serversToTry, shownVersion);
|
return attemptPingPassthrough(connection, passthroughMode, serversToTry, shownVersion, virtualHostStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,8 +23,6 @@ import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
|||||||
import it.unimi.dsi.fastutil.Pair;
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
@ -111,42 +109,6 @@ public enum EncryptionUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a signature for input data.
|
|
||||||
*
|
|
||||||
* @param algorithm the signature algorithm
|
|
||||||
* @param base the private key to sign with
|
|
||||||
* @param toSign the byte array(s) of data to sign
|
|
||||||
* @return the generated signature
|
|
||||||
*/
|
|
||||||
public static byte[] generateSignature(String algorithm, PrivateKey base, byte[]... toSign) {
|
|
||||||
Preconditions.checkArgument(toSign.length > 0);
|
|
||||||
try {
|
|
||||||
Signature construct = Signature.getInstance(algorithm);
|
|
||||||
construct.initSign(base);
|
|
||||||
for (byte[] bytes : toSign) {
|
|
||||||
construct.update(bytes);
|
|
||||||
}
|
|
||||||
return construct.sign();
|
|
||||||
} catch (GeneralSecurityException e) {
|
|
||||||
throw new IllegalArgumentException("Invalid signature parameters");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encodes a long array as Big-endian byte array.
|
|
||||||
*
|
|
||||||
* @param bits the long (array) of numbers to encode
|
|
||||||
* @return the encoded bytes
|
|
||||||
*/
|
|
||||||
public static byte[] longToBigEndianByteArray(long... bits) {
|
|
||||||
ByteBuffer ret = ByteBuffer.allocate(8 * bits.length).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
for (long put : bits) {
|
|
||||||
ret.putLong(put);
|
|
||||||
}
|
|
||||||
return ret.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String encodeUrlEncoded(byte[] data) {
|
public static String encodeUrlEncoded(byte[] data) {
|
||||||
return MIME_SPECIAL_ENCODER.encodeToString(data);
|
return MIME_SPECIAL_ENCODER.encodeToString(data);
|
||||||
}
|
}
|
||||||
@ -155,22 +117,6 @@ public enum EncryptionUtils {
|
|||||||
return Base64.getMimeDecoder().decode(toParse);
|
return Base64.getMimeDecoder().decode(toParse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a cer-encoded RSA key into its key bytes.
|
|
||||||
*
|
|
||||||
* @param toParse the cer-encoded key String
|
|
||||||
* @param descriptors the type of key
|
|
||||||
* @return the parsed key bytes
|
|
||||||
*/
|
|
||||||
public static byte[] parsePemEncoded(String toParse, Pair<String, String> descriptors) {
|
|
||||||
int startIdx = toParse.indexOf(descriptors.first());
|
|
||||||
Preconditions.checkArgument(startIdx >= 0);
|
|
||||||
int firstLen = descriptors.first().length();
|
|
||||||
int endIdx = toParse.indexOf(descriptors.second(), firstLen + startIdx) + 1;
|
|
||||||
Preconditions.checkArgument(endIdx > 0);
|
|
||||||
return decodeUrlEncoded(toParse.substring(startIdx + firstLen, endIdx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodes an RSA key as String cer format.
|
* Encodes an RSA key as String cer format.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -350,8 +350,9 @@ public class VelocityEventManager implements EventManager {
|
|||||||
asyncType = AsyncType.ALWAYS;
|
asyncType = AsyncType.ALWAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The default value of 0 will fall back to PostOrder, the default PostOrder (NORMAL) is also 0
|
||||||
final short order;
|
final short order;
|
||||||
if (subscribe.order() == PostOrder.CUSTOM) {
|
if (subscribe.priority() != 0) {
|
||||||
order = subscribe.priority();
|
order = subscribe.priority();
|
||||||
} else {
|
} else {
|
||||||
order = (short) POST_ORDER_MAP.get(subscribe.order());
|
order = (short) POST_ORDER_MAP.get(subscribe.order());
|
||||||
|
|||||||
@ -18,6 +18,8 @@
|
|||||||
package com.velocitypowered.proxy.network;
|
package com.velocitypowered.proxy.network;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.HashMultimap;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
||||||
import com.velocitypowered.api.event.proxy.ListenerCloseEvent;
|
import com.velocitypowered.api.event.proxy.ListenerCloseEvent;
|
||||||
import com.velocitypowered.api.network.ListenerType;
|
import com.velocitypowered.api.network.ListenerType;
|
||||||
@ -28,14 +30,17 @@ import com.velocitypowered.proxy.protocol.netty.GameSpyQueryHandler;
|
|||||||
import io.netty.bootstrap.Bootstrap;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.WriteBufferWaterMark;
|
import io.netty.channel.WriteBufferWaterMark;
|
||||||
|
import io.netty.channel.unix.UnixChannelOption;
|
||||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||||
|
import io.netty.util.concurrent.MultithreadEventExecutorGroup;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.util.HashMap;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@ -49,7 +54,7 @@ public final class ConnectionManager {
|
|||||||
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
|
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
|
||||||
1 << 21);
|
1 << 21);
|
||||||
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
|
private static final Logger LOGGER = LogManager.getLogger(ConnectionManager.class);
|
||||||
private final Map<InetSocketAddress, Endpoint> endpoints = new HashMap<>();
|
private final Multimap<InetSocketAddress, Endpoint> endpoints = HashMultimap.create();
|
||||||
private final TransportType transportType;
|
private final TransportType transportType;
|
||||||
private final EventLoopGroup bossGroup;
|
private final EventLoopGroup bossGroup;
|
||||||
private final EventLoopGroup workerGroup;
|
private final EventLoopGroup workerGroup;
|
||||||
@ -93,7 +98,6 @@ public final class ConnectionManager {
|
|||||||
public void bind(final InetSocketAddress address) {
|
public void bind(final InetSocketAddress address) {
|
||||||
final ServerBootstrap bootstrap = new ServerBootstrap()
|
final ServerBootstrap bootstrap = new ServerBootstrap()
|
||||||
.channelFactory(this.transportType.serverSocketChannelFactory)
|
.channelFactory(this.transportType.serverSocketChannelFactory)
|
||||||
.group(this.bossGroup, this.workerGroup)
|
|
||||||
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
|
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, SERVER_WRITE_MARK)
|
||||||
.childHandler(this.serverChannelInitializer.get())
|
.childHandler(this.serverChannelInitializer.get())
|
||||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
.childOption(ChannelOption.TCP_NODELAY, true)
|
||||||
@ -104,26 +108,50 @@ public final class ConnectionManager {
|
|||||||
bootstrap.option(ChannelOption.TCP_FASTOPEN, 3);
|
bootstrap.option(ChannelOption.TCP_FASTOPEN, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
bootstrap.bind()
|
if (server.getConfiguration().isEnableReusePort()) {
|
||||||
.addListener((ChannelFutureListener) future -> {
|
// We don't need a boss group, since each worker will bind to the socket
|
||||||
final Channel channel = future.channel();
|
bootstrap.option(UnixChannelOption.SO_REUSEPORT, true)
|
||||||
if (future.isSuccess()) {
|
.group(this.workerGroup);
|
||||||
this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT));
|
} else {
|
||||||
|
bootstrap.group(this.bossGroup, this.workerGroup);
|
||||||
|
}
|
||||||
|
|
||||||
// Warn people with console access that HAProxy is in use, see PR: #1436
|
final int binds = server.getConfiguration().isEnableReusePort()
|
||||||
if (this.server.getConfiguration().isProxyProtocol()) {
|
? ((MultithreadEventExecutorGroup) this.workerGroup).executorCount() : 1;
|
||||||
LOGGER.warn("Using HAProxy and listening on {}, please ensure this listener is adequately firewalled.", channel.localAddress());
|
|
||||||
|
for (int bind = 0; bind < binds; bind++) {
|
||||||
|
// Wait for each bind to open. If we encounter any errors, don't try to bind again.
|
||||||
|
int finalBind = bind;
|
||||||
|
ChannelFuture f = bootstrap.bind()
|
||||||
|
.addListener((ChannelFutureListener) future -> {
|
||||||
|
final Channel channel = future.channel();
|
||||||
|
if (future.isSuccess()) {
|
||||||
|
this.endpoints.put(address, new Endpoint(channel, ListenerType.MINECRAFT));
|
||||||
|
|
||||||
|
LOGGER.info("Listening on {}", channel.localAddress());
|
||||||
|
|
||||||
|
if (finalBind == 0) {
|
||||||
|
// Warn people with console access that HAProxy is in use, see PR: #1436
|
||||||
|
if (this.server.getConfiguration().isProxyProtocol()) {
|
||||||
|
LOGGER.warn(
|
||||||
|
"Using HAProxy and listening on {}, please ensure this listener is adequately firewalled.",
|
||||||
|
channel.localAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire the proxy bound event after the socket is bound
|
||||||
|
server.getEventManager().fireAndForget(
|
||||||
|
new ListenerBoundEvent(address, ListenerType.MINECRAFT));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGGER.error("Can't bind to {}", address, future.cause());
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
f.syncUninterruptibly();
|
||||||
|
|
||||||
LOGGER.info("Listening on {}", channel.localAddress());
|
if (!f.isSuccess()) {
|
||||||
|
break;
|
||||||
// Fire the proxy bound event after the socket is bound
|
}
|
||||||
server.getEventManager().fireAndForget(
|
}
|
||||||
new ListenerBoundEvent(address, ListenerType.MINECRAFT));
|
|
||||||
} else {
|
|
||||||
LOGGER.error("Can't bind to {}", address, future.cause());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,17 +209,20 @@ public final class ConnectionManager {
|
|||||||
* @param oldBind the endpoint to close
|
* @param oldBind the endpoint to close
|
||||||
*/
|
*/
|
||||||
public void close(InetSocketAddress oldBind) {
|
public void close(InetSocketAddress oldBind) {
|
||||||
Endpoint endpoint = endpoints.remove(oldBind);
|
Collection<Endpoint> endpoints = this.endpoints.removeAll(oldBind);
|
||||||
|
Preconditions.checkState(!endpoints.isEmpty(), "Endpoint was not registered");
|
||||||
|
|
||||||
|
ListenerType type = endpoints.iterator().next().getType();
|
||||||
|
|
||||||
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
||||||
// should have a chance to be notified before the server stops accepting connections.
|
// should have a chance to be notified before the server stops accepting connections.
|
||||||
server.getEventManager().fire(new ListenerCloseEvent(oldBind, endpoint.getType())).join();
|
server.getEventManager().fire(new ListenerCloseEvent(oldBind, type)).join();
|
||||||
|
|
||||||
Channel serverChannel = endpoint.getChannel();
|
for (Endpoint endpoint : endpoints) {
|
||||||
|
Channel serverChannel = endpoint.getChannel();
|
||||||
Preconditions.checkState(serverChannel != null, "Endpoint %s not registered", oldBind);
|
LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
|
||||||
LOGGER.info("Closing endpoint {}", serverChannel.localAddress());
|
serverChannel.close().syncUninterruptibly();
|
||||||
serverChannel.close().syncUninterruptibly();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,24 +231,28 @@ public final class ConnectionManager {
|
|||||||
* @param interrupt should closing forward interruptions
|
* @param interrupt should closing forward interruptions
|
||||||
*/
|
*/
|
||||||
public void closeEndpoints(boolean interrupt) {
|
public void closeEndpoints(boolean interrupt) {
|
||||||
for (final Map.Entry<InetSocketAddress, Endpoint> entry : this.endpoints.entrySet()) {
|
for (final Map.Entry<InetSocketAddress, Collection<Endpoint>> entry : this.endpoints.asMap()
|
||||||
|
.entrySet()) {
|
||||||
final InetSocketAddress address = entry.getKey();
|
final InetSocketAddress address = entry.getKey();
|
||||||
final Endpoint endpoint = entry.getValue();
|
final Collection<Endpoint> endpoints = entry.getValue();
|
||||||
|
ListenerType type = endpoints.iterator().next().getType();
|
||||||
|
|
||||||
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
// Fire proxy close event to notify plugins of socket close. We block since plugins
|
||||||
// should have a chance to be notified before the server stops accepting connections.
|
// should have a chance to be notified before the server stops accepting connections.
|
||||||
server.getEventManager().fire(new ListenerCloseEvent(address, endpoint.getType())).join();
|
server.getEventManager().fire(new ListenerCloseEvent(address, type)).join();
|
||||||
|
|
||||||
LOGGER.info("Closing endpoint {}", address);
|
for (Endpoint endpoint : endpoints) {
|
||||||
if (interrupt) {
|
LOGGER.info("Closing endpoint {}", address);
|
||||||
try {
|
if (interrupt) {
|
||||||
endpoint.getChannel().close().sync();
|
try {
|
||||||
} catch (final InterruptedException e) {
|
endpoint.getChannel().close().sync();
|
||||||
LOGGER.info("Interrupted whilst closing endpoint", e);
|
} catch (final InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
LOGGER.info("Interrupted whilst closing endpoint", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
endpoint.getChannel().close().syncUninterruptibly();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
endpoint.getChannel().close().syncUninterruptibly();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.endpoints.clear();
|
this.endpoints.clear();
|
||||||
|
|||||||
@ -39,6 +39,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5;
|
|||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4;
|
||||||
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
|
||||||
@ -377,7 +378,8 @@ public enum StateRegistry {
|
|||||||
map(0x0D, MINECRAFT_1_17, false),
|
map(0x0D, MINECRAFT_1_17, false),
|
||||||
map(0x0A, MINECRAFT_1_19, false),
|
map(0x0A, MINECRAFT_1_19, false),
|
||||||
map(0x0B, MINECRAFT_1_19_4, false),
|
map(0x0B, MINECRAFT_1_19_4, false),
|
||||||
map(0x0A, MINECRAFT_1_20_2, false));
|
map(0x0A, MINECRAFT_1_20_2, false),
|
||||||
|
map(0x09, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
LegacyChatPacket.class,
|
LegacyChatPacket.class,
|
||||||
LegacyChatPacket::new,
|
LegacyChatPacket::new,
|
||||||
@ -398,7 +400,8 @@ public enum StateRegistry {
|
|||||||
map(0x0E, MINECRAFT_1_19, false),
|
map(0x0E, MINECRAFT_1_19, false),
|
||||||
map(0x0D, MINECRAFT_1_19_3, false),
|
map(0x0D, MINECRAFT_1_19_3, false),
|
||||||
map(0x0F, MINECRAFT_1_19_4, false),
|
map(0x0F, MINECRAFT_1_19_4, false),
|
||||||
map(0x10, MINECRAFT_1_20_2, false));
|
map(0x10, MINECRAFT_1_20_2, false),
|
||||||
|
map(0x0F, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
AvailableCommandsPacket.class,
|
AvailableCommandsPacket.class,
|
||||||
AvailableCommandsPacket::new,
|
AvailableCommandsPacket::new,
|
||||||
@ -410,10 +413,12 @@ public enum StateRegistry {
|
|||||||
map(0x0F, MINECRAFT_1_19, false),
|
map(0x0F, MINECRAFT_1_19, false),
|
||||||
map(0x0E, MINECRAFT_1_19_3, false),
|
map(0x0E, MINECRAFT_1_19_3, false),
|
||||||
map(0x10, MINECRAFT_1_19_4, false),
|
map(0x10, MINECRAFT_1_19_4, false),
|
||||||
map(0x11, MINECRAFT_1_20_2, false));
|
map(0x11, MINECRAFT_1_20_2, false),
|
||||||
|
map(0x10, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new,
|
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new,
|
||||||
map(0x16, MINECRAFT_1_20_5, false));
|
map(0x16, MINECRAFT_1_20_5, false),
|
||||||
|
map(0x15, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
PluginMessagePacket.class,
|
PluginMessagePacket.class,
|
||||||
PluginMessagePacket::new,
|
PluginMessagePacket::new,
|
||||||
@ -430,7 +435,8 @@ public enum StateRegistry {
|
|||||||
map(0x15, MINECRAFT_1_19_3, false),
|
map(0x15, MINECRAFT_1_19_3, false),
|
||||||
map(0x17, MINECRAFT_1_19_4, false),
|
map(0x17, MINECRAFT_1_19_4, false),
|
||||||
map(0x18, MINECRAFT_1_20_2, false),
|
map(0x18, MINECRAFT_1_20_2, false),
|
||||||
map(0x19, MINECRAFT_1_20_5, false));
|
map(0x19, MINECRAFT_1_20_5, false),
|
||||||
|
map(0x18, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
DisconnectPacket.class,
|
DisconnectPacket.class,
|
||||||
() -> new DisconnectPacket(this),
|
() -> new DisconnectPacket(this),
|
||||||
@ -447,7 +453,8 @@ public enum StateRegistry {
|
|||||||
map(0x17, MINECRAFT_1_19_3, false),
|
map(0x17, MINECRAFT_1_19_3, false),
|
||||||
map(0x1A, MINECRAFT_1_19_4, false),
|
map(0x1A, MINECRAFT_1_19_4, false),
|
||||||
map(0x1B, MINECRAFT_1_20_2, false),
|
map(0x1B, MINECRAFT_1_20_2, false),
|
||||||
map(0x1D, MINECRAFT_1_20_5, false));
|
map(0x1D, MINECRAFT_1_20_5, false),
|
||||||
|
map(0x1C, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
KeepAlivePacket.class,
|
KeepAlivePacket.class,
|
||||||
KeepAlivePacket::new,
|
KeepAlivePacket::new,
|
||||||
@ -465,7 +472,8 @@ public enum StateRegistry {
|
|||||||
map(0x23, MINECRAFT_1_19_4, false),
|
map(0x23, MINECRAFT_1_19_4, false),
|
||||||
map(0x24, MINECRAFT_1_20_2, false),
|
map(0x24, MINECRAFT_1_20_2, false),
|
||||||
map(0x26, MINECRAFT_1_20_5, false),
|
map(0x26, MINECRAFT_1_20_5, false),
|
||||||
map(0x27, MINECRAFT_1_21_2, false));
|
map(0x27, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x26, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
JoinGamePacket.class,
|
JoinGamePacket.class,
|
||||||
JoinGamePacket::new,
|
JoinGamePacket::new,
|
||||||
@ -483,7 +491,8 @@ public enum StateRegistry {
|
|||||||
map(0x28, MINECRAFT_1_19_4, false),
|
map(0x28, MINECRAFT_1_19_4, false),
|
||||||
map(0x29, MINECRAFT_1_20_2, false),
|
map(0x29, MINECRAFT_1_20_2, false),
|
||||||
map(0x2B, MINECRAFT_1_20_5, false),
|
map(0x2B, MINECRAFT_1_20_5, false),
|
||||||
map(0x2C, MINECRAFT_1_21_2, false));
|
map(0x2C, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x2B, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
RespawnPacket.class,
|
RespawnPacket.class,
|
||||||
RespawnPacket::new,
|
RespawnPacket::new,
|
||||||
@ -504,13 +513,15 @@ public enum StateRegistry {
|
|||||||
map(0x43, MINECRAFT_1_20_2, true),
|
map(0x43, MINECRAFT_1_20_2, true),
|
||||||
map(0x45, MINECRAFT_1_20_3, true),
|
map(0x45, MINECRAFT_1_20_3, true),
|
||||||
map(0x47, MINECRAFT_1_20_5, true),
|
map(0x47, MINECRAFT_1_20_5, true),
|
||||||
map(0x4C, MINECRAFT_1_21_2, true));
|
map(0x4C, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x4B, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
RemoveResourcePackPacket.class,
|
RemoveResourcePackPacket.class,
|
||||||
RemoveResourcePackPacket::new,
|
RemoveResourcePackPacket::new,
|
||||||
map(0x43, MINECRAFT_1_20_3, false),
|
map(0x43, MINECRAFT_1_20_3, false),
|
||||||
map(0x45, MINECRAFT_1_20_5, false),
|
map(0x45, MINECRAFT_1_20_5, false),
|
||||||
map(0x4A, MINECRAFT_1_21_2, false));
|
map(0x4A, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x49, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ResourcePackRequestPacket.class,
|
ResourcePackRequestPacket.class,
|
||||||
ResourcePackRequestPacket::new,
|
ResourcePackRequestPacket::new,
|
||||||
@ -531,7 +542,8 @@ public enum StateRegistry {
|
|||||||
map(0x42, MINECRAFT_1_20_2, false),
|
map(0x42, MINECRAFT_1_20_2, false),
|
||||||
map(0x44, MINECRAFT_1_20_3, false),
|
map(0x44, MINECRAFT_1_20_3, false),
|
||||||
map(0x46, MINECRAFT_1_20_5, false),
|
map(0x46, MINECRAFT_1_20_5, false),
|
||||||
map(0x4B, MINECRAFT_1_21_2, false));
|
map(0x4B, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x4A, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
HeaderAndFooterPacket.class,
|
HeaderAndFooterPacket.class,
|
||||||
HeaderAndFooterPacket::new,
|
HeaderAndFooterPacket::new,
|
||||||
@ -553,7 +565,8 @@ public enum StateRegistry {
|
|||||||
map(0x68, MINECRAFT_1_20_2, true),
|
map(0x68, MINECRAFT_1_20_2, true),
|
||||||
map(0x6A, MINECRAFT_1_20_3, true),
|
map(0x6A, MINECRAFT_1_20_3, true),
|
||||||
map(0x6D, MINECRAFT_1_20_5, true),
|
map(0x6D, MINECRAFT_1_20_5, true),
|
||||||
map(0x74, MINECRAFT_1_21_2, true));
|
map(0x74, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x73, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
LegacyTitlePacket.class,
|
LegacyTitlePacket.class,
|
||||||
LegacyTitlePacket::new,
|
LegacyTitlePacket::new,
|
||||||
@ -574,7 +587,8 @@ public enum StateRegistry {
|
|||||||
map(0x5F, MINECRAFT_1_20_2, true),
|
map(0x5F, MINECRAFT_1_20_2, true),
|
||||||
map(0x61, MINECRAFT_1_20_3, true),
|
map(0x61, MINECRAFT_1_20_3, true),
|
||||||
map(0x63, MINECRAFT_1_20_5, true),
|
map(0x63, MINECRAFT_1_20_5, true),
|
||||||
map(0x6A, MINECRAFT_1_21_2, true));
|
map(0x6A, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x69, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleTextPacket.class,
|
TitleTextPacket.class,
|
||||||
TitleTextPacket::new,
|
TitleTextPacket::new,
|
||||||
@ -586,7 +600,8 @@ public enum StateRegistry {
|
|||||||
map(0x61, MINECRAFT_1_20_2, true),
|
map(0x61, MINECRAFT_1_20_2, true),
|
||||||
map(0x63, MINECRAFT_1_20_3, true),
|
map(0x63, MINECRAFT_1_20_3, true),
|
||||||
map(0x65, MINECRAFT_1_20_5, true),
|
map(0x65, MINECRAFT_1_20_5, true),
|
||||||
map(0x6C, MINECRAFT_1_21_2, true));
|
map(0x6C, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x6B, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleActionbarPacket.class,
|
TitleActionbarPacket.class,
|
||||||
TitleActionbarPacket::new,
|
TitleActionbarPacket::new,
|
||||||
@ -598,7 +613,8 @@ public enum StateRegistry {
|
|||||||
map(0x48, MINECRAFT_1_20_2, true),
|
map(0x48, MINECRAFT_1_20_2, true),
|
||||||
map(0x4A, MINECRAFT_1_20_3, true),
|
map(0x4A, MINECRAFT_1_20_3, true),
|
||||||
map(0x4C, MINECRAFT_1_20_5, true),
|
map(0x4C, MINECRAFT_1_20_5, true),
|
||||||
map(0x51, MINECRAFT_1_21_2, true));
|
map(0x51, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x50, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleTimesPacket.class,
|
TitleTimesPacket.class,
|
||||||
TitleTimesPacket::new,
|
TitleTimesPacket::new,
|
||||||
@ -610,7 +626,8 @@ public enum StateRegistry {
|
|||||||
map(0x62, MINECRAFT_1_20_2, true),
|
map(0x62, MINECRAFT_1_20_2, true),
|
||||||
map(0x64, MINECRAFT_1_20_3, true),
|
map(0x64, MINECRAFT_1_20_3, true),
|
||||||
map(0x66, MINECRAFT_1_20_5, true),
|
map(0x66, MINECRAFT_1_20_5, true),
|
||||||
map(0x6D, MINECRAFT_1_21_2, true));
|
map(0x6D, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x6C, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
TitleClearPacket.class,
|
TitleClearPacket.class,
|
||||||
TitleClearPacket::new,
|
TitleClearPacket::new,
|
||||||
@ -618,7 +635,8 @@ public enum StateRegistry {
|
|||||||
map(0x0D, MINECRAFT_1_19, true),
|
map(0x0D, MINECRAFT_1_19, true),
|
||||||
map(0x0C, MINECRAFT_1_19_3, true),
|
map(0x0C, MINECRAFT_1_19_3, true),
|
||||||
map(0x0E, MINECRAFT_1_19_4, true),
|
map(0x0E, MINECRAFT_1_19_4, true),
|
||||||
map(0x0F, MINECRAFT_1_20_2, true));
|
map(0x0F, MINECRAFT_1_20_2, true),
|
||||||
|
map(0x0E, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
LegacyPlayerListItemPacket.class,
|
LegacyPlayerListItemPacket.class,
|
||||||
LegacyPlayerListItemPacket::new,
|
LegacyPlayerListItemPacket::new,
|
||||||
@ -638,7 +656,8 @@ public enum StateRegistry {
|
|||||||
map(0x39, MINECRAFT_1_19_4, false),
|
map(0x39, MINECRAFT_1_19_4, false),
|
||||||
map(0x3B, MINECRAFT_1_20_2, false),
|
map(0x3B, MINECRAFT_1_20_2, false),
|
||||||
map(0x3D, MINECRAFT_1_20_5, false),
|
map(0x3D, MINECRAFT_1_20_5, false),
|
||||||
map(0x3F, MINECRAFT_1_21_2, false));
|
map(0x3F, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x3E, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
UpsertPlayerInfoPacket.class,
|
UpsertPlayerInfoPacket.class,
|
||||||
UpsertPlayerInfoPacket::new,
|
UpsertPlayerInfoPacket::new,
|
||||||
@ -646,11 +665,13 @@ public enum StateRegistry {
|
|||||||
map(0x3A, MINECRAFT_1_19_4, false),
|
map(0x3A, MINECRAFT_1_19_4, false),
|
||||||
map(0x3C, MINECRAFT_1_20_2, false),
|
map(0x3C, MINECRAFT_1_20_2, false),
|
||||||
map(0x3E, MINECRAFT_1_20_5, false),
|
map(0x3E, MINECRAFT_1_20_5, false),
|
||||||
map(0x40, MINECRAFT_1_21_2, false));
|
map(0x40, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x3F, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new,
|
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new,
|
||||||
map(0x6B, MINECRAFT_1_20_5, false),
|
map(0x6B, MINECRAFT_1_20_5, false),
|
||||||
map(0x72, MINECRAFT_1_21_2, false));
|
map(0x72, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x71, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
SystemChatPacket.class,
|
SystemChatPacket.class,
|
||||||
SystemChatPacket::new,
|
SystemChatPacket::new,
|
||||||
@ -661,7 +682,8 @@ public enum StateRegistry {
|
|||||||
map(0x67, MINECRAFT_1_20_2, true),
|
map(0x67, MINECRAFT_1_20_2, true),
|
||||||
map(0x69, MINECRAFT_1_20_3, true),
|
map(0x69, MINECRAFT_1_20_3, true),
|
||||||
map(0x6C, MINECRAFT_1_20_5, true),
|
map(0x6C, MINECRAFT_1_20_5, true),
|
||||||
map(0x73, MINECRAFT_1_21_2, true));
|
map(0x73, MINECRAFT_1_21_2, true),
|
||||||
|
map(0x72, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
PlayerChatCompletionPacket.class,
|
PlayerChatCompletionPacket.class,
|
||||||
PlayerChatCompletionPacket::new,
|
PlayerChatCompletionPacket::new,
|
||||||
@ -669,7 +691,8 @@ public enum StateRegistry {
|
|||||||
map(0x14, MINECRAFT_1_19_3, true),
|
map(0x14, MINECRAFT_1_19_3, true),
|
||||||
map(0x16, MINECRAFT_1_19_4, true),
|
map(0x16, MINECRAFT_1_19_4, true),
|
||||||
map(0x17, MINECRAFT_1_20_2, true),
|
map(0x17, MINECRAFT_1_20_2, true),
|
||||||
map(0x18, MINECRAFT_1_20_5, true));
|
map(0x18, MINECRAFT_1_20_5, true),
|
||||||
|
map(0x17, MINECRAFT_1_21_5, true));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
ServerDataPacket.class,
|
ServerDataPacket.class,
|
||||||
ServerDataPacket::new,
|
ServerDataPacket::new,
|
||||||
@ -680,14 +703,16 @@ public enum StateRegistry {
|
|||||||
map(0x47, MINECRAFT_1_20_2, false),
|
map(0x47, MINECRAFT_1_20_2, false),
|
||||||
map(0x49, MINECRAFT_1_20_3, false),
|
map(0x49, MINECRAFT_1_20_3, false),
|
||||||
map(0x4B, MINECRAFT_1_20_5, false),
|
map(0x4B, MINECRAFT_1_20_5, false),
|
||||||
map(0x50, MINECRAFT_1_21_2, false));
|
map(0x50, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x4F, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
StartUpdatePacket.class,
|
StartUpdatePacket.class,
|
||||||
() -> StartUpdatePacket.INSTANCE,
|
() -> StartUpdatePacket.INSTANCE,
|
||||||
map(0x65, MINECRAFT_1_20_2, false),
|
map(0x65, MINECRAFT_1_20_2, false),
|
||||||
map(0x67, MINECRAFT_1_20_3, false),
|
map(0x67, MINECRAFT_1_20_3, false),
|
||||||
map(0x69, MINECRAFT_1_20_5, false),
|
map(0x69, MINECRAFT_1_20_5, false),
|
||||||
map(0x70, MINECRAFT_1_21_2, false));
|
map(0x70, MINECRAFT_1_21_2, false),
|
||||||
|
map(0x6F, MINECRAFT_1_21_5, false));
|
||||||
clientbound.register(
|
clientbound.register(
|
||||||
BundleDelimiterPacket.class,
|
BundleDelimiterPacket.class,
|
||||||
() -> BundleDelimiterPacket.INSTANCE,
|
() -> BundleDelimiterPacket.INSTANCE,
|
||||||
|
|||||||
@ -52,6 +52,9 @@ public class MinecraftCompressDecoder extends MessageToMessageDecoder<ByteBuf> {
|
|||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
|
||||||
int claimedUncompressedSize = ProtocolUtils.readVarInt(in);
|
int claimedUncompressedSize = ProtocolUtils.readVarInt(in);
|
||||||
if (claimedUncompressedSize == 0) {
|
if (claimedUncompressedSize == 0) {
|
||||||
|
int actualUncompressedSize = in.readableBytes();
|
||||||
|
checkFrame(actualUncompressedSize < threshold, "Actual uncompressed size %s is greater than"
|
||||||
|
+ " threshold %s", actualUncompressedSize, threshold);
|
||||||
// This message is not compressed.
|
// This message is not compressed.
|
||||||
out.add(in.retain());
|
out.add(in.retain());
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -58,6 +58,13 @@ public class ArgumentIdentifier {
|
|||||||
this.versionById = ImmutableMap.copyOf(temp);
|
this.versionById = ImmutableMap.copyOf(temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ArgumentIdentifier{" +
|
||||||
|
"identifier='" + identifier + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
public String getIdentifier() {
|
public String getIdentifier() {
|
||||||
return identifier;
|
return identifier;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_3;
|
|||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_4;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_3;
|
||||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5;
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_20_5;
|
||||||
|
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.id;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.id;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.ArgumentIdentifier.mapSet;
|
||||||
import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgumentPropertySerializer.DOUBLE;
|
import static com.velocitypowered.proxy.protocol.packet.brigadier.DoubleArgumentPropertySerializer.DOUBLE;
|
||||||
@ -254,17 +255,20 @@ public class ArgumentPropertyRegistry {
|
|||||||
register(id("minecraft:resource_key", mapSet(MINECRAFT_1_20_5, 46), mapSet(MINECRAFT_1_20_3, 45), mapSet(MINECRAFT_1_19_3, 44)),
|
register(id("minecraft:resource_key", mapSet(MINECRAFT_1_20_5, 46), mapSet(MINECRAFT_1_20_3, 45), mapSet(MINECRAFT_1_19_3, 44)),
|
||||||
RegistryKeyArgumentList.ResourceKey.class,
|
RegistryKeyArgumentList.ResourceKey.class,
|
||||||
RegistryKeyArgumentList.ResourceKey.Serializer.REGISTRY);
|
RegistryKeyArgumentList.ResourceKey.Serializer.REGISTRY);
|
||||||
|
register(id("minecraft:resource_selector", mapSet(MINECRAFT_1_21_5, 47)),
|
||||||
|
RegistryKeyArgumentList.ResourceSelector.class,
|
||||||
|
RegistryKeyArgumentList.ResourceSelector.Serializer.REGISTRY);
|
||||||
|
|
||||||
empty(id("minecraft:template_mirror", mapSet(MINECRAFT_1_20_5, 47), mapSet(MINECRAFT_1_20_3, 46), mapSet(MINECRAFT_1_19, 45))); // 1.19
|
empty(id("minecraft:template_mirror", mapSet(MINECRAFT_1_21_5, 48), mapSet(MINECRAFT_1_20_5, 47), mapSet(MINECRAFT_1_20_3, 46), mapSet(MINECRAFT_1_19, 45))); // 1.19
|
||||||
empty(id("minecraft:template_rotation", mapSet(MINECRAFT_1_20_5, 48), mapSet(MINECRAFT_1_20_3, 47), mapSet(MINECRAFT_1_19, 46))); // 1.19
|
empty(id("minecraft:template_rotation", mapSet(MINECRAFT_1_21_5, 49), mapSet(MINECRAFT_1_20_5, 48), mapSet(MINECRAFT_1_20_3, 47), mapSet(MINECRAFT_1_19, 46))); // 1.19
|
||||||
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_20_3, 49), mapSet(MINECRAFT_1_19_4, 47))); // 1.19.4
|
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_21_5, 50), mapSet(MINECRAFT_1_20_3, 49), mapSet(MINECRAFT_1_19_4, 47))); // 1.19.4
|
||||||
|
|
||||||
empty(id("minecraft:uuid", mapSet(MINECRAFT_1_20_5, 53), mapSet(MINECRAFT_1_20_3, 48), mapSet(MINECRAFT_1_19_4, 48),
|
empty(id("minecraft:uuid", mapSet(MINECRAFT_1_21_5, 54),mapSet(MINECRAFT_1_20_5, 53), mapSet(MINECRAFT_1_20_3, 48), mapSet(MINECRAFT_1_19_4, 48),
|
||||||
mapSet(MINECRAFT_1_19, 47))); // added in 1.16
|
mapSet(MINECRAFT_1_19, 47))); // added in 1.16
|
||||||
|
|
||||||
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_20_5, 50)));
|
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_21_5, 51), mapSet(MINECRAFT_1_20_5, 50)));
|
||||||
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_20_5, 51)));
|
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_21_5, 52), mapSet(MINECRAFT_1_20_5, 51)));
|
||||||
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_20_5, 52)));
|
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_21_5, 53), mapSet(MINECRAFT_1_20_5, 52)));
|
||||||
|
|
||||||
// Crossstitch support
|
// Crossstitch support
|
||||||
register(id("crossstitch:mod_argument", mapSet(MINECRAFT_1_19, -256)), ModArgumentProperty.class, MOD);
|
register(id("crossstitch:mod_argument", mapSet(MINECRAFT_1_19, -256)), ModArgumentProperty.class, MOD);
|
||||||
|
|||||||
@ -67,23 +67,23 @@ public final class RegistryKeyArgumentList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Resource extends RegistryKeyArgument {
|
public static class ResourceSelector extends RegistryKeyArgument {
|
||||||
|
|
||||||
public Resource(String identifier) {
|
public ResourceSelector(String identifier) {
|
||||||
super(identifier);
|
super(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Serializer implements ArgumentPropertySerializer<Resource> {
|
public static class Serializer implements ArgumentPropertySerializer<ResourceSelector> {
|
||||||
|
|
||||||
static final Resource.Serializer REGISTRY = new Resource.Serializer();
|
static final ResourceSelector.Serializer REGISTRY = new ResourceSelector.Serializer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
public ResourceSelector deserialize(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||||
return new Resource(ProtocolUtils.readString(buf));
|
return new ResourceSelector(ProtocolUtils.readString(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(Resource object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
public void serialize(ResourceSelector object, ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||||
ProtocolUtils.writeString(buf, object.getIdentifier());
|
ProtocolUtils.writeString(buf, object.getIdentifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,13 +32,15 @@ import java.util.function.Function;
|
|||||||
* A precisely ordered queue which allows for outside entries into the ordered queue through
|
* A precisely ordered queue which allows for outside entries into the ordered queue through
|
||||||
* piggybacking timestamps.
|
* piggybacking timestamps.
|
||||||
*/
|
*/
|
||||||
public class ChatQueue {
|
public class ChatQueue implements AutoCloseable {
|
||||||
|
|
||||||
private final Object internalLock = new Object();
|
private final Object internalLock = new Object();
|
||||||
private final ConnectedPlayer player;
|
private final ConnectedPlayer player;
|
||||||
private final ChatState chatState = new ChatState();
|
private final ChatState chatState = new ChatState();
|
||||||
private CompletableFuture<Void> head = CompletableFuture.completedFuture(null);
|
private CompletableFuture<Void> head = CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
|
private volatile boolean closed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a {@link ChatQueue} for a specific {@link ConnectedPlayer}.
|
* Instantiates a {@link ChatQueue} for a specific {@link ConnectedPlayer}.
|
||||||
*
|
*
|
||||||
@ -50,8 +52,14 @@ public class ChatQueue {
|
|||||||
|
|
||||||
private void queueTask(Task task) {
|
private void queueTask(Task task) {
|
||||||
synchronized (internalLock) {
|
synchronized (internalLock) {
|
||||||
|
if (closed) {
|
||||||
|
throw new IllegalStateException("ChatQueue has already been closed");
|
||||||
|
}
|
||||||
MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected();
|
MinecraftConnection smc = player.ensureAndGetCurrentServer().ensureConnected();
|
||||||
head = head.thenCompose(v -> {
|
head = head.thenCompose(v -> {
|
||||||
|
if (closed) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return task.update(chatState, smc).exceptionally(ignored -> null);
|
return task.update(chatState, smc).exceptionally(ignored -> null);
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
@ -102,9 +110,9 @@ public class ChatQueue {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends MinecraftPacket> CompletableFuture<Void> writePacket(T packet, MinecraftConnection smc) {
|
private <T extends MinecraftPacket> CompletableFuture<Void> writePacket(T packet, MinecraftConnection smc) {
|
||||||
return CompletableFuture.runAsync(() -> {
|
return CompletableFuture.runAsync(() -> {
|
||||||
if (!smc.isClosed()) {
|
if (!closed && !smc.isClosed()) {
|
||||||
ChannelFuture future = smc.write(packet);
|
ChannelFuture future = smc.write(packet);
|
||||||
if (future != null) {
|
if (future != null) {
|
||||||
future.awaitUninterruptibly();
|
future.awaitUninterruptibly();
|
||||||
@ -113,6 +121,11 @@ public class ChatQueue {
|
|||||||
}, smc.eventLoop());
|
}, smc.eventLoop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
private interface Task {
|
private interface Task {
|
||||||
CompletableFuture<Void> update(ChatState chatState, MinecraftConnection smc);
|
CompletableFuture<Void> update(ChatState chatState, MinecraftConnection smc);
|
||||||
}
|
}
|
||||||
@ -174,7 +187,7 @@ public class ChatQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages createLastSeen() {
|
public LastSeenMessages createLastSeen() {
|
||||||
return new LastSeenMessages(0, lastSeenMessages);
|
return new LastSeenMessages(0, lastSeenMessages, (byte) 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,6 @@ import net.kyori.adventure.nbt.LongBinaryTag;
|
|||||||
import net.kyori.adventure.nbt.ShortBinaryTag;
|
import net.kyori.adventure.nbt.ShortBinaryTag;
|
||||||
import net.kyori.adventure.nbt.StringBinaryTag;
|
import net.kyori.adventure.nbt.StringBinaryTag;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@ -106,16 +105,14 @@ public class ComponentHolder {
|
|||||||
public BinaryTag getBinaryTag() {
|
public BinaryTag getBinaryTag() {
|
||||||
if (binaryTag == null) {
|
if (binaryTag == null) {
|
||||||
// TODO: replace this with adventure-text-serializer-nbt
|
// TODO: replace this with adventure-text-serializer-nbt
|
||||||
binaryTag = serialize(GsonComponentSerializer.gson().serializeToTree(getComponent()));
|
binaryTag = serialize(ProtocolUtils.getJsonChatSerializer(version).serializeToTree(getComponent()));
|
||||||
}
|
}
|
||||||
return binaryTag;
|
return binaryTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BinaryTag serialize(JsonElement json) {
|
public static BinaryTag serialize(JsonElement json) {
|
||||||
if (json instanceof JsonPrimitive) {
|
if (json instanceof JsonPrimitive jsonPrimitive) {
|
||||||
JsonPrimitive jsonPrimitive = (JsonPrimitive) json;
|
if (jsonPrimitive.isNumber()) {
|
||||||
|
|
||||||
if (jsonPrimitive.isNumber()) {
|
|
||||||
Number number = json.getAsNumber();
|
Number number = json.getAsNumber();
|
||||||
|
|
||||||
if (number instanceof Byte) {
|
if (number instanceof Byte) {
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.protocol.packet.chat;
|
package com.velocitypowered.proxy.protocol.packet.chat;
|
||||||
|
|
||||||
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -26,30 +27,38 @@ public class LastSeenMessages {
|
|||||||
|
|
||||||
public static final int WINDOW_SIZE = 20;
|
public static final int WINDOW_SIZE = 20;
|
||||||
private static final int DIV_FLOOR = -Math.floorDiv(-WINDOW_SIZE, 8);
|
private static final int DIV_FLOOR = -Math.floorDiv(-WINDOW_SIZE, 8);
|
||||||
private int offset;
|
private final int offset;
|
||||||
private BitSet acknowledged;
|
private final BitSet acknowledged;
|
||||||
|
private byte checksum;
|
||||||
|
|
||||||
public LastSeenMessages() {
|
public LastSeenMessages() {
|
||||||
this.offset = 0;
|
this(0, new BitSet(), (byte) 0);
|
||||||
this.acknowledged = new BitSet();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages(int offset, BitSet acknowledged) {
|
public LastSeenMessages(int offset, BitSet acknowledged, byte checksum) {
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
this.acknowledged = acknowledged;
|
this.acknowledged = acknowledged;
|
||||||
|
this.checksum = checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages(ByteBuf buf) {
|
public LastSeenMessages(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||||
this.offset = ProtocolUtils.readVarInt(buf);
|
this.offset = ProtocolUtils.readVarInt(buf);
|
||||||
|
|
||||||
byte[] bytes = new byte[DIV_FLOOR];
|
byte[] bytes = new byte[DIV_FLOOR];
|
||||||
buf.readBytes(bytes);
|
buf.readBytes(bytes);
|
||||||
this.acknowledged = BitSet.valueOf(bytes);
|
this.acknowledged = BitSet.valueOf(bytes);
|
||||||
|
|
||||||
|
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) {
|
||||||
|
this.checksum = buf.readByte();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encode(ByteBuf buf) {
|
public void encode(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||||
ProtocolUtils.writeVarInt(buf, offset);
|
ProtocolUtils.writeVarInt(buf, offset);
|
||||||
buf.writeBytes(Arrays.copyOf(acknowledged.toByteArray(), DIV_FLOOR));
|
buf.writeBytes(Arrays.copyOf(acknowledged.toByteArray(), DIV_FLOOR));
|
||||||
|
if (protocolVersion.noLessThan(ProtocolVersion.MINECRAFT_1_21_5)) {
|
||||||
|
buf.writeByte(this.checksum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getOffset() {
|
public int getOffset() {
|
||||||
@ -61,14 +70,15 @@ public class LastSeenMessages {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public LastSeenMessages offset(final int offset) {
|
public LastSeenMessages offset(final int offset) {
|
||||||
return new LastSeenMessages(this.offset + offset, acknowledged);
|
return new LastSeenMessages(this.offset + offset, acknowledged, checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "LastSeenMessages{" +
|
return "LastSeenMessages{" +
|
||||||
"offset=" + offset +
|
"offset=" + offset +
|
||||||
", acknowledged=" + acknowledged +
|
", acknowledged=" + acknowledged +
|
||||||
'}';
|
", checksum=" + checksum +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,7 +91,8 @@ public class LegacyChatPacket implements MinecraftPacket {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
|
||||||
message = ProtocolUtils.readString(buf);
|
message = ProtocolUtils.readString(buf, direction == ProtocolUtils.Direction.CLIENTBOUND
|
||||||
|
? 262144 : version.noLessThan(ProtocolVersion.MINECRAFT_1_11) ? 256 : 100);
|
||||||
if (direction == ProtocolUtils.Direction.CLIENTBOUND
|
if (direction == ProtocolUtils.Direction.CLIENTBOUND
|
||||||
&& version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
&& version.noLessThan(ProtocolVersion.MINECRAFT_1_8)) {
|
||||||
type = buf.readByte();
|
type = buf.readByte();
|
||||||
|
|||||||
@ -74,7 +74,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
|
|||||||
} else {
|
} else {
|
||||||
this.signature = new byte[0];
|
this.signature = new byte[0];
|
||||||
}
|
}
|
||||||
this.lastSeenMessages = new LastSeenMessages(buf);
|
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -87,7 +87,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
|
|||||||
if (this.signed) {
|
if (this.signed) {
|
||||||
buf.writeBytes(this.signature);
|
buf.writeBytes(this.signature);
|
||||||
}
|
}
|
||||||
this.lastSeenMessages.encode(buf);
|
this.lastSeenMessages.encode(buf, protocolVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -45,7 +45,7 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
|||||||
this.timeStamp = Instant.ofEpochMilli(buf.readLong());
|
this.timeStamp = Instant.ofEpochMilli(buf.readLong());
|
||||||
this.salt = buf.readLong();
|
this.salt = buf.readLong();
|
||||||
this.argumentSignatures = new ArgumentSignatures(buf);
|
this.argumentSignatures = new ArgumentSignatures(buf);
|
||||||
this.lastSeenMessages = new LastSeenMessages(buf);
|
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
|
||||||
|
|
||||||
this.argumentSignatures = new ArgumentSignatures();
|
this.argumentSignatures = new ArgumentSignatures();
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
|||||||
buf.writeLong(this.timeStamp.toEpochMilli());
|
buf.writeLong(this.timeStamp.toEpochMilli());
|
||||||
buf.writeLong(this.salt);
|
buf.writeLong(this.salt);
|
||||||
this.argumentSignatures.encode(buf);
|
this.argumentSignatures.encode(buf);
|
||||||
this.lastSeenMessages.encode(buf);
|
this.lastSeenMessages.encode(buf, protocolVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCommand() {
|
public String getCommand() {
|
||||||
|
|||||||
@ -68,12 +68,6 @@ public class ClientboundServerLinksPacket implements MinecraftPacket {
|
|||||||
|
|
||||||
public record ServerLink(int id, ComponentHolder displayName, String url) {
|
public record ServerLink(int id, ComponentHolder displayName, String url) {
|
||||||
|
|
||||||
public ServerLink(com.velocitypowered.api.util.ServerLink link, ProtocolVersion protocolVersion) {
|
|
||||||
this(link.getBuiltInType().map(Enum::ordinal).orElse(-1),
|
|
||||||
link.getCustomLabel().map(c -> new ComponentHolder(protocolVersion, c)).orElse(null),
|
|
||||||
link.getUrl().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ServerLink read(ByteBuf buf, ProtocolVersion version) {
|
private static ServerLink read(ByteBuf buf, ProtocolVersion version) {
|
||||||
if (buf.readBoolean()) {
|
if (buf.readBoolean()) {
|
||||||
return new ServerLink(ProtocolUtils.readVarInt(buf), null, ProtocolUtils.readString(buf));
|
return new ServerLink(ProtocolUtils.readVarInt(buf), null, ProtocolUtils.readString(buf));
|
||||||
|
|||||||
@ -43,20 +43,23 @@ public class PingSessionHandler implements MinecraftSessionHandler {
|
|||||||
private final MinecraftConnection connection;
|
private final MinecraftConnection connection;
|
||||||
private final ProtocolVersion version;
|
private final ProtocolVersion version;
|
||||||
private boolean completed = false;
|
private boolean completed = false;
|
||||||
|
private final String virtualHostString;
|
||||||
|
|
||||||
PingSessionHandler(CompletableFuture<ServerPing> result, RegisteredServer server,
|
PingSessionHandler(CompletableFuture<ServerPing> result, RegisteredServer server,
|
||||||
MinecraftConnection connection, ProtocolVersion version) {
|
MinecraftConnection connection, ProtocolVersion version, String virtualHostString) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
this.virtualHostString = virtualHostString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activated() {
|
public void activated() {
|
||||||
HandshakePacket handshake = new HandshakePacket();
|
HandshakePacket handshake = new HandshakePacket();
|
||||||
handshake.setIntent(HandshakeIntent.STATUS);
|
handshake.setIntent(HandshakeIntent.STATUS);
|
||||||
handshake.setServerAddress(server.getServerInfo().getAddress().getHostString());
|
handshake.setServerAddress(this.virtualHostString == null || this.virtualHostString.isEmpty()
|
||||||
|
? server.getServerInfo().getAddress().getHostString() : this.virtualHostString);
|
||||||
handshake.setPort(server.getServerInfo().getAddress().getPort());
|
handshake.setPort(server.getServerInfo().getAddress().getPort());
|
||||||
handshake.setProtocolVersion(version);
|
handshake.setProtocolVersion(version);
|
||||||
connection.delayedWrite(handshake);
|
connection.delayedWrite(handshake);
|
||||||
|
|||||||
@ -129,7 +129,7 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
|
|||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class);
|
MinecraftConnection conn = future.channel().pipeline().get(MinecraftConnection.class);
|
||||||
PingSessionHandler handler = new PingSessionHandler(pingFuture,
|
PingSessionHandler handler = new PingSessionHandler(pingFuture,
|
||||||
VelocityRegisteredServer.this, conn, pingOptions.getProtocolVersion());
|
VelocityRegisteredServer.this, conn, pingOptions.getProtocolVersion(), pingOptions.getVirtualHost());
|
||||||
conn.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler);
|
conn.setActiveSessionHandler(StateRegistry.HANDSHAKE, handler);
|
||||||
} else {
|
} else {
|
||||||
pingFuture.completeExceptionally(future.cause());
|
pingFuture.completeExceptionally(future.cause());
|
||||||
|
|||||||
@ -166,7 +166,7 @@ public class KeyedVelocityTabList implements InternalTabList {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder) {
|
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder, boolean showHat) {
|
||||||
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed);
|
return buildEntry(profile, displayName, latency, gameMode, chatSession, listed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,7 +90,7 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
} else {
|
} else {
|
||||||
entry = new VelocityTabListEntry(this, entry1.getProfile(),
|
entry = new VelocityTabListEntry(this, entry1.getProfile(),
|
||||||
entry1.getDisplayNameComponent().orElse(null),
|
entry1.getDisplayNameComponent().orElse(null),
|
||||||
entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed(), entry1.getListOrder());
|
entry1.getLatency(), entry1.getGameMode(), entry1.getChatSession(), entry1.isListed(), entry1.getListOrder(), entry1.isShowHat());
|
||||||
}
|
}
|
||||||
|
|
||||||
EnumSet<UpsertPlayerInfoPacket.Action> actions = EnumSet
|
EnumSet<UpsertPlayerInfoPacket.Action> actions = EnumSet
|
||||||
@ -134,6 +134,11 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
||||||
playerInfoEntry.setListOrder(entry.getListOrder());
|
playerInfoEntry.setListOrder(entry.getListOrder());
|
||||||
}
|
}
|
||||||
|
if (!Objects.equals(previousEntry.isShowHat(), entry.isShowHat())
|
||||||
|
&& player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||||
|
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_HAT);
|
||||||
|
playerInfoEntry.setShowHat(entry.isShowHat());
|
||||||
|
}
|
||||||
if (!Objects.equals(previousEntry.getChatSession(), entry.getChatSession())) {
|
if (!Objects.equals(previousEntry.getChatSession(), entry.getChatSession())) {
|
||||||
ChatSession from = entry.getChatSession();
|
ChatSession from = entry.getChatSession();
|
||||||
if (from != null) {
|
if (from != null) {
|
||||||
@ -173,6 +178,10 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_LIST_ORDER);
|
||||||
playerInfoEntry.setListOrder(entry.getListOrder());
|
playerInfoEntry.setListOrder(entry.getListOrder());
|
||||||
}
|
}
|
||||||
|
if (!entry.isShowHat() && player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||||
|
actions.add(UpsertPlayerInfoPacket.Action.UPDATE_HAT);
|
||||||
|
playerInfoEntry.setShowHat(entry.isShowHat());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
});
|
});
|
||||||
@ -218,9 +227,9 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
@Override
|
@Override
|
||||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
int gameMode,
|
int gameMode,
|
||||||
@Nullable ChatSession chatSession, boolean listed, int listOrder) {
|
@Nullable ChatSession chatSession, boolean listed, int listOrder, boolean showHat) {
|
||||||
return new VelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession,
|
return new VelocityTabListEntry(this, profile, displayName, latency, gameMode, chatSession,
|
||||||
listed, listOrder);
|
listed, listOrder, showHat);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -258,7 +267,8 @@ public class VelocityTabList implements InternalTabList {
|
|||||||
-1,
|
-1,
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
0
|
0,
|
||||||
|
true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -40,6 +40,7 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
private int gameMode;
|
private int gameMode;
|
||||||
private boolean listed;
|
private boolean listed;
|
||||||
private int listOrder;
|
private int listOrder;
|
||||||
|
private boolean showHat;
|
||||||
private @Nullable ChatSession session;
|
private @Nullable ChatSession session;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +48,7 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
*/
|
*/
|
||||||
public VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, Component displayName,
|
public VelocityTabListEntry(VelocityTabList tabList, GameProfile profile, Component displayName,
|
||||||
int latency,
|
int latency,
|
||||||
int gameMode, @Nullable ChatSession session, boolean listed, int listOrder) {
|
int gameMode, @Nullable ChatSession session, boolean listed, int listOrder, boolean showHat) {
|
||||||
this.tabList = tabList;
|
this.tabList = tabList;
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
@ -56,6 +57,7 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
this.session = session;
|
this.session = session;
|
||||||
this.listed = listed;
|
this.listed = listed;
|
||||||
this.listOrder = listOrder;
|
this.listOrder = listOrder;
|
||||||
|
this.showHat = showHat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -173,4 +175,24 @@ public class VelocityTabListEntry implements TabListEntry {
|
|||||||
void setListOrderWithoutUpdate(int listOrder) {
|
void setListOrderWithoutUpdate(int listOrder) {
|
||||||
this.listOrder = listOrder;
|
this.listOrder = listOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShowHat() {
|
||||||
|
return showHat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VelocityTabListEntry setShowHat(boolean showHat) {
|
||||||
|
this.showHat = showHat;
|
||||||
|
if (tabList.getPlayer().getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21_4)) {
|
||||||
|
UpsertPlayerInfoPacket.Entry upsertEntry = this.tabList.createRawEntry(this);
|
||||||
|
upsertEntry.setShowHat(showHat);
|
||||||
|
tabList.emitActionRaw(UpsertPlayerInfoPacket.Action.UPDATE_HAT, upsertEntry);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setShowHatWithoutUpdate(boolean showHat) {
|
||||||
|
this.showHat = showHat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -35,6 +35,8 @@ public class VelocityTabListEntryLegacy extends KeyedVelocityTabListEntry {
|
|||||||
@Override
|
@Override
|
||||||
public TabListEntry setDisplayName(@Nullable Component displayName) {
|
public TabListEntry setDisplayName(@Nullable Component displayName) {
|
||||||
getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating
|
getTabList().removeEntry(getProfile().getId()); // We have to remove first if updating
|
||||||
return super.setDisplayName(displayName);
|
setDisplayNameInternal(displayName);
|
||||||
|
getTabList().addEntry(this);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ package com.velocitypowered.proxy.tablist;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
|
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||||
|
import com.velocitypowered.api.proxy.player.ChatSession;
|
||||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
@ -133,9 +135,22 @@ public class VelocityTabListLegacy extends KeyedVelocityTabList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TabListEntry buildEntry(GameProfile profile,
|
||||||
|
net.kyori.adventure.text.@Nullable Component displayName,
|
||||||
|
int latency, int gameMode, @Nullable IdentifiedKey key) {
|
||||||
|
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
int gameMode) {
|
int gameMode, @Nullable ChatSession chatSession, boolean listed) {
|
||||||
|
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||||
|
int gameMode, @Nullable ChatSession chatSession, boolean listed, int listOrder, boolean showHat) {
|
||||||
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
return new VelocityTabListEntryLegacy(this, profile, displayName, latency, gameMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,6 +145,12 @@ log-player-connections = true
|
|||||||
# Transfer packet (Minecraft 1.20.5) to be received.
|
# Transfer packet (Minecraft 1.20.5) to be received.
|
||||||
accepts-transfers = false
|
accepts-transfers = false
|
||||||
|
|
||||||
|
# Enables support for SO_REUSEPORT. This may help the proxy scale better on multicore systems
|
||||||
|
# with a lot of incoming connections, and provide better CPU utilization than the existing
|
||||||
|
# strategy of having a single thread accepting connections and distributing them to worker
|
||||||
|
# threads. Disabled by default. Requires Linux or macOS.
|
||||||
|
enable-reuse-port = false
|
||||||
|
|
||||||
[query]
|
[query]
|
||||||
# Whether to enable responding to GameSpy 4 query responses or not.
|
# Whether to enable responding to GameSpy 4 query responses or not.
|
||||||
enabled = false
|
enabled = false
|
||||||
|
|||||||
@ -17,7 +17,7 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "velocity"
|
rootProject.name = "velocity"
|
||||||
|
|||||||
Reference in New Issue
Block a user