Compare commits
64
Commits
15b6ddd1bf
...
664826fec3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
664826fec3 | ||
|
|
fb0f21f425 | ||
|
|
5bb1992014 | ||
|
|
f975230f14 | ||
|
|
a6f9de9581 | ||
|
|
5000227d11 | ||
|
|
c10b492554 | ||
|
|
dff94da2b3 | ||
|
|
1b48f40534 | ||
|
|
bc55300123 | ||
|
|
a3b6177059 | ||
|
|
3ef5b2ab99 | ||
|
|
7a8bf8a5e0 | ||
|
|
05de745780 | ||
|
|
75ce1a9bf7 | ||
|
|
7a39ebb058 | ||
|
|
57e9cd7f24 | ||
|
|
b9463125d1 | ||
|
|
67e16a308e
|
||
|
|
4b8ceb5264 | ||
|
|
2d1cade7bd | ||
|
|
4067c44cd1 | ||
|
|
45bb5de601 | ||
|
|
ebb9eb046d | ||
|
|
afc84b88ac | ||
|
|
e6caa87865 | ||
|
|
cdeabe91d0 | ||
|
|
babb22bf88
|
||
|
|
468127996c
|
||
|
|
873fca763d | ||
|
|
67d63faeca | ||
|
|
871b053561 | ||
|
|
c2edc26d8e | ||
|
|
76417b13d4 | ||
|
|
47f36e3ff9 | ||
|
|
75bb48d00e | ||
|
|
11834de220 | ||
|
|
91a61643bd | ||
|
|
b6e05cb0b9 | ||
|
|
1507b91463 | ||
|
|
b06af3718c | ||
|
|
a20a896582 | ||
|
|
e1a3421212 | ||
|
|
19e51a2b12 | ||
|
|
b89a5c5ce9 | ||
|
|
65d3277319 | ||
|
|
a22bfa10f9 | ||
|
|
d9d1319a3a | ||
|
|
15ecbf4345 | ||
|
|
5e3bbcd427 | ||
|
|
a6c79db07b | ||
|
|
6e33bc6c17 | ||
|
|
01208bb359 | ||
|
|
fa88aaae52 | ||
|
|
2da400a267 | ||
|
|
8103135dfb | ||
|
|
cfabff7288 | ||
|
|
2f5a27a708 | ||
|
|
fdfe8bcc4b | ||
|
|
a19fd8db74 | ||
|
|
e63d71423d | ||
|
|
a7afe35fab | ||
|
|
56d6339313 | ||
|
|
2475572573 |
@@ -6,18 +6,28 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: namespace-profile-linux-default
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v6
|
||||
uses: namespacelabs/nscloud-checkout-action@v9
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1
|
||||
with:
|
||||
cache: gradle
|
||||
- name: Setup gradle build cache
|
||||
run: |
|
||||
nsc cache gradle setup --init-gradle /tmp/init.gradle
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
cache-disabled: true # namespace handles this
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@v6
|
||||
with:
|
||||
java-version: 21
|
||||
distribution: 'zulu'
|
||||
cache: "" # namespace handles this
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
run: ./gradlew --init-script=/tmp/init.gradle build
|
||||
|
||||
@@ -40,3 +40,5 @@ page.
|
||||
Translations are handled using [Crowdin](https://papermc-io.crowdin.com/velocity).
|
||||
If you want to translate a language not available on Crowdin,
|
||||
you might want to ask in the [Discord](https://discord.gg/papermc) about it.
|
||||
|
||||
[](https://namespace.so/github-actions/?utm_source=oss&utm_campaign=papermc)
|
||||
|
||||
@@ -73,6 +73,7 @@ public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLogin
|
||||
|
||||
/**
|
||||
* Returns the UUID of the connecting player.
|
||||
*
|
||||
* <p>This value is {@code null} on 1.19.2 and lower,
|
||||
* up to 1.20.1 it is optional and from 1.20.2 it will always be available.</p>
|
||||
*
|
||||
|
||||
@@ -40,9 +40,9 @@ public final class PlayerClientBrandEvent {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlayerClientBrandEvent{"
|
||||
+ "player=" + player
|
||||
+ ", brand='" + brand + '\''
|
||||
+ '}';
|
||||
+ "player=" + player
|
||||
+ ", brand='" + brand + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is executed when a player entered the configuration state and can be configured by Velocity.
|
||||
*
|
||||
* <p>Velocity will wait for this event before continuing/ending the configuration state.</p>
|
||||
*
|
||||
* @param player The player who can be configured.
|
||||
|
||||
+1
@@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* This event is executed when a player is about to enter the configuration state.
|
||||
* It is <b>not</b> called for the initial configuration of a player after login.
|
||||
*
|
||||
* <p>Velocity will wait for this event before asking the client to enter configuration state.
|
||||
* However due to backend server being unable to keep the connection alive during state changes,
|
||||
* Velocity will only wait for a maximum of 5 seconds.</p>
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is executed when a player has entered the configuration state.
|
||||
*
|
||||
* <p>From this moment on, until the {@link PlayerFinishedConfigurationEvent} is executed,
|
||||
* the {@linkplain Player#getProtocolState()} method is guaranteed
|
||||
* to return {@link ProtocolState#CONFIGURATION}.</p>
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is executed when a player is about to finish the configuration state.
|
||||
*
|
||||
* <p>Velocity will wait for this event before asking the client to finish the configuration state.
|
||||
* However due to backend server being unable to keep the connection alive during state changes,
|
||||
* Velocity will only wait for a maximum of 5 seconds. If you need to hold a player in configuration
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This event is executed when a player has finished the configuration state.
|
||||
*
|
||||
* <p>From this moment on, the {@link Player#getProtocolState()} method
|
||||
* will return {@link ProtocolState#PLAY}.</p>
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ package com.velocitypowered.api.network;
|
||||
public enum ProtocolState {
|
||||
/**
|
||||
* Initial connection State.
|
||||
*
|
||||
* <p>This status can be caused by a {@link HandshakeIntent#STATUS},
|
||||
* {@link HandshakeIntent#LOGIN} or {@link HandshakeIntent#TRANSFER} intent.</p>
|
||||
* If the intent is LOGIN or TRANSFER, the next state will be {@link #LOGIN},
|
||||
@@ -24,6 +25,7 @@ public enum ProtocolState {
|
||||
HANDSHAKE,
|
||||
/**
|
||||
* Ping State of a connection.
|
||||
*
|
||||
* <p>Connections with the {@link HandshakeIntent#STATUS} intent will pass through this state
|
||||
* and be disconnected after it requests the ping from the server
|
||||
* and the server responds with the respective ping.</p>
|
||||
@@ -31,11 +33,13 @@ public enum ProtocolState {
|
||||
STATUS,
|
||||
/**
|
||||
* Authentication State of a connection.
|
||||
*
|
||||
* <p>At this moment the player is authenticating with the authentication servers.</p>
|
||||
*/
|
||||
LOGIN,
|
||||
/**
|
||||
* Configuration State of a connection.
|
||||
*
|
||||
* <p>At this point the player allows the server to send information
|
||||
* such as resource packs and plugin messages, at the same time the player
|
||||
* will send his client brand and the respective plugin messages
|
||||
@@ -46,6 +50,7 @@ public enum ProtocolState {
|
||||
CONFIGURATION,
|
||||
/**
|
||||
* Game State of a connection.
|
||||
*
|
||||
* <p>In this state is where the whole game runs, the server is able to change
|
||||
* the player's state to {@link #CONFIGURATION} as needed in versions 1.20.2 and higher.</p>
|
||||
*/
|
||||
|
||||
@@ -96,7 +96,8 @@ public enum ProtocolVersion implements Ordered<ProtocolVersion> {
|
||||
MINECRAFT_1_21_9(773, "1.21.9", "1.21.10"),
|
||||
MINECRAFT_1_21_11(774, "1.21.11"),
|
||||
MINECRAFT_26_1(775, "26.1", "26.1.1", "26.1.2"),
|
||||
MINECRAFT_26_2(776, "26.2");
|
||||
MINECRAFT_26_2(776, "26.2"),
|
||||
MINECRAFT_26_3(777, "26.3");
|
||||
|
||||
private static final int SNAPSHOT_BIT = 30;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
* Represents three different states of a setting.
|
||||
*
|
||||
* <p>Possible values:</p>
|
||||
* <p></p>
|
||||
* <ul>
|
||||
* <li>{@link #TRUE} - a positive setting</li>
|
||||
* <li>{@link #FALSE} - a negative (negated) setting</li>
|
||||
|
||||
@@ -244,7 +244,7 @@ public interface Player extends
|
||||
* Gets the {@link ResourcePackInfo} of the currently applied
|
||||
* resource-pack or null if none.
|
||||
*
|
||||
* <p> Note that since 1.20.3 it is no longer recommended to use
|
||||
* <p>Note that since 1.20.3 it is no longer recommended to use
|
||||
* this method as it will only return the last applied
|
||||
* resource pack. To get all applied resource packs, use
|
||||
* {@link #getAppliedResourcePacks()} instead. </p>
|
||||
@@ -260,7 +260,7 @@ public interface Player extends
|
||||
* the user is currently downloading or is currently
|
||||
* prompted to install or null if none.
|
||||
*
|
||||
* <p> Note that since 1.20.3 it is no longer recommended to use
|
||||
* <p>Note that since 1.20.3 it is no longer recommended to use
|
||||
* this method as it will only return the last pending
|
||||
* resource pack. To get all pending resource packs, use
|
||||
* {@link #getPendingResourcePacks()} instead. </p>
|
||||
@@ -315,6 +315,7 @@ public interface Player extends
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p><strong>Note that this method does not send a plugin message to the server the player
|
||||
* is connected to.</strong> You should only use this method if you are trying to communicate
|
||||
* with a mod that is installed on the player's client.</p>
|
||||
@@ -400,7 +401,7 @@ public interface Player extends
|
||||
* and will not perform any actions.</b>
|
||||
* @see #playSound(Sound, Sound.Emitter)
|
||||
* @see <a href="https://docs.papermc.io/velocity/dev/pitfalls/#audience-operations-are-not-fully-supported">
|
||||
* Unsupported Adventure Operations</a>
|
||||
* Unsupported Adventure Operations</a>
|
||||
*/
|
||||
@Override
|
||||
default void playSound(@NotNull Sound sound) {
|
||||
@@ -413,7 +414,7 @@ public interface Player extends
|
||||
* and will not perform any actions.</b>
|
||||
* @see #playSound(Sound, Sound.Emitter)
|
||||
* @see <a href="https://docs.papermc.io/velocity/dev/pitfalls/#audience-operations-are-not-fully-supported">
|
||||
* Unsupported Adventure Operations</a>
|
||||
* Unsupported Adventure Operations</a>
|
||||
*/
|
||||
@Override
|
||||
default void playSound(@NotNull Sound sound, double x, double y, double z) {
|
||||
@@ -456,7 +457,7 @@ public interface Player extends
|
||||
* and will not perform any actions.</b>
|
||||
*
|
||||
* @see <a href="https://docs.papermc.io/velocity/dev/pitfalls/#audience-operations-are-not-fully-supported">
|
||||
* Unsupported Adventure Operations</a>
|
||||
* Unsupported Adventure Operations</a>
|
||||
*/
|
||||
@Override
|
||||
default void openBook(@NotNull Book book) {
|
||||
@@ -469,7 +470,7 @@ public interface Player extends
|
||||
* and will not perform any actions.</b>
|
||||
*
|
||||
* @see <a href="https://docs.papermc.io/velocity/dev/pitfalls/#audience-operations-are-not-fully-supported">
|
||||
* Unsupported Adventure Operations</a>
|
||||
* Unsupported Adventure Operations</a>
|
||||
*/
|
||||
@Override
|
||||
default void showDialog(@NotNull DialogLike dialog) {
|
||||
@@ -482,7 +483,7 @@ public interface Player extends
|
||||
* and will not perform any actions.</b>
|
||||
*
|
||||
* @see <a href="https://docs.papermc.io/velocity/dev/pitfalls/#audience-operations-are-not-fully-supported">
|
||||
* Unsupported Adventure Operations</a>
|
||||
* Unsupported Adventure Operations</a>
|
||||
*/
|
||||
@Override
|
||||
default void closeDialog() {
|
||||
|
||||
@@ -56,6 +56,7 @@ public interface KeySigned {
|
||||
* Validates the signature, expiry temporal and key against the
|
||||
* signer public key. Note: This will **not** check for
|
||||
* expiry. You can check for expiry with {@link KeySigned#hasExpired()}.
|
||||
*
|
||||
* <p>DOES NOT WORK YET FOR MESSAGES AND COMMANDS!</p>
|
||||
* Addendum: Does not work for 1.19.1 until the user has authenticated.
|
||||
*
|
||||
|
||||
@@ -237,8 +237,11 @@ public interface TabListEntry extends KeyIdentifiable {
|
||||
|
||||
/**
|
||||
* Sets the {@link IdentifiedKey} of the {@link TabListEntry}.
|
||||
*
|
||||
* <p>This only works for players currently <b>not</b> connected to this proxy.</p>
|
||||
*
|
||||
* <p>For any player currently connected to this proxy this will be filled automatically.</p>
|
||||
*
|
||||
* <p>Will ignore mismatching key revisions data.</p>
|
||||
*
|
||||
* @param chatSession session to set
|
||||
|
||||
@@ -33,6 +33,7 @@ public final class ServerPing {
|
||||
private final @Nullable Component description;
|
||||
private final @Nullable Favicon favicon;
|
||||
private final @Nullable ModInfo modinfo;
|
||||
private final boolean preventsChatReports = true;
|
||||
|
||||
public ServerPing(Version version, @Nullable Players players,
|
||||
net.kyori.adventure.text.Component description, @Nullable Favicon favicon) {
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ subprojects {
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(25))
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
group=com.velocitypowered
|
||||
version=4.1.2-SNAPSHOT
|
||||
version=4.2.1-SNAPSHOT
|
||||
|
||||
@@ -20,7 +20,7 @@ brigadier = "com.velocitypowered:velocity-brigadier:1.0.0-SNAPSHOT"
|
||||
bstats = "org.bstats:bstats-base:3.2.1"
|
||||
caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.4"
|
||||
checker-qual = "org.checkerframework:checker-qual:4.2.3"
|
||||
checkstyle = "com.puppycrawl.tools:checkstyle:10.9.3"
|
||||
checkstyle = "com.puppycrawl.tools:checkstyle:14.1.0"
|
||||
completablefutures = "com.spotify:completable-futures:0.3.6"
|
||||
configurate3-hocon = { module = "org.spongepowered:configurate-hocon", version.ref = "configurate3" }
|
||||
configurate3-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate3" }
|
||||
|
||||
@@ -54,8 +54,8 @@ public class MoreByteBufUtils {
|
||||
BufferPreference preferred = nativeStuff.preferredBufferType();
|
||||
return switch (preferred) {
|
||||
case DIRECT_PREFERRED, HEAP_PREFERRED ->
|
||||
// The native prefers this type, but doesn't strictly require we provide it.
|
||||
true;
|
||||
// The native prefers this type, but doesn't strictly require we provide it.
|
||||
true;
|
||||
case DIRECT_REQUIRED -> buf.hasMemoryAddress();
|
||||
case HEAP_REQUIRED -> buf.hasArray();
|
||||
};
|
||||
|
||||
@@ -488,23 +488,46 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Re-register servers. If a server is being replaced, make sure to note what players need to
|
||||
// move back to a fallback server.
|
||||
// Re-register servers. If a server is being replaced or removed, make sure to note what
|
||||
// players need to move back to a fallback server.
|
||||
Collection<ConnectedPlayer> evacuate = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : configuration.getServers().entrySet()) {
|
||||
if (newConfiguration.getServers().keySet().stream()
|
||||
.anyMatch(name -> name.equalsIgnoreCase(entry.getKey()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RegisteredServer rs = servers.getServer(entry.getKey()).orElse(null);
|
||||
if (rs == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Player player : rs.getPlayersConnected()) {
|
||||
if (!(player instanceof ConnectedPlayer)) {
|
||||
throw new IllegalStateException("Expected ConnectedPlayer for player " + player
|
||||
+ " on server " + rs.getServerInfo().getName() + ", got "
|
||||
+ player.getClass().getName());
|
||||
}
|
||||
evacuate.add((ConnectedPlayer) player);
|
||||
}
|
||||
servers.unregister(rs.getServerInfo());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : newConfiguration.getServers().entrySet()) {
|
||||
ServerInfo newInfo = new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue()));
|
||||
Optional<RegisteredServer> rs = servers.getServer(entry.getKey());
|
||||
if (rs.isEmpty()) {
|
||||
RegisteredServer rs = servers.getServer(entry.getKey()).orElse(null);
|
||||
if (rs == null) {
|
||||
servers.register(newInfo);
|
||||
} else if (!rs.get().getServerInfo().equals(newInfo)) {
|
||||
for (Player player : rs.get().getPlayersConnected()) {
|
||||
} else if (!rs.getServerInfo().equals(newInfo)) {
|
||||
for (Player player : rs.getPlayersConnected()) {
|
||||
if (!(player instanceof ConnectedPlayer)) {
|
||||
throw new IllegalStateException("ConnectedPlayer not found for player " + player
|
||||
+ " in server " + rs.get().getServerInfo().getName());
|
||||
throw new IllegalStateException("Expected ConnectedPlayer for player " + player
|
||||
+ " on server " + rs.getServerInfo().getName() + ", got "
|
||||
+ player.getClass().getName());
|
||||
}
|
||||
evacuate.add((ConnectedPlayer) player);
|
||||
}
|
||||
servers.unregister(rs.get().getServerInfo());
|
||||
servers.unregister(rs.getServerInfo());
|
||||
servers.register(newInfo);
|
||||
}
|
||||
}
|
||||
@@ -653,7 +676,7 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link #shutdown(boolean, Component)} with the default reason "Proxy shutting down."
|
||||
* Calls {@link #shutdown(boolean, Component)} with the default reason "Proxy shutting down".
|
||||
*
|
||||
* @param explicitExit whether the user explicitly shut down the proxy
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,6 @@ public record PingPassthroughMode(boolean version, boolean players,
|
||||
|
||||
public boolean enabled() {
|
||||
return this.version || this.players || this.description || this.favicon
|
||||
|| this.modinfo;
|
||||
|| this.modinfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.velocitypowered.proxy.protocol.packet.BossBarPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundPostEffectsPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundSoundEntityPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundStopSoundPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket;
|
||||
@@ -404,4 +405,8 @@ public interface MinecraftSessionHandler {
|
||||
default boolean handle(ClientboundStopSoundPacket packet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean handle(ClientboundPostEffectsPacket packet) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-6
@@ -34,6 +34,8 @@ import com.velocitypowered.api.event.player.ServerResourcePackSendEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.command.CommandGraphInjector;
|
||||
@@ -55,6 +57,7 @@ import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.DisconnectPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.JoinGamePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.KeepAlivePacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.LegacyPlayerListItemPacket;
|
||||
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
|
||||
@@ -164,6 +167,13 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return false; // forwards on
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(JoinGamePacket packet) {
|
||||
// We receive this if the connection is reconfigured
|
||||
packet.setOnlineMode(serverConn.getPlayer().isOnlineMode());
|
||||
return false; // forward
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ClientSettingsPacket packet) {
|
||||
serverConn.ensureConnected().write(packet);
|
||||
@@ -312,13 +322,9 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
ChannelIdentifier id = server.getChannelRegistrar().getFromId(packet.getChannel());
|
||||
if (id == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] copy = ByteBufUtil.getBytes(packet.content());
|
||||
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, copy);
|
||||
String channel = packet.getChannel();
|
||||
PluginMessageEvent event = new PluginMessageEvent(serverConn, serverConn.getPlayer(), channel.indexOf(':') == -1 ? new LegacyChannelIdentifier(channel) : MinecraftChannelIdentifier.from(channel), copy);
|
||||
server.getEventManager().fire(event).thenAcceptAsync(pme -> {
|
||||
if (pme.getResult().isAllowed() && !playerConnection.isClosed()) {
|
||||
PluginMessagePacket copied = new PluginMessagePacket(
|
||||
|
||||
+2
@@ -31,6 +31,8 @@ import com.velocitypowered.api.event.player.TabCompleteEvent;
|
||||
import com.velocitypowered.api.event.player.configuration.PlayerEnteredConfigurationEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.ConnectionTypes;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Modern (Minecraft 1.20.3+) ResourcePackHandler
|
||||
* Modern (Minecraft 1.20.3+) ResourcePackHandler.
|
||||
*/
|
||||
public final class ModernResourcePackHandler extends ResourcePackHandler {
|
||||
private final ListMultimap<UUID, ResourcePackInfo> outstandingResourcePacks =
|
||||
|
||||
+1
@@ -118,6 +118,7 @@ public abstract sealed class ResourcePackHandler
|
||||
|
||||
/**
|
||||
* Processes a client response to a sent resource-pack.
|
||||
*
|
||||
* <p>Cases in which no action will be taken:</p>
|
||||
* <ul>
|
||||
*
|
||||
|
||||
@@ -545,6 +545,36 @@ public class VelocityEventManager implements EventManager {
|
||||
}
|
||||
}
|
||||
|
||||
private <E> void fire(final @Nullable CompletableFuture<E> future, final E event,
|
||||
final int offset, final boolean currentlyAsync, final HandlerRegistration[] registrations) {
|
||||
for (int i = offset; i < registrations.length; i++) {
|
||||
final HandlerRegistration registration = registrations[i];
|
||||
try {
|
||||
final EventTask eventTask = registration.handler.executeAsync(event);
|
||||
if (eventTask == null) {
|
||||
continue;
|
||||
}
|
||||
final ContinuationTask<E> continuationTask = new ContinuationTask<>(eventTask,
|
||||
registrations, future, event, i, currentlyAsync);
|
||||
if (currentlyAsync || !eventTask.requiresAsync()) {
|
||||
if (continuationTask.execute()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
registration.plugin.getExecutorService().execute(continuationTask);
|
||||
}
|
||||
// fire will continue in another thread once the async task is
|
||||
// executed and the continuation is resumed
|
||||
return;
|
||||
} catch (final Throwable t) {
|
||||
logHandlerException(registration, t);
|
||||
}
|
||||
}
|
||||
if (future != null) {
|
||||
future.complete(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int TASK_STATE_DEFAULT = 0;
|
||||
private static final int TASK_STATE_EXECUTING = 1;
|
||||
private static final int TASK_STATE_CONTINUE_IMMEDIATELY = 2;
|
||||
@@ -669,36 +699,6 @@ public class VelocityEventManager implements EventManager {
|
||||
}
|
||||
}
|
||||
|
||||
private <E> void fire(final @Nullable CompletableFuture<E> future, final E event,
|
||||
final int offset, final boolean currentlyAsync, final HandlerRegistration[] registrations) {
|
||||
for (int i = offset; i < registrations.length; i++) {
|
||||
final HandlerRegistration registration = registrations[i];
|
||||
try {
|
||||
final EventTask eventTask = registration.handler.executeAsync(event);
|
||||
if (eventTask == null) {
|
||||
continue;
|
||||
}
|
||||
final ContinuationTask<E> continuationTask = new ContinuationTask<>(eventTask,
|
||||
registrations, future, event, i, currentlyAsync);
|
||||
if (currentlyAsync || !eventTask.requiresAsync()) {
|
||||
if (continuationTask.execute()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
registration.plugin.getExecutorService().execute(continuationTask);
|
||||
}
|
||||
// fire will continue in another thread once the async task is
|
||||
// executed and the continuation is resumed
|
||||
return;
|
||||
} catch (final Throwable t) {
|
||||
logHandlerException(registration, t);
|
||||
}
|
||||
}
|
||||
if (future != null) {
|
||||
future.complete(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static void logHandlerException(
|
||||
final HandlerRegistration registration, final Throwable t) {
|
||||
final PluginDescription pluginDescription = registration.plugin.getDescription();
|
||||
|
||||
@@ -143,6 +143,7 @@ public enum ProtocolUtils {
|
||||
.build();
|
||||
|
||||
public static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
|
||||
private static final int DEFAULT_MAX_KEY_ARRAY_SIZE = 1024;
|
||||
private static final int MAXIMUM_VARINT_SIZE = 5;
|
||||
private static final BinaryTagType<? extends BinaryTag>[] BINARY_TAG_TYPES = new BinaryTagType[] {
|
||||
BinaryTagTypes.END, BinaryTagTypes.BYTE, BinaryTagTypes.SHORT, BinaryTagTypes.INT,
|
||||
@@ -349,15 +350,20 @@ public enum ProtocolUtils {
|
||||
writeString(buf, key.asMinimalString());
|
||||
}
|
||||
|
||||
public static Key[] readKeyArray(ByteBuf buf) {
|
||||
return readKeyArray(buf, DEFAULT_MAX_KEY_ARRAY_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a standard Mojang Text namespaced:key array from the buffer.
|
||||
*
|
||||
* @param buf the buffer to read from
|
||||
* @return the decoded key array
|
||||
*/
|
||||
public static Key[] readKeyArray(ByteBuf buf) {
|
||||
public static Key[] readKeyArray(ByteBuf buf, int cap) {
|
||||
int length = readVarInt(buf);
|
||||
checkFrame(length >= 0, "Got a negative-length array (%s)", length);
|
||||
checkFrame(length <= cap, "Bad key array size (got %s, maximum is %s)", length, cap);
|
||||
checkFrame(buf.isReadable(length),
|
||||
"Trying to read an array that is too long (wanted %s, only have %s)", length,
|
||||
buf.readableBytes());
|
||||
@@ -644,7 +650,7 @@ public enum ProtocolUtils {
|
||||
private static final int FORGE_MAX_ARRAY_LENGTH = Integer.MAX_VALUE & 0x1FFF9A;
|
||||
|
||||
/**
|
||||
* Reads an byte array for legacy version 1.7 from the specified {@code buf}
|
||||
* Reads an byte array for legacy version 1.7 from the specified {@code buf}.
|
||||
*
|
||||
* @param buf the buffer to read from
|
||||
* @return the read byte array
|
||||
@@ -685,7 +691,7 @@ public enum ProtocolUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an byte array for legacy version 1.7 to the specified {@code buf}
|
||||
* Writes an byte array for legacy version 1.7 to the specified {@code buf}.
|
||||
*
|
||||
* @param b array
|
||||
* @param buf buf
|
||||
@@ -709,7 +715,7 @@ public enum ProtocolUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an {@link ByteBuf} for legacy version 1.7 to the specified {@code buf}
|
||||
* Writes an {@link ByteBuf} for legacy version 1.7 to the specified {@code buf}.
|
||||
*
|
||||
* @param b array
|
||||
* @param buf buf
|
||||
|
||||
@@ -18,37 +18,7 @@
|
||||
package com.velocitypowered.proxy.protocol;
|
||||
|
||||
import static com.google.common.collect.Iterables.getLast;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_12_1;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_4;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18_2;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_1;
|
||||
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_20_2;
|
||||
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_21;
|
||||
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_5;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_9;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9_4;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_26_1;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINIMUM_VERSION;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.SUPPORTED_VERSIONS;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.*;
|
||||
import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
|
||||
import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction.CLIENTBOUND;
|
||||
import static com.velocitypowered.proxy.protocol.ProtocolUtils.Direction.SERVERBOUND;
|
||||
@@ -234,31 +204,44 @@ public enum StateRegistry {
|
||||
map(0x06, MINECRAFT_1_20_2, false),
|
||||
map(0x07, MINECRAFT_1_20_3, false),
|
||||
map(0x09, MINECRAFT_1_20_5, false));
|
||||
clientbound.register(
|
||||
ClientboundPostEffectsPacket.class, ClientboundPostEffectsPacket::new,
|
||||
map(0x0A, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new,
|
||||
map(0x0A, MINECRAFT_1_20_5, false));
|
||||
map(0x0A, MINECRAFT_1_20_5, false),
|
||||
map(0x0B, MINECRAFT_26_3, false));
|
||||
clientbound.register(TransferPacket.class, TransferPacket::new,
|
||||
map(0x0B, MINECRAFT_1_20_5, false));
|
||||
map(0x0B, MINECRAFT_1_20_5, false),
|
||||
map(0x0C, MINECRAFT_26_3, false));
|
||||
clientbound.register(ActiveFeaturesPacket.class, ActiveFeaturesPacket::new,
|
||||
map(0x07, MINECRAFT_1_20_2, false),
|
||||
map(0x08, MINECRAFT_1_20_3, false),
|
||||
map(0x0C, MINECRAFT_1_20_5, false));
|
||||
map(0x0C, MINECRAFT_1_20_5, false),
|
||||
map(0x0D, MINECRAFT_26_3, false));
|
||||
clientbound.register(TagsUpdatePacket.class, TagsUpdatePacket::new,
|
||||
map(0x08, MINECRAFT_1_20_2, false),
|
||||
map(0x09, MINECRAFT_1_20_3, false),
|
||||
map(0x0D, MINECRAFT_1_20_5, false));
|
||||
map(0x0D, MINECRAFT_1_20_5, false),
|
||||
map(0x0E, MINECRAFT_26_3, false));
|
||||
clientbound.register(KnownPacksPacket.class, KnownPacksPacket::new,
|
||||
map(0x0E, MINECRAFT_1_20_5, false));
|
||||
map(0x0E, MINECRAFT_1_20_5, false),
|
||||
map(0x0F, MINECRAFT_26_3, false));
|
||||
clientbound.register(ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket::new,
|
||||
map(0x0F, MINECRAFT_1_21, false));
|
||||
map(0x0F, MINECRAFT_1_21, false),
|
||||
map(0x10, MINECRAFT_26_3, false));
|
||||
clientbound.register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new,
|
||||
map(0x10, MINECRAFT_1_21, false));
|
||||
map(0x10, MINECRAFT_1_21, false),
|
||||
map(0x11, MINECRAFT_26_3, false));
|
||||
clientbound.register(DialogClearPacket.class, () -> DialogClearPacket.INSTANCE,
|
||||
map(0x11, MINECRAFT_1_21_6, false));
|
||||
map(0x11, MINECRAFT_1_21_6, false),
|
||||
map(0x12, MINECRAFT_26_3, false));
|
||||
clientbound.register(DialogShowPacket.class, () -> new DialogShowPacket(this),
|
||||
map(0x12, MINECRAFT_1_21_6, false));
|
||||
map(0x12, MINECRAFT_1_21_6, false),
|
||||
map(0x13, MINECRAFT_26_3, false));
|
||||
clientbound.register(CodeOfConductPacket.class, CodeOfConductPacket::new,
|
||||
map(0x13, MINECRAFT_1_21_9, false));
|
||||
map(0x13, MINECRAFT_1_21_9, false),
|
||||
map(0x14, MINECRAFT_26_3, false));
|
||||
}
|
||||
},
|
||||
PLAY {
|
||||
@@ -410,7 +393,8 @@ public enum StateRegistry {
|
||||
map(0x2D, MINECRAFT_1_21_2, false),
|
||||
map(0x2F, MINECRAFT_1_21_4, false),
|
||||
map(0x30, MINECRAFT_1_21_6, false),
|
||||
map(0x31, MINECRAFT_26_1, false));
|
||||
map(0x31, MINECRAFT_26_1, false),
|
||||
map(0x32, MINECRAFT_26_3, false));
|
||||
serverbound.register(
|
||||
FinishedUpdatePacket.class, () -> FinishedUpdatePacket.INSTANCE,
|
||||
map(0x0B, MINECRAFT_1_20_2, false),
|
||||
@@ -469,6 +453,9 @@ public enum StateRegistry {
|
||||
ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new,
|
||||
map(0x16, MINECRAFT_1_20_5, false),
|
||||
map(0x15, MINECRAFT_1_21_5, false));
|
||||
clientbound.register(
|
||||
ClientboundPostEffectsPacket.class, ClientboundPostEffectsPacket::new,
|
||||
map(0x53, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket::new,
|
||||
map(0x5D, MINECRAFT_1_19_3, true),
|
||||
@@ -479,7 +466,8 @@ public enum StateRegistry {
|
||||
map(0x6E, MINECRAFT_1_21_2, true),
|
||||
map(0x6D, MINECRAFT_1_21_5, true),
|
||||
map(0x72, MINECRAFT_1_21_9, true),
|
||||
map(0x74, MINECRAFT_26_1, true));
|
||||
map(0x74, MINECRAFT_26_1, true),
|
||||
map(0x76, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new,
|
||||
map(0x5F, MINECRAFT_1_19_3, true),
|
||||
@@ -490,7 +478,8 @@ public enum StateRegistry {
|
||||
map(0x71, MINECRAFT_1_21_2, true),
|
||||
map(0x70, MINECRAFT_1_21_5, true),
|
||||
map(0x75, MINECRAFT_1_21_9, true),
|
||||
map(0x77, MINECRAFT_26_1, true));
|
||||
map(0x77, MINECRAFT_26_1, true),
|
||||
map(0x79, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
PluginMessagePacket.class,
|
||||
PluginMessagePacket::new,
|
||||
@@ -548,7 +537,8 @@ public enum StateRegistry {
|
||||
map(0x27, MINECRAFT_1_21_2, false),
|
||||
map(0x26, MINECRAFT_1_21_5, false),
|
||||
map(0x2B, MINECRAFT_1_21_9, false),
|
||||
map(0x2C, MINECRAFT_26_1, false));
|
||||
map(0x2C, MINECRAFT_26_1, false),
|
||||
map(0x2D, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
JoinGamePacket.class,
|
||||
JoinGamePacket::new,
|
||||
@@ -569,7 +559,8 @@ public enum StateRegistry {
|
||||
map(0x2C, MINECRAFT_1_21_2, false),
|
||||
map(0x2B, MINECRAFT_1_21_5, false),
|
||||
map(0x30, MINECRAFT_1_21_9, false),
|
||||
map(0x31, MINECRAFT_26_1, false));
|
||||
map(0x31, MINECRAFT_26_1, false),
|
||||
map(0x32, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
RespawnPacket.class,
|
||||
RespawnPacket::new,
|
||||
@@ -593,7 +584,8 @@ public enum StateRegistry {
|
||||
map(0x4C, MINECRAFT_1_21_2, true),
|
||||
map(0x4B, MINECRAFT_1_21_5, true),
|
||||
map(0x50, MINECRAFT_1_21_9, true),
|
||||
map(0x52, MINECRAFT_26_1, true));
|
||||
map(0x52, MINECRAFT_26_1, true),
|
||||
map(0x54, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
RemoveResourcePackPacket.class,
|
||||
RemoveResourcePackPacket::new,
|
||||
@@ -602,7 +594,8 @@ public enum StateRegistry {
|
||||
map(0x4A, MINECRAFT_1_21_2, false),
|
||||
map(0x49, MINECRAFT_1_21_5, false),
|
||||
map(0x4E, MINECRAFT_1_21_9, false),
|
||||
map(0x50, MINECRAFT_26_1, false));
|
||||
map(0x50, MINECRAFT_26_1, false),
|
||||
map(0x51, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
ResourcePackRequestPacket.class,
|
||||
ResourcePackRequestPacket::new,
|
||||
@@ -626,7 +619,8 @@ public enum StateRegistry {
|
||||
map(0x4B, MINECRAFT_1_21_2, false),
|
||||
map(0x4A, MINECRAFT_1_21_5, false),
|
||||
map(0x4F, MINECRAFT_1_21_9, false),
|
||||
map(0x51, MINECRAFT_26_1, false));
|
||||
map(0x51, MINECRAFT_26_1, false),
|
||||
map(0x52, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
HeaderAndFooterPacket.class,
|
||||
HeaderAndFooterPacket::new,
|
||||
@@ -651,7 +645,8 @@ public enum StateRegistry {
|
||||
map(0x74, MINECRAFT_1_21_2, true),
|
||||
map(0x73, MINECRAFT_1_21_5, true),
|
||||
map(0x78, MINECRAFT_1_21_9, true),
|
||||
map(0x7A, MINECRAFT_26_1, true));
|
||||
map(0x7A, MINECRAFT_26_1, true),
|
||||
map(0x7D, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
LegacyTitlePacket.class,
|
||||
LegacyTitlePacket::new,
|
||||
@@ -675,7 +670,8 @@ public enum StateRegistry {
|
||||
map(0x6A, MINECRAFT_1_21_2, true),
|
||||
map(0x69, MINECRAFT_1_21_5, true),
|
||||
map(0x6E, MINECRAFT_1_21_9, true),
|
||||
map(0x70, MINECRAFT_26_1, true));
|
||||
map(0x70, MINECRAFT_26_1, true),
|
||||
map(0x72, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
TitleTextPacket.class,
|
||||
TitleTextPacket::new,
|
||||
@@ -690,7 +686,8 @@ public enum StateRegistry {
|
||||
map(0x6C, MINECRAFT_1_21_2, true),
|
||||
map(0x6B, MINECRAFT_1_21_5, true),
|
||||
map(0x70, MINECRAFT_1_21_9, true),
|
||||
map(0x72, MINECRAFT_26_1, true));
|
||||
map(0x72, MINECRAFT_26_1, true),
|
||||
map(0x74, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
TitleActionbarPacket.class,
|
||||
TitleActionbarPacket::new,
|
||||
@@ -705,7 +702,8 @@ public enum StateRegistry {
|
||||
map(0x51, MINECRAFT_1_21_2, true),
|
||||
map(0x50, MINECRAFT_1_21_5, true),
|
||||
map(0x55, MINECRAFT_1_21_9, true),
|
||||
map(0x57, MINECRAFT_26_1, true));
|
||||
map(0x57, MINECRAFT_26_1, true),
|
||||
map(0x59, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
TitleTimesPacket.class,
|
||||
TitleTimesPacket::new,
|
||||
@@ -720,7 +718,8 @@ public enum StateRegistry {
|
||||
map(0x6D, MINECRAFT_1_21_2, true),
|
||||
map(0x6C, MINECRAFT_1_21_5, true),
|
||||
map(0x71, MINECRAFT_1_21_9, true),
|
||||
map(0x73, MINECRAFT_26_1, true));
|
||||
map(0x73, MINECRAFT_26_1, true),
|
||||
map(0x75, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
TitleClearPacket.class,
|
||||
TitleClearPacket::new,
|
||||
@@ -752,7 +751,8 @@ public enum StateRegistry {
|
||||
map(0x3F, MINECRAFT_1_21_2, false),
|
||||
map(0x3E, MINECRAFT_1_21_5, false),
|
||||
map(0x43, MINECRAFT_1_21_9, false),
|
||||
map(0x45, MINECRAFT_26_1, false));
|
||||
map(0x45, MINECRAFT_26_1, false),
|
||||
map(0x46, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
UpsertPlayerInfoPacket.class,
|
||||
UpsertPlayerInfoPacket::new,
|
||||
@@ -763,14 +763,16 @@ public enum StateRegistry {
|
||||
map(0x40, MINECRAFT_1_21_2, false),
|
||||
map(0x3F, MINECRAFT_1_21_5, false),
|
||||
map(0x44, MINECRAFT_1_21_9, false),
|
||||
map(0x46, MINECRAFT_26_1, false));
|
||||
map(0x46, MINECRAFT_26_1, false),
|
||||
map(0x47, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new,
|
||||
map(0x6B, MINECRAFT_1_20_5, false),
|
||||
map(0x72, MINECRAFT_1_21_2, false),
|
||||
map(0x71, MINECRAFT_1_21_5, false),
|
||||
map(0x76, MINECRAFT_1_21_9, false),
|
||||
map(0x78, MINECRAFT_26_1, false));
|
||||
map(0x78, MINECRAFT_26_1, false),
|
||||
map(0x7A, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
SystemChatPacket.class,
|
||||
SystemChatPacket::new,
|
||||
@@ -784,7 +786,8 @@ public enum StateRegistry {
|
||||
map(0x73, MINECRAFT_1_21_2, true),
|
||||
map(0x72, MINECRAFT_1_21_5, true),
|
||||
map(0x77, MINECRAFT_1_21_9, true),
|
||||
map(0x79, MINECRAFT_26_1, true));
|
||||
map(0x79, MINECRAFT_26_1, true),
|
||||
map(0x7C, MINECRAFT_26_3, true));
|
||||
clientbound.register(
|
||||
PlayerChatCompletionPacket.class,
|
||||
PlayerChatCompletionPacket::new,
|
||||
@@ -807,7 +810,8 @@ public enum StateRegistry {
|
||||
map(0x50, MINECRAFT_1_21_2, false),
|
||||
map(0x4F, MINECRAFT_1_21_5, false),
|
||||
map(0x54, MINECRAFT_1_21_9, false),
|
||||
map(0x56, MINECRAFT_26_1, false));
|
||||
map(0x56, MINECRAFT_26_1, false),
|
||||
map(0x58, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
StartUpdatePacket.class,
|
||||
() -> StartUpdatePacket.INSTANCE,
|
||||
@@ -817,7 +821,8 @@ public enum StateRegistry {
|
||||
map(0x70, MINECRAFT_1_21_2, false),
|
||||
map(0x6F, MINECRAFT_1_21_5, false),
|
||||
map(0x74, MINECRAFT_1_21_9, false),
|
||||
map(0x76, MINECRAFT_26_1, false));
|
||||
map(0x76, MINECRAFT_26_1, false),
|
||||
map(0x78, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
BundleDelimiterPacket.class,
|
||||
() -> BundleDelimiterPacket.INSTANCE,
|
||||
@@ -828,21 +833,46 @@ public enum StateRegistry {
|
||||
map(0x73, MINECRAFT_1_20_5, false),
|
||||
map(0x7A, MINECRAFT_1_21_2, false),
|
||||
map(0x7F, MINECRAFT_1_21_9, false),
|
||||
map(0x81, MINECRAFT_26_1, false));
|
||||
map(0x81, MINECRAFT_26_1, false),
|
||||
map(0x84, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
ClientboundCustomReportDetailsPacket.class,
|
||||
ClientboundCustomReportDetailsPacket::new,
|
||||
map(0x7A, MINECRAFT_1_21, false),
|
||||
map(0x81, MINECRAFT_1_21_2, false),
|
||||
map(0x86, MINECRAFT_1_21_9, false),
|
||||
map(0x88, MINECRAFT_26_1, false));
|
||||
map(0x88, MINECRAFT_26_1, false),
|
||||
map(0x8B, MINECRAFT_26_3, false));
|
||||
clientbound.register(
|
||||
ClientboundServerLinksPacket.class,
|
||||
ClientboundServerLinksPacket::new,
|
||||
map(0x7B, MINECRAFT_1_21, false),
|
||||
map(0x82, MINECRAFT_1_21_2, false),
|
||||
map(0x87, MINECRAFT_1_21_9, false),
|
||||
map(0x89, MINECRAFT_26_1, false),
|
||||
map(0x8C, MINECRAFT_26_3, false));
|
||||
map(0x89, MINECRAFT_26_1, false));
|
||||
clientbound.register(
|
||||
UpdateTeamsPacket.class,
|
||||
UpdateTeamsPacket::new,
|
||||
map(0x41, MINECRAFT_1_9, true),
|
||||
map(0x43, MINECRAFT_1_12, true),
|
||||
map(0x44, MINECRAFT_1_12_1, true),
|
||||
map(0x47, MINECRAFT_1_13, true),
|
||||
map(0x4B, MINECRAFT_1_14, true),
|
||||
map(0x4C, MINECRAFT_1_15, true),
|
||||
map(0x55, MINECRAFT_1_17, true),
|
||||
map(0x58, MINECRAFT_1_19_1, true),
|
||||
map(0x56, MINECRAFT_1_19_3, true),
|
||||
map(0x5A, MINECRAFT_1_19_4, true),
|
||||
map(0x5C, MINECRAFT_1_20_2, true),
|
||||
map(0x5E, MINECRAFT_1_20_3, true),
|
||||
map(0x60, MINECRAFT_1_20_5, true),
|
||||
map(0x67, MINECRAFT_1_21_2, true),
|
||||
map(0x6B, MINECRAFT_1_21_9, true),
|
||||
map(0x63, MINECRAFT_1_21_11, true),
|
||||
map(0x6D, MINECRAFT_26_1, true),
|
||||
map(0x6D, MINECRAFT_26_2, true));
|
||||
}
|
||||
},
|
||||
LOGIN {
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2018-2026 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
public final class ClientboundPostEffectsPacket implements MinecraftPacket {
|
||||
|
||||
private Key[] postEffects;
|
||||
|
||||
public ClientboundPostEffectsPacket() {
|
||||
}
|
||||
|
||||
public ClientboundPostEffectsPacket(Key[] postEffects) {
|
||||
this.postEffects = postEffects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
|
||||
this.postEffects = ProtocolUtils.readKeyArray(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) {
|
||||
ProtocolUtils.writeKeyArray(buf, this.postEffects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MinecraftSessionHandler handler) {
|
||||
return handler.handle(this);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class JoinGamePacket implements MinecraftPacket {
|
||||
|
||||
private static final BinaryTagIO.Reader JOINGAME_READER = BinaryTagIO.reader(4 * 1024 * 1024);
|
||||
private int entityId;
|
||||
private short gamemode;
|
||||
private int gamemode;
|
||||
private int dimension;
|
||||
private long partialHashedSeed; // 1.15+
|
||||
private short difficulty;
|
||||
@@ -47,7 +47,7 @@ public class JoinGamePacket implements MinecraftPacket {
|
||||
private CompoundBinaryTag registry; // 1.16+
|
||||
private DimensionInfo dimensionInfo; // 1.16+
|
||||
private CompoundBinaryTag currentDimensionData; // 1.16.2+
|
||||
private short previousGamemode; // 1.16+
|
||||
private int previousGamemode; // 1.16+
|
||||
private int simulationDistance; // 1.18+
|
||||
private @Nullable Pair<String, Long> lastDeathPosition; // 1.19+
|
||||
private int portalCooldown; // 1.20+
|
||||
@@ -63,11 +63,11 @@ public class JoinGamePacket implements MinecraftPacket {
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
public short getGamemode() {
|
||||
public int getGamemode() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
public void setGamemode(short gamemode) {
|
||||
public void setGamemode(int gamemode) {
|
||||
this.gamemode = gamemode;
|
||||
}
|
||||
|
||||
@@ -131,11 +131,11 @@ public class JoinGamePacket implements MinecraftPacket {
|
||||
this.dimensionInfo = dimensionInfo;
|
||||
}
|
||||
|
||||
public short getPreviousGamemode() {
|
||||
public int getPreviousGamemode() {
|
||||
return previousGamemode;
|
||||
}
|
||||
|
||||
public void setPreviousGamemode(short previousGamemode) {
|
||||
public void setPreviousGamemode(int previousGamemode) {
|
||||
this.previousGamemode = previousGamemode;
|
||||
}
|
||||
|
||||
@@ -345,8 +345,13 @@ public class JoinGamePacket implements MinecraftPacket {
|
||||
String levelName = ProtocolUtils.readString(buf);
|
||||
this.partialHashedSeed = buf.readLong();
|
||||
|
||||
this.gamemode = buf.readByte();
|
||||
this.previousGamemode = buf.readByte();
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_26_3)) {
|
||||
this.gamemode = ProtocolUtils.readVarInt(buf);
|
||||
this.previousGamemode = ProtocolUtils.readVarInt(buf); // game mode + 1 or 0
|
||||
} else {
|
||||
this.gamemode = buf.readByte();
|
||||
this.previousGamemode = buf.readByte(); // game mode or -1
|
||||
}
|
||||
|
||||
boolean isDebug = buf.readBoolean();
|
||||
boolean isFlat = buf.readBoolean();
|
||||
@@ -498,8 +503,13 @@ public class JoinGamePacket implements MinecraftPacket {
|
||||
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
|
||||
buf.writeLong(partialHashedSeed);
|
||||
|
||||
buf.writeByte(gamemode);
|
||||
buf.writeByte(previousGamemode);
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_26_3)) {
|
||||
ProtocolUtils.writeVarInt(buf, this.gamemode);
|
||||
ProtocolUtils.writeVarInt(buf, this.previousGamemode);
|
||||
} else {
|
||||
buf.writeByte(this.gamemode);
|
||||
buf.writeByte(this.previousGamemode);
|
||||
}
|
||||
|
||||
buf.writeBoolean(dimensionInfo.isDebugType());
|
||||
buf.writeBoolean(dimensionInfo.isFlat());
|
||||
|
||||
@@ -33,11 +33,11 @@ public class RespawnPacket implements MinecraftPacket {
|
||||
private int dimension;
|
||||
private long partialHashedSeed;
|
||||
private short difficulty;
|
||||
private short gamemode;
|
||||
private int gamemode;
|
||||
private String levelType = "";
|
||||
private byte dataToKeep; // 1.16+
|
||||
private DimensionInfo dimensionInfo; // 1.16-1.16.1
|
||||
private short previousGamemode; // 1.16+
|
||||
private int previousGamemode; // 1.16+
|
||||
private CompoundBinaryTag currentDimensionData; // 1.16.2+
|
||||
private @Nullable Pair<String, Long> lastDeathPosition; // 1.19+
|
||||
private int portalCooldown; // 1.20+
|
||||
@@ -46,9 +46,9 @@ public class RespawnPacket implements MinecraftPacket {
|
||||
public RespawnPacket() {
|
||||
}
|
||||
|
||||
public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, short gamemode,
|
||||
public RespawnPacket(int dimension, long partialHashedSeed, short difficulty, int gamemode,
|
||||
String levelType, byte dataToKeep, DimensionInfo dimensionInfo,
|
||||
short previousGamemode, CompoundBinaryTag currentDimensionData,
|
||||
int previousGamemode, CompoundBinaryTag currentDimensionData,
|
||||
@Nullable Pair<String, Long> lastDeathPosition, int portalCooldown,
|
||||
int seaLevel) {
|
||||
this.dimension = dimension;
|
||||
@@ -97,11 +97,11 @@ public class RespawnPacket implements MinecraftPacket {
|
||||
this.difficulty = difficulty;
|
||||
}
|
||||
|
||||
public short getGamemode() {
|
||||
public int getGamemode() {
|
||||
return gamemode;
|
||||
}
|
||||
|
||||
public void setGamemode(short gamemode) {
|
||||
public void setGamemode(int gamemode) {
|
||||
this.gamemode = gamemode;
|
||||
}
|
||||
|
||||
@@ -121,11 +121,11 @@ public class RespawnPacket implements MinecraftPacket {
|
||||
this.dataToKeep = dataToKeep;
|
||||
}
|
||||
|
||||
public short getPreviousGamemode() {
|
||||
public int getPreviousGamemode() {
|
||||
return previousGamemode;
|
||||
}
|
||||
|
||||
public void setPreviousGamemode(short previousGamemode) {
|
||||
public void setPreviousGamemode(int previousGamemode) {
|
||||
this.previousGamemode = previousGamemode;
|
||||
}
|
||||
|
||||
@@ -197,9 +197,17 @@ public class RespawnPacket implements MinecraftPacket {
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
|
||||
this.partialHashedSeed = buf.readLong();
|
||||
}
|
||||
this.gamemode = buf.readByte();
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_26_3)) {
|
||||
this.gamemode = ProtocolUtils.readVarInt(buf);
|
||||
} else {
|
||||
this.gamemode = buf.readByte();
|
||||
}
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
|
||||
this.previousGamemode = buf.readByte();
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_26_3)) {
|
||||
this.previousGamemode = ProtocolUtils.readVarInt(buf);
|
||||
} else {
|
||||
this.previousGamemode = buf.readByte();
|
||||
}
|
||||
boolean isDebug = buf.readBoolean();
|
||||
boolean isFlat = buf.readBoolean();
|
||||
this.dimensionInfo = new DimensionInfo(dimensionKey, levelName, isFlat, isDebug, version);
|
||||
@@ -249,9 +257,17 @@ public class RespawnPacket implements MinecraftPacket {
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_15)) {
|
||||
buf.writeLong(partialHashedSeed);
|
||||
}
|
||||
buf.writeByte(gamemode);
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_26_3)) {
|
||||
ProtocolUtils.writeVarInt(buf, this.gamemode);
|
||||
} else {
|
||||
buf.writeByte(this.gamemode);
|
||||
}
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_1_16)) {
|
||||
buf.writeByte(previousGamemode);
|
||||
if (version.noLessThan(ProtocolVersion.MINECRAFT_26_3)) {
|
||||
ProtocolUtils.writeVarInt(buf, this.previousGamemode);
|
||||
} else {
|
||||
buf.writeByte(this.previousGamemode);
|
||||
}
|
||||
buf.writeBoolean(dimensionInfo.isDebugType());
|
||||
buf.writeBoolean(dimensionInfo.isFlat());
|
||||
if (version.lessThan(ProtocolVersion.MINECRAFT_1_19_3)) {
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2024 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.protocol.packet;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class UpdateTeamsPacket implements MinecraftPacket {
|
||||
protected String name;
|
||||
protected Mode mode;
|
||||
|
||||
protected Component displayName;
|
||||
protected Component prefix;
|
||||
protected Component suffix;
|
||||
protected NameTagVisibility nameTagVisibility;
|
||||
protected CollisionRule collisionRule;
|
||||
protected int color;
|
||||
protected byte friendlyFlags;
|
||||
|
||||
protected List<String> players;
|
||||
|
||||
public UpdateTeamsPacket(String name, Mode mode, Component displayName, Component prefix, Component suffix, NameTagVisibility nameTagVisibility, CollisionRule collisionRule, int color, byte friendlyFlags, List<String> players) {
|
||||
this.name = name;
|
||||
this.mode = mode;
|
||||
this.displayName = displayName;
|
||||
this.prefix = prefix;
|
||||
this.suffix = suffix;
|
||||
this.nameTagVisibility = nameTagVisibility;
|
||||
this.collisionRule = collisionRule;
|
||||
this.color = color;
|
||||
this.friendlyFlags = friendlyFlags;
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
public UpdateTeamsPacket() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException("Packet is not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MinecraftSessionHandler minecraftSessionHandler) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf byteBuf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
throw new UnsupportedOperationException("Packet is not implemented");
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
CREATE,
|
||||
REMOVE,
|
||||
UPDATE,
|
||||
ADD_PLAYER,
|
||||
REMOVE_PLAYER,
|
||||
}
|
||||
|
||||
public enum NameTagVisibility {
|
||||
ALWAYS("always"),
|
||||
NEVER("never"),
|
||||
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
|
||||
HIDE_FOR_OWN_TEAM("hideForOwnTeam");
|
||||
|
||||
private final String value;
|
||||
|
||||
NameTagVisibility(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CollisionRule {
|
||||
ALWAYS("always"),
|
||||
NEVER("never"),
|
||||
PUSH_OTHER_TEAMS("pushOtherTeams"),
|
||||
PUSH_OWN_TEAM("pushOwnTeam");
|
||||
|
||||
private final String value;
|
||||
|
||||
CollisionRule(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public Component getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public Component getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public Component getSuffix() {
|
||||
return suffix;
|
||||
}
|
||||
|
||||
public NameTagVisibility getNameTagVisibility() {
|
||||
return nameTagVisibility;
|
||||
}
|
||||
|
||||
public CollisionRule getCollisionRule() {
|
||||
return collisionRule;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public byte getFriendlyFlags() {
|
||||
return friendlyFlags;
|
||||
}
|
||||
|
||||
public List<String> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setMode(Mode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public void setDisplayName(Component displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public void setPrefix(Component prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public void setSuffix(Component suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public void setNameTagVisibility(NameTagVisibility nameTagVisibility) {
|
||||
this.nameTagVisibility = nameTagVisibility;
|
||||
}
|
||||
|
||||
public void setCollisionRule(CollisionRule collisionRule) {
|
||||
this.collisionRule = collisionRule;
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public void setFriendlyFlags(byte friendlyFlags) {
|
||||
this.friendlyFlags = friendlyFlags;
|
||||
}
|
||||
|
||||
public void setPlayers(List<String> players) {
|
||||
this.players = players;
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -25,6 +25,7 @@ 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.api.network.ProtocolVersion.MINECRAFT_1_21_6;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_26_2;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_26_3;
|
||||
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.DoubleArgumentPropertySerializer.DOUBLE;
|
||||
@@ -273,16 +274,21 @@ public class ArgumentPropertyRegistry {
|
||||
empty(id("minecraft:heightmap", mapSet(MINECRAFT_1_21_6, 51), 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_21_6, 56), 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
|
||||
empty(id("minecraft:uuid", mapSet(MINECRAFT_26_3, 61), mapSet(MINECRAFT_1_21_6, 56), 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
|
||||
|
||||
empty(id("minecraft:loot_table", mapSet(MINECRAFT_1_21_6, 52), mapSet(MINECRAFT_1_21_5, 51), mapSet(MINECRAFT_1_20_5, 50)));
|
||||
empty(id("minecraft:loot_predicate", mapSet(MINECRAFT_1_21_6, 53), mapSet(MINECRAFT_1_21_5, 52), mapSet(MINECRAFT_1_20_5, 51)));
|
||||
empty(id("minecraft:loot_modifier", mapSet(MINECRAFT_1_21_6, 54), mapSet(MINECRAFT_1_21_5, 53), mapSet(MINECRAFT_1_20_5, 52)));
|
||||
|
||||
empty(id("minecraft:hex_color", mapSet(MINECRAFT_1_21_6, 17))); // added in 1.21.6
|
||||
empty(id("minecraft:dialog", mapSet(MINECRAFT_1_21_6, 55))); // added in 1.21.6
|
||||
empty(id("minecraft:dialog", mapSet(MINECRAFT_26_3, 58), mapSet(MINECRAFT_1_21_6, 55))); // added in 1.21.6
|
||||
empty(id("minecraft:team_color", mapSet(MINECRAFT_26_2, 16))); // renamed from color in 26.2
|
||||
empty(id("minecraft:context_float_provider", mapSet(MINECRAFT_26_3, 55))); // added in 26.3
|
||||
empty(id("minecraft:context_int_provider", mapSet(MINECRAFT_26_3, 56))); // added in 26.3
|
||||
empty(id("minecraft:slot_source", mapSet(MINECRAFT_26_3, 57))); // added in 26.3
|
||||
empty(id("minecraft:feature", mapSet(MINECRAFT_26_3, 59))); // added in 26.3
|
||||
empty(id("minecraft:swing_animation", mapSet(MINECRAFT_26_3, 60))); // added in 26.3
|
||||
|
||||
// Crossstitch support
|
||||
register(id("crossstitch:mod_argument", mapSet(MINECRAFT_1_19, -256)), ModArgumentProperty.class, MOD);
|
||||
|
||||
+2
@@ -116,6 +116,8 @@ public class KeyedPlayerChatPacket implements MinecraftPacket {
|
||||
ProtocolUtils.readByteArray(buf));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
@@ -132,6 +132,7 @@ public class KeyedPlayerCommandPacket implements MinecraftPacket {
|
||||
unsigned = true;
|
||||
}
|
||||
|
||||
unsigned = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ public class SessionPlayerChatPacket implements MinecraftPacket {
|
||||
this.salt = buf.readLong();
|
||||
this.signed = buf.readBoolean();
|
||||
if (this.signed) {
|
||||
this.signed = false;
|
||||
this.signature = readMessageSignature(buf);
|
||||
} else {
|
||||
this.signature = new byte[0];
|
||||
|
||||
+2
@@ -47,6 +47,8 @@ public class SessionPlayerCommandPacket implements MinecraftPacket {
|
||||
this.salt = buf.readLong();
|
||||
this.argumentSignatures = new ArgumentSignatures(buf);
|
||||
this.lastSeenMessages = new LastSeenMessages(buf, protocolVersion);
|
||||
|
||||
this.argumentSignatures = new ArgumentSignatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,40 +30,51 @@ package com.velocitypowered.proxy.util;
|
||||
* <p>This class is not thread-safe. If multiple threads access an instance concurrently,
|
||||
* external synchronization is required.</p>
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:WhitespaceAfter") // Not our class
|
||||
public final class IntervalledCounter {
|
||||
|
||||
private static final int INITIAL_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Data points within this many nanoseconds of the newest one are merged into it, bounding the
|
||||
* number of stored data points to roughly {@code interval / COALESCE_INTERVAL}.
|
||||
*/
|
||||
private static final long COALESCE_INTERVAL = 1_000_000L; // 1ms
|
||||
|
||||
/**
|
||||
* Ring buffer holding the timestamp (in nanoseconds) for each data point.
|
||||
*/
|
||||
protected long[] times;
|
||||
private long[] times;
|
||||
|
||||
/**
|
||||
* Ring buffer holding the count associated with each timestamp.
|
||||
*/
|
||||
protected long[] counts;
|
||||
private long[] counts;
|
||||
|
||||
/**
|
||||
* The sliding window size in nanoseconds. Only entries with time >= (currentTime - interval)
|
||||
* are considered part of the window.
|
||||
*/
|
||||
protected final long interval;
|
||||
private final long interval;
|
||||
|
||||
/**
|
||||
* Cached lower bound of the window (in nanoseconds) after the last update.
|
||||
*/
|
||||
protected long minTime;
|
||||
private long minTime;
|
||||
|
||||
/**
|
||||
* Running sum of all counts currently within the window.
|
||||
*/
|
||||
protected long sum;
|
||||
private long sum;
|
||||
|
||||
/**
|
||||
* Head index (inclusive) of the ring buffer.
|
||||
*/
|
||||
protected int head; // inclusive
|
||||
private int head; // inclusive
|
||||
|
||||
/**
|
||||
* Tail index (exclusive) of the ring buffer.
|
||||
*/
|
||||
protected int tail; // exclusive
|
||||
private int tail; // exclusive
|
||||
|
||||
/**
|
||||
* Creates a new counter with the specified interval.
|
||||
@@ -131,6 +142,8 @@ public final class IntervalledCounter {
|
||||
/**
|
||||
* Adds {@code count} units at the specified timestamp, assuming the timestamp is within the
|
||||
* current window. If the timestamp is older than {@code minTime}, the value is ignored.
|
||||
* If the timestamp is within {@link #COALESCE_INTERVAL} of the newest stored data point, the
|
||||
* count is merged into that data point instead of creating a new one.
|
||||
* This method does not automatically advance the window; callers should invoke
|
||||
* {@link #updateCurrentTime()} or {@link #updateCurrentTime(long)} beforehand.
|
||||
*
|
||||
@@ -142,6 +155,15 @@ public final class IntervalledCounter {
|
||||
if (currTime - this.minTime < 0) {
|
||||
return;
|
||||
}
|
||||
if (this.head != this.tail) {
|
||||
final int last = this.tail == 0 ? this.times.length - 1 : this.tail - 1;
|
||||
// guard against overflow by using subtraction
|
||||
if (currTime - this.times[last] < COALESCE_INTERVAL) {
|
||||
this.counts[last] += count;
|
||||
this.sum += count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
int nextTail = (this.tail + 1) % this.times.length;
|
||||
if (nextTail == this.head) {
|
||||
this.resize();
|
||||
@@ -219,7 +241,7 @@ public final class IntervalledCounter {
|
||||
* @return the rate in units per second for the current window
|
||||
*/
|
||||
public double getRate() {
|
||||
return (double)this.sum / ((double)this.interval * 1.0E-9);
|
||||
return (double) this.sum / ((double) this.interval * 1.0E-9);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class IntervalledCounterTest {
|
||||
|
||||
private static final long INTERVAL = TimeUnit.SECONDS.toNanos(7);
|
||||
|
||||
@Test
|
||||
void sumAndRateTrackAddedCounts() {
|
||||
IntervalledCounter counter = new IntervalledCounter(INTERVAL);
|
||||
long now = 0;
|
||||
|
||||
counter.updateAndAdd(700, now);
|
||||
assertEquals(700, counter.getSum());
|
||||
assertEquals(100.0, counter.getRate(), 1e-9);
|
||||
|
||||
now += TimeUnit.SECONDS.toNanos(1);
|
||||
counter.updateAndAdd(1400, now);
|
||||
assertEquals(2100, counter.getSum());
|
||||
assertEquals(300.0, counter.getRate(), 1e-9);
|
||||
}
|
||||
|
||||
@Test
|
||||
void dataPointsOutsideTheWindowAreEvicted() {
|
||||
IntervalledCounter counter = new IntervalledCounter(INTERVAL);
|
||||
long now = 0;
|
||||
|
||||
counter.updateAndAdd(10, now);
|
||||
now += TimeUnit.SECONDS.toNanos(3);
|
||||
counter.updateAndAdd(20, now);
|
||||
assertEquals(30, counter.getSum());
|
||||
assertEquals(2, counter.totalDataPoints());
|
||||
|
||||
// 7.5s after the first point: only the second point remains
|
||||
now = TimeUnit.MILLISECONDS.toNanos(7500);
|
||||
counter.updateCurrentTime(now);
|
||||
assertEquals(20, counter.getSum());
|
||||
assertEquals(1, counter.totalDataPoints());
|
||||
|
||||
// 10.5s: everything has expired
|
||||
now = TimeUnit.MILLISECONDS.toNanos(10500);
|
||||
counter.updateCurrentTime(now);
|
||||
assertEquals(0, counter.getSum());
|
||||
assertEquals(0, counter.totalDataPoints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void storedDataPointsAreBoundedByTimeNotByCallCount() {
|
||||
IntervalledCounter counter = new IntervalledCounter(INTERVAL);
|
||||
|
||||
// Simulate a flood of tiny (even zero-sized) packets arriving far faster than one per
|
||||
// millisecond for the whole window. Before coalescing was introduced, each call stored a
|
||||
// separate data point, so an attacker could grow the ring buffer without bound while staying
|
||||
// under a bytes-per-second limit.
|
||||
final long stepNanos = 100;
|
||||
final long calls = INTERVAL / stepNanos;
|
||||
long now = 0;
|
||||
for (long i = 0; i < calls; i++) {
|
||||
counter.updateAndAdd(i % 2, now);
|
||||
now += stepNanos;
|
||||
}
|
||||
|
||||
long expectedSum = calls / 2;
|
||||
assertEquals(expectedSum, counter.getSum());
|
||||
// 7s window at 1ms coalescing granularity is ~7000 points; leave some slack.
|
||||
assertTrue(counter.totalDataPoints() <= 7100,
|
||||
"expected at most ~7000 data points, got " + counter.totalDataPoints());
|
||||
assertTrue(counter.totalDataPoints() >= 7000,
|
||||
"expected at least 7000 data points, got " + counter.totalDataPoints());
|
||||
}
|
||||
|
||||
@Test
|
||||
void coalescedDataPointsExpireTogether() {
|
||||
IntervalledCounter counter = new IntervalledCounter(INTERVAL);
|
||||
|
||||
long now = 0;
|
||||
counter.updateAndAdd(5, now);
|
||||
// Within the same 1ms bucket: merged into the previous point
|
||||
counter.updateAndAdd(7, now + 500_000L);
|
||||
assertEquals(12, counter.getSum());
|
||||
assertEquals(1, counter.totalDataPoints());
|
||||
|
||||
// A new bucket starts a new point
|
||||
counter.updateAndAdd(1, now + 1_000_000L);
|
||||
assertEquals(13, counter.getSum());
|
||||
assertEquals(2, counter.totalDataPoints());
|
||||
|
||||
// The merged bucket carries the timestamp of its first point and expires with it
|
||||
counter.updateCurrentTime(now + INTERVAL + 1);
|
||||
assertEquals(1, counter.getSum());
|
||||
assertEquals(1, counter.totalDataPoints());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
build:
|
||||
- "./gradlew build -x check -x javadoc --no-daemon"
|
||||
|
||||
|
||||
artifacts:
|
||||
"/jars/Velocity.jar": "proxy/build/libs/velocity-proxy-4.1.0-SNAPSHOT-all.jar"
|
||||
|
||||
release:
|
||||
- "mvn deploy:deploy-file -DgroupId=de.steamwar -DartifactId=velocity -Dversion=RELEASE -Dpackaging=jar -Dfile=/jars/Velocity.jar -Durl=file:///var/www/maven/"
|
||||
Reference in New Issue
Block a user