3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-11-17 05:20:14 +01:00

Merge branch 'PaperMC:dev/3.0.0' into forwarding-mode

Dieser Commit ist enthalten in:
wafarm 2024-06-24 22:02:59 +08:00 committet von GitHub
Commit bb3d3b0729
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
29 geänderte Dateien mit 531 neuen und 90 gelöschten Zeilen

69
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normale Datei
Datei anzeigen

@ -0,0 +1,69 @@
name: Bug Report
description: Report issues with Velocity not working properly.
labels: ["type: bug"]
body:
- type: textarea
attributes:
label: Expected Behavior
description: What you expected to work and how.
validations:
required: true
- type: textarea
attributes:
label: Actual Behavior
description: What actually happens.
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Information on how we can reproduce this bug on our own, this can be e.g. just an explanation, a video or your Velocity config.
validations:
required: true
- type: textarea
attributes:
label: Plugin List
description: |
All plugins running on your proxy and the backend server you're experiencing this issue on.
Use `/velocity plugins` to list plugins on Velocity and `/plugins` to list plugins on your backend server.
validations:
required: true
- type: textarea
attributes:
label: Velocity Version
description: |
The full, unmodified output of running `/velocity info`.
*"Latest"* is not a version. We require you to paste the text, not a screenshot.
<details>
<summary>Example</summary>
```
[17:44:10 INFO]: Velocity 3.3.0-SNAPSHOT (git-9d25d309-b400)
[17:44:10 INFO]: Copyright 2018-2023 Velocity Contributors. Velocity is licensed under the terms of the GNU General Public License v3.
[17:44:10 INFO]: velocitypowered.com - GitHub
```
</details>
validations:
required: true
- type: textarea
attributes:
label: Additional Information
description: Anything else you think is helpful.
validations:
required: false
- type: markdown
attributes:
value: |
Before submitting this issue, please ensure the following:
1. You are running the latest version of Velocity from [our downloads page](https://papermc.io/downloads/velocity).
2. You searched for and ensured there isn't already an open issue regarding this.
If you think you have a bug, but are not sure, feel free to ask in the `#velocity-help` channel on our
[Discord](https://discord.gg/papermc).

10
.github/ISSUE_TEMPLATE/config.yml vendored Normale Datei
Datei anzeigen

@ -0,0 +1,10 @@
blank_issues_enabled: false
contact_links:
- name: PaperMC Discord
url: https://discord.gg/papermc
about: If you are having issues with the proxy not connecting to servers or have other minor issues, come ask us on our Discord server!
- name: Exploit Report
url: https://discord.gg/papermc
about: |
Due to GitHub not currently allowing private issues, exploit reports are currently handled via our Discord.
To report an exploit, see the #paper-exploit-report channel.

48
.github/ISSUE_TEMPLATE/feature-request.yml vendored Normale Datei
Datei anzeigen

@ -0,0 +1,48 @@
name: Feature Request
description: Request for a feature to be implemented into Velocity.
labels: ["type: feature"]
body:
- type: textarea
attributes:
label: Requested Feature
description: |
Please describe as best as you can what you'd like to be added to Velocity.
validations:
required: true
- type: textarea
attributes:
label: Why is this needed?
description: |
Please describe why do you need this feature.
Do you think it could be useful? Is it due to another problem?
validations:
required: true
- type: textarea
attributes:
label: Alternative Solutions
description: |
Are there any alternative solutions to implementing a new feature?
What have you tried instead?
validations:
required: true
- type: textarea
attributes:
label: Additional Information
description: Anything else you want to add.
validations:
required: false
- type: markdown
attributes:
value: |
Before submitting this request, please ensure the following:
1. You are running the latest version of Velocity from [our downloads page](https://papermc.io/downloads/velocity).
2. You searched for and ensured there isn't already an open issue regarding this.
3. The feature you're requesting has to be implemented on Velocity and not on the backend server.
If you are unsure whether your problem can already be fixed in another way, feel free to ask in the `#velocity-help` channel on our
[Discord](https://discord.gg/papermc).

Datei anzeigen

@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 Velocity Contributors
*
* The Velocity API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the api top-level directory.
*/
package com.velocitypowered.api.event.player.configuration;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import org.jetbrains.annotations.NotNull;
/**
* This event is executed when a player with version 1.20.2 or higher enters the configuration phase.
* <p>From this moment on, until the {@link PlayerFinishedConfigurationEvent} is executed,
* the {@linkplain Player#getProtocolState()} method is guaranteed
* to return {@link ProtocolState#CONFIGURATION}.</p>
*
* @param player The player that has entered the configuration phase.
* @param server The server that will now (re-)configure the player.
* @since 3.3.0
* @sinceMinecraft 1.20.2
*/
public record PlayerEnterConfigurationEvent(@NotNull Player player, ServerConnection server) {
}

Datei anzeigen

@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 Velocity Contributors
*
* The Velocity API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the api top-level directory.
*/
package com.velocitypowered.api.event.player.configuration;
import com.velocitypowered.api.event.annotation.AwaitingEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import org.jetbrains.annotations.NotNull;
/**
* This event is executed when the player is about to finish the Configuration state.
* <p>Velocity will wait for this event to finish the configuration phase on the client.</p>
*
* @param player The player who is about to complete the configuration phase.
* @param server The server that is currently (re-)configuring the player.
* @since 3.3.0
* @sinceMinecraft 1.20.2
*/
@AwaitingEvent
public record PlayerFinishConfigurationEvent(@NotNull Player player, @NotNull ServerConnection server) {
}

Datei anzeigen

@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 Velocity Contributors
*
* The Velocity API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the api top-level directory.
*/
package com.velocitypowered.api.event.player.configuration;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection;
import org.jetbrains.annotations.NotNull;
/**
* Event executed when a player of version 1.20.2 or higher finishes the Configuration state.
* <p>From this moment on, the {@link Player#getProtocolState()} method
* will return {@link ProtocolState#PLAY}.</p>
*
* @param player The player who has completed the Configuration state
* @param server The server that has (re-)configured the player.
* @since 3.3.0
* @sinceMinecraft 1.20.2
*/
public record PlayerFinishedConfigurationEvent(@NotNull Player player, @NotNull ServerConnection server) {
}

Datei anzeigen

@ -21,6 +21,7 @@ import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.api.util.ServerLink;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -461,4 +462,16 @@ public interface Player extends
* @sinceMinecraft 1.20.5 * @sinceMinecraft 1.20.5
*/ */
void requestCookie(Key key); void requestCookie(Key key);
/**
* Send the player a list of custom links to display in their client's pause menu.
*
* <p>Note that later packets sent by the backend server may override links sent by the proxy.
*
* @param links an ordered list of {@link ServerLink}s to send to the player
* @throws IllegalArgumentException if the player is from a version lower than 1.21
* @since 3.3.0
* @sinceMinecraft 1.21
*/
void setServerLinks(@NotNull List<ServerLink> links);
} }

Datei anzeigen

@ -0,0 +1,101 @@
/*
* Copyright (C) 2021-2024 Velocity Contributors
*
* The Velocity API is licensed under the terms of the MIT License. For more details,
* reference the LICENSE file in the api top-level directory.
*/
package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import java.net.URI;
import java.util.Optional;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
/**
* Represents a custom URL servers can show in player pause menus.
* Links can be of a built-in type or use a custom component text label.
*/
public final class ServerLink {
private @Nullable Type type;
private @Nullable Component label;
private final URI url;
private ServerLink(Component label, String url) {
this.label = Preconditions.checkNotNull(label, "label");
this.url = URI.create(url);
}
private ServerLink(Type type, String url) {
this.type = Preconditions.checkNotNull(type, "type");
this.url = URI.create(url);
}
/**
* Construct a server link with a custom component label.
*
* @param label a custom component label to display
* @param link the URL to open when clicked
*/
public static ServerLink serverLink(Component label, String link) {
return new ServerLink(label, link);
}
/**
* Construct a server link with a built-in type.
*
* @param type the {@link Type built-in type} of link
* @param link the URL to open when clicked
*/
public static ServerLink serverLink(Type type, String link) {
return new ServerLink(type, link);
}
/**
* Get the type of the server link.
*
* @return the type of the server link
*/
public Optional<Type> getBuiltInType() {
return Optional.ofNullable(type);
}
/**
* Get the custom component label of the server link.
*
* @return the custom component label of the server link
*/
public Optional<Component> getCustomLabel() {
return Optional.ofNullable(label);
}
/**
* Get the link {@link URI}.
*
* @return the link {@link URI}
*/
public URI getUrl() {
return url;
}
/**
* Built-in types of server links.
*
* @apiNote {@link Type#BUG_REPORT} links are shown on the connection error screen
*/
public enum Type {
BUG_REPORT,
COMMUNITY_GUIDELINES,
SUPPORT,
STATUS,
FEEDBACK,
COMMUNITY,
WEBSITE,
FORUMS,
NEWS,
ANNOUNCEMENTS
}
}

Datei anzeigen

@ -45,7 +45,7 @@ import com.velocitypowered.proxy.command.builtin.ShutdownCommand;
import com.velocitypowered.proxy.command.builtin.VelocityCommand; import com.velocitypowered.proxy.command.builtin.VelocityCommand;
import com.velocitypowered.proxy.config.VelocityConfiguration; import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.util.ServerListPingHandler; import com.velocitypowered.proxy.connection.util.ServerListPingHandler;
import com.velocitypowered.proxy.console.VelocityConsole; import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.crypto.EncryptionUtils; import com.velocitypowered.proxy.crypto.EncryptionUtils;

Datei anzeigen

@ -47,7 +47,8 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftCompressorAndLengthEnco
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueHandler; import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueInboundHandler;
import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueOutboundHandler;
import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket; import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
@ -148,13 +149,11 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
return; return;
} }
if (msg instanceof MinecraftPacket) { if (msg instanceof MinecraftPacket pkt) {
MinecraftPacket pkt = (MinecraftPacket) msg;
if (!pkt.handle(activeSessionHandler)) { if (!pkt.handle(activeSessionHandler)) {
activeSessionHandler.handleGeneric((MinecraftPacket) msg); activeSessionHandler.handleGeneric((MinecraftPacket) msg);
} }
} else if (msg instanceof HAProxyMessage) { } else if (msg instanceof HAProxyMessage proxyMessage) {
HAProxyMessage proxyMessage = (HAProxyMessage) msg;
this.remoteAddress = new InetSocketAddress(proxyMessage.sourceAddress(), this.remoteAddress = new InetSocketAddress(proxyMessage.sourceAddress(),
proxyMessage.sourcePort()); proxyMessage.sourcePort());
} else if (msg instanceof ByteBuf) { } else if (msg instanceof ByteBuf) {
@ -383,9 +382,14 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
if (state == StateRegistry.CONFIG) { if (state == StateRegistry.CONFIG) {
// Activate the play packet queue // Activate the play packet queue
addPlayPacketQueueHandler(); addPlayPacketQueueHandler();
} else if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE) != null) { } else {
// Remove the queue // Remove the queue
this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE); if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) != null) {
this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE_OUTBOUND);
}
if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_INBOUND) != null) {
this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE_INBOUND);
}
} }
} }
@ -393,10 +397,13 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
* Adds the play packet queue handler. * Adds the play packet queue handler.
*/ */
public void addPlayPacketQueueHandler() { public void addPlayPacketQueueHandler() {
if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE) == null) { if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) == null) {
this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE, this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE_OUTBOUND,
new PlayPacketQueueHandler(this.protocolVersion, new PlayPacketQueueOutboundHandler(this.protocolVersion, channel.pipeline().get(MinecraftEncoder.class).getDirection()));
channel.pipeline().get(MinecraftEncoder.class).getDirection())); }
if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_INBOUND) == null) {
this.channel.pipeline().addAfter(Connections.MINECRAFT_DECODER, Connections.PLAY_PACKET_QUEUE_INBOUND,
new PlayPacketQueueInboundHandler(this.protocolVersion, channel.pipeline().get(MinecraftDecoder.class).getDirection()));
} }
} }

Datei anzeigen

@ -40,8 +40,8 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler; import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackHandler; import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;

Datei anzeigen

@ -37,7 +37,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import java.util.Optional; import java.util.Optional;
import java.util.StringJoiner; import java.util.StringJoiner;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@ -143,7 +142,7 @@ public class BungeeCordMessageResponder {
out.writeUTF("PlayerList"); out.writeUTF("PlayerList");
out.writeUTF(info.getServerInfo().getName()); out.writeUTF(info.getServerInfo().getName());
StringJoiner joiner = new StringJoiner(", "); final StringJoiner joiner = new StringJoiner(", ");
for (Player online : info.getPlayersConnected()) { for (Player online : info.getPlayersConnected()) {
joiner.add(online.getUsername()); joiner.add(online.getUsername());
} }
@ -187,10 +186,9 @@ public class BungeeCordMessageResponder {
Component messageComponent = serializer.deserialize(message); Component messageComponent = serializer.deserialize(message);
if (target.equals("ALL")) { if (target.equals("ALL")) {
proxy.sendMessage(Identity.nil(), messageComponent); proxy.sendMessage(messageComponent);
} else { } else {
proxy.getPlayer(target).ifPresent(player -> player.sendMessage(Identity.nil(), proxy.getPlayer(target).ifPresent(player -> player.sendMessage(messageComponent));
messageComponent));
} }
} }

Datei anzeigen

@ -29,7 +29,7 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler; import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
@ -132,7 +132,8 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(KeepAlivePacket packet) { public boolean handle(KeepAlivePacket packet) {
serverConn.ensureConnected().write(packet); serverConn.getPendingPings().put(packet.getRandomId(), System.nanoTime());
serverConn.getPlayer().getConnection().write(packet);
return true; return true;
} }
@ -193,30 +194,25 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(FinishedUpdatePacket packet) { public boolean handle(FinishedUpdatePacket packet) {
MinecraftConnection smc = serverConn.ensureConnected(); final MinecraftConnection smc = serverConn.ensureConnected();
ConnectedPlayer player = serverConn.getPlayer(); final ConnectedPlayer player = serverConn.getPlayer();
ClientConfigSessionHandler configHandler = final ClientConfigSessionHandler configHandler = (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
(ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
smc.setAutoReading(false);
// Even when not auto reading messages are still decoded. Decode them with the correct state
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY); smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY);
configHandler.handleBackendFinishUpdate(serverConn).thenAcceptAsync((unused) -> { //noinspection DataFlowIssue
configHandler.handleBackendFinishUpdate(serverConn).thenRunAsync(() -> {
smc.write(FinishedUpdatePacket.INSTANCE);
if (serverConn == player.getConnectedServer()) { if (serverConn == player.getConnectedServer()) {
smc.setActiveSessionHandler(StateRegistry.PLAY); smc.setActiveSessionHandler(StateRegistry.PLAY);
player.sendPlayerListHeaderAndFooter( player.sendPlayerListHeaderAndFooter(player.getPlayerListHeader(), player.getPlayerListFooter());
player.getPlayerListHeader(), player.getPlayerListFooter());
// The client cleared the tab list. TODO: Restore changes done via TabList API // The client cleared the tab list. TODO: Restore changes done via TabList API
player.getTabList().clearAllSilent(); player.getTabList().clearAllSilent();
} else { } else {
smc.setActiveSessionHandler(StateRegistry.PLAY, smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture));
new TransitionSessionHandler(server, serverConn, resultFuture));
} }
if (player.resourcePackHandler().getFirstAppliedPack() == null if (player.resourcePackHandler().getFirstAppliedPack() == null && resourcePackToApply != null) {
&& resourcePackToApply != null) {
player.resourcePackHandler().queueResourcePack(resourcePackToApply); player.resourcePackHandler().queueResourcePack(resourcePackToApply);
} }
smc.setAutoReading(true);
}, smc.eventLoop()); }, smc.eventLoop());
return true; return true;
} }

Datei anzeigen

@ -166,12 +166,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
if (player.getClientSettingsPacket() != null) { if (player.getClientSettingsPacket() != null) {
smc.write(player.getClientSettingsPacket()); smc.write(player.getClientSettingsPacket());
} }
if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler) { if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) {
smc.setAutoReading(false); smc.setAutoReading(false);
((ClientPlaySessionHandler) player.getConnection() clientPlaySessionHandler.doSwitch().thenAcceptAsync((unused) -> smc.setAutoReading(true), smc.eventLoop());
.getActiveSessionHandler()).doSwitch().thenAcceptAsync((unused) -> {
smc.setAutoReading(true);
}, smc.eventLoop());
} }
} }

Datei anzeigen

@ -19,6 +19,8 @@ package com.velocitypowered.proxy.connection.client;
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.PlayerFinishConfigurationEvent;
import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
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;
@ -246,13 +248,11 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
smc.write(brandPacket); smc.write(brandPacket);
} }
player.getConnection().eventLoop().execute(() -> { server.getEventManager().fire(new PlayerFinishConfigurationEvent(player, serverConn)).thenAcceptAsync(event -> {
player.getConnection().write(FinishedUpdatePacket.INSTANCE); player.getConnection().write(FinishedUpdatePacket.INSTANCE);
player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY); player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
}); server.getEventManager().fireAndForget(new PlayerFinishedConfigurationEvent(player, serverConn));
}, player.getConnection().eventLoop());
smc.write(FinishedUpdatePacket.INSTANCE);
smc.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
return configSwitchFuture; return configSwitchFuture;
} }

Datei anzeigen

@ -27,6 +27,7 @@ import com.velocitypowered.api.event.player.CookieReceiveEvent;
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent; import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent; import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
import com.velocitypowered.api.event.player.TabCompleteEvent; import com.velocitypowered.api.event.player.TabCompleteEvent;
import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
@ -406,6 +407,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Complete client switch // Complete client switch
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG); player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
VelocityServerConnection serverConnection = player.getConnectedServer(); VelocityServerConnection serverConnection = player.getConnectedServer();
server.getEventManager().fireAndForget(new PlayerEnterConfigurationEvent(player, serverConnection));
if (serverConnection != null) { if (serverConnection != null) {
MinecraftConnection smc = serverConnection.ensureConnected(); MinecraftConnection smc = serverConnection.ensureConnected();
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
@ -512,7 +514,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
* @return a future that completes when the switch is complete * @return a future that completes when the switch is complete
*/ */
public CompletableFuture<Void> doSwitch() { public CompletableFuture<Void> doSwitch() {
VelocityServerConnection existingConnection = player.getConnectedServer(); final VelocityServerConnection existingConnection = player.getConnectedServer();
if (existingConnection != null) { if (existingConnection != null) {
// Shut down the existing server connection. // Shut down the existing server connection.

Datei anzeigen

@ -37,6 +37,7 @@ import com.velocitypowered.api.event.player.KickedFromServerEvent.ServerKickResu
import com.velocitypowered.api.event.player.PlayerModInfoEvent; 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.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;
@ -54,13 +55,15 @@ import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.util.ModInfo; import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.api.util.ServerLink;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.adventure.VelocityBossBarImplementation; import com.velocitypowered.proxy.adventure.VelocityBossBarImplementation;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.bundle.BundleDelimiterHandler;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackHandler; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler;
import com.velocitypowered.proxy.connection.util.ConnectionMessages; import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl; import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
import com.velocitypowered.proxy.connection.util.VelocityInboundConnection; import com.velocitypowered.proxy.connection.util.VelocityInboundConnection;
@ -81,6 +84,7 @@ import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket; import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletionPacket;
import com.velocitypowered.proxy.protocol.packet.chat.builder.ChatBuilderFactory; import com.velocitypowered.proxy.protocol.packet.chat.builder.ChatBuilderFactory;
import com.velocitypowered.proxy.protocol.packet.chat.legacy.LegacyChatPacket; import com.velocitypowered.proxy.protocol.packet.chat.legacy.LegacyChatPacket;
import com.velocitypowered.proxy.protocol.packet.config.ClientboundServerLinksPacket;
import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket;
import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket; import com.velocitypowered.proxy.protocol.packet.title.GenericTitlePacket;
import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput; import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput;
@ -806,7 +810,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
}, connection.eventLoop()); }, connection.eventLoop());
} else if (event.getResult() instanceof final Notify res) { } else if (event.getResult() instanceof final Notify res) {
if (event.kickedDuringServerConnect() && previousConnection != null) { if (event.kickedDuringServerConnect() && previousConnection != null) {
sendMessage(Identity.nil(), res.getMessageComponent()); sendMessage(res.getMessageComponent());
} else { } else {
disconnect(res.getMessageComponent()); disconnect(res.getMessageComponent());
} }
@ -1057,6 +1061,22 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
}, connection.eventLoop()); }, connection.eventLoop());
} }
@Override
public void setServerLinks(final @NotNull List<ServerLink> links) {
Preconditions.checkNotNull(links, "links");
Preconditions.checkArgument(
this.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_21),
"Player version must be at least 1.21 to be able to set server links");
if (connection.getState() != StateRegistry.PLAY
&& connection.getState() != StateRegistry.CONFIG) {
throw new IllegalStateException("Can only send server links in CONFIGURATION or PLAY protocol");
}
connection.write(new ClientboundServerLinksPacket(List.copyOf(links).stream()
.map(l -> new ClientboundServerLinksPacket.ServerLink(l, getProtocolVersion())).toList()));
}
@Override @Override
public void addCustomChatCompletions(@NotNull Collection<String> completions) { public void addCustomChatCompletions(@NotNull Collection<String> completions) {
Preconditions.checkNotNull(completions, "completions"); Preconditions.checkNotNull(completions, "completions");
@ -1227,8 +1247,9 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
.get(MinecraftEncoder.class).setState(StateRegistry.CONFIG); .get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
// Make sure we don't send any play packets to the player after update start // Make sure we don't send any play packets to the player after update start
connection.addPlayPacketQueueHandler(); connection.addPlayPacketQueueHandler();
server.getEventManager().fireAndForget(new PlayerEnterConfigurationEvent(this, connectionInFlight));
}, connection.eventLoop()).exceptionally((ex) -> { }, connection.eventLoop()).exceptionally((ex) -> {
logger.error("Error switching player connection to config state:", ex); logger.error("Error switching player connection to config state", ex);
return null; return null;
}); });
} }
@ -1363,24 +1384,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
} }
switch (status.getStatus()) { switch (status.getStatus()) {
case ALREADY_CONNECTED: case ALREADY_CONNECTED -> sendMessage(ConnectionMessages.ALREADY_CONNECTED);
sendMessage(Identity.nil(), ConnectionMessages.ALREADY_CONNECTED); case CONNECTION_IN_PROGRESS -> sendMessage(ConnectionMessages.IN_PROGRESS);
break; case CONNECTION_CANCELLED -> {
case CONNECTION_IN_PROGRESS:
sendMessage(Identity.nil(), ConnectionMessages.IN_PROGRESS);
break;
case CONNECTION_CANCELLED:
// Ignored; the plugin probably already handled this. // Ignored; the plugin probably already handled this.
break; }
case SERVER_DISCONNECTED: case SERVER_DISCONNECTED -> {
Component reason = status.getReasonComponent() final Component reason = status.getReasonComponent()
.orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR); .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
handleConnectionException(toConnect, handleConnectionException(toConnect,
DisconnectPacket.create(reason, getProtocolVersion(), connection.getState()), status.isSafe()); DisconnectPacket.create(reason, getProtocolVersion(), connection.getState()), status.isSafe());
break; }
default: default -> {
// The only remaining value is successful (no need to do anything!) // The only remaining value is successful (no need to do anything!)
break; }
} }
}, connection.eventLoop()).thenApply(Result::isSuccessful); }, connection.eventLoop()).thenApply(Result::isSuccessful);
} }

Datei anzeigen

@ -15,10 +15,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.velocitypowered.proxy.connection.client; package com.velocitypowered.proxy.connection.player.bundle;
import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;

Datei anzeigen

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.velocitypowered.proxy.connection.player; package com.velocitypowered.proxy.connection.player.resourcepack;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;

Datei anzeigen

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.velocitypowered.proxy.connection.player.resourcepack; package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;

Datei anzeigen

@ -15,13 +15,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.velocitypowered.proxy.connection.player.resourcepack; package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent; import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;

Datei anzeigen

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.velocitypowered.proxy.connection.player.resourcepack; package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps; import com.google.common.collect.Multimaps;
@ -23,6 +23,7 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;

Datei anzeigen

@ -15,14 +15,15 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.velocitypowered.proxy.connection.player.resourcepack; package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
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.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket; import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket;
import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;

Datei anzeigen

@ -35,7 +35,8 @@ public class Connections {
public static final String MINECRAFT_DECODER = "minecraft-decoder"; public static final String MINECRAFT_DECODER = "minecraft-decoder";
public static final String MINECRAFT_ENCODER = "minecraft-encoder"; public static final String MINECRAFT_ENCODER = "minecraft-encoder";
public static final String READ_TIMEOUT = "read-timeout"; public static final String READ_TIMEOUT = "read-timeout";
public static final String PLAY_PACKET_QUEUE = "play-packet-queue"; public static final String PLAY_PACKET_QUEUE_OUTBOUND = "play-packet-queue-outbound";
public static final String PLAY_PACKET_QUEUE_INBOUND = "play-packet-queue-inbound";
private Connections() { private Connections() {
throw new AssertionError(); throw new AssertionError();

Datei anzeigen

@ -56,8 +56,7 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) { if (msg instanceof ByteBuf buf) {
ByteBuf buf = (ByteBuf) msg;
tryDecode(ctx, buf); tryDecode(ctx, buf);
} else { } else {
ctx.fireChannelRead(msg); ctx.fireChannelRead(msg);
@ -147,4 +146,8 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
this.state = state; this.state = state;
this.setProtocolVersion(registry.version); this.setProtocolVersion(registry.version);
} }
public ProtocolUtils.Direction getDirection() {
return direction;
}
} }

Datei anzeigen

@ -0,0 +1,94 @@
/*
* Copyright (C) 2023 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.netty;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.StateRegistry;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.PlatformDependent;
import java.util.Queue;
import org.jetbrains.annotations.NotNull;
/**
* Queues up any pending PLAY packets while the client is in the CONFIG state.
*
* <p>Much of the Velocity API (i.e. chat messages) utilize PLAY packets, however the client is
* incapable of receiving these packets during the CONFIG state. Certain events such as the
* ServerPreConnectEvent may be called during this time, and we need to ensure that any API that
* uses these packets will work as expected.
*
* <p>This handler will queue up any packets that are sent to the client during this time, and send
* them once the client has (re)entered the PLAY state.
*/
public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler {
private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
private final Queue<Object> queue = PlatformDependent.newMpscQueue();
/**
* Provides registries for client &amp; server bound packets.
*
* @param version the protocol version
*/
public PlayPacketQueueInboundHandler(ProtocolVersion version, ProtocolUtils.Direction direction) {
this.registry = StateRegistry.CONFIG.getProtocolRegistry(direction, version);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof final MinecraftPacket packet) {
// If the packet exists in the CONFIG state, we want to always
// ensure that it gets handled by the current handler
if (this.registry.containsPacket(packet)) {
ctx.fireChannelRead(msg);
return;
}
}
// Otherwise, queue the packet
this.queue.offer(msg);
}
@Override
public void channelInactive(@NotNull ChannelHandlerContext ctx) throws Exception {
this.releaseQueue(ctx, false);
super.channelInactive(ctx);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
this.releaseQueue(ctx, ctx.channel().isActive());
}
private void releaseQueue(ChannelHandlerContext ctx, boolean active) {
// Handle all the queued packets
Object msg;
while ((msg = this.queue.poll()) != null) {
if (active) {
ctx.fireChannelRead(msg);
} else {
ReferenceCountUtil.release(msg);
}
}
}
}

Datei anzeigen

@ -40,7 +40,7 @@ import org.jetbrains.annotations.NotNull;
* <p>This handler will queue up any packets that are sent to the client during this time, and send * <p>This handler will queue up any packets that are sent to the client during this time, and send
* them once the client has (re)entered the PLAY state. * them once the client has (re)entered the PLAY state.
*/ */
public class PlayPacketQueueHandler extends ChannelDuplexHandler { public class PlayPacketQueueOutboundHandler extends ChannelDuplexHandler {
private final StateRegistry.PacketRegistry.ProtocolRegistry registry; private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
private final Queue<MinecraftPacket> queue = PlatformDependent.newMpscQueue(); private final Queue<MinecraftPacket> queue = PlatformDependent.newMpscQueue();
@ -50,28 +50,26 @@ public class PlayPacketQueueHandler extends ChannelDuplexHandler {
* *
* @param version the protocol version * @param version the protocol version
*/ */
public PlayPacketQueueHandler(ProtocolVersion version, ProtocolUtils.Direction direction) { public PlayPacketQueueOutboundHandler(ProtocolVersion version, ProtocolUtils.Direction direction) {
this.registry = this.registry = StateRegistry.CONFIG.getProtocolRegistry(direction, version);
StateRegistry.CONFIG.getProtocolRegistry(direction, version);
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
throws Exception { if (!(msg instanceof final MinecraftPacket packet)) {
if (!(msg instanceof MinecraftPacket)) {
ctx.write(msg, promise); ctx.write(msg, promise);
return; return;
} }
// If the packet exists in the CONFIG state, we want to always // If the packet exists in the CONFIG state, we want to always
// ensure that it gets sent out to the client // ensure that it gets sent out to the client
if (this.registry.containsPacket(((MinecraftPacket) msg))) { if (this.registry.containsPacket(packet)) {
ctx.write(msg, promise); ctx.write(msg, promise);
return; return;
} }
// Otherwise, queue the packet // Otherwise, queue the packet
this.queue.offer((MinecraftPacket) msg); this.queue.offer(packet);
} }
@Override @Override
@ -87,10 +85,6 @@ public class PlayPacketQueueHandler extends ChannelDuplexHandler {
} }
private void releaseQueue(ChannelHandlerContext ctx, boolean active) { private void releaseQueue(ChannelHandlerContext ctx, boolean active) {
if (this.queue.isEmpty()) {
return;
}
// Send out all the queued packets // Send out all the queued packets
MinecraftPacket packet; MinecraftPacket packet;
while ((packet = this.queue.poll()) != null) { while ((packet = this.queue.poll()) != null) {

Datei anzeigen

@ -21,7 +21,7 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction;

Datei anzeigen

@ -18,6 +18,7 @@
package com.velocitypowered.proxy.protocol.packet.config; package com.velocitypowered.proxy.protocol.packet.config;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.ServerLink;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
@ -66,6 +67,13 @@ 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));