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:
Commit
bb3d3b0729
69
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normale Datei
69
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normale Datei
@ -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
10
.github/ISSUE_TEMPLATE/config.yml
vendored
Normale Datei
@ -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
48
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normale Datei
@ -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).
|
@ -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) {
|
||||||
|
}
|
@ -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) {
|
||||||
|
}
|
@ -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) {
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
101
api/src/main/java/com/velocitypowered/api/util/ServerLink.java
Normale Datei
101
api/src/main/java/com/velocitypowered/api/util/ServerLink.java
Normale Datei
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 & 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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) {
|
@ -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;
|
||||||
|
@ -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));
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren