13
0
geforkt von Mirrors/Velocity

Improved PluginMessaging API (#1254)

* Improved PluginMessaging API by adding a method to easily encode the data to be sent

* Added javadocs

* Implement PluginMessageEncoder
Dieser Commit ist enthalten in:
Adrian 2024-04-08 12:24:20 -05:00 committet von GitHub
Ursprung a72aef526b
Commit 9e42049a67
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
7 geänderte Dateien mit 168 neuen und 15 gelöschten Zeilen

Datei anzeigen

@ -13,6 +13,7 @@ import com.velocitypowered.api.proxy.crypto.KeyIdentifiable;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink; import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource; import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.messages.PluginMessageEncoder;
import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.player.TabList; import com.velocitypowered.api.proxy.player.TabList;
@ -282,16 +283,42 @@ public interface Player extends
@NotNull Collection<ResourcePackInfo> getPendingResourcePacks(); @NotNull Collection<ResourcePackInfo> getPendingResourcePacks();
/** /**
* <strong>Note that this method does not send a plugin message to the server the player * {@inheritDoc}
*
* <p><strong>Note that this method does not send a plugin message to the server the player
* is connected to.</strong> You should only use this method if you are trying to communicate * is connected to.</strong> You should only use this method if you are trying to communicate
* with a mod that is installed on the player's client. To send a plugin message to the server * with a mod that is installed on the player's client.</p>
*
* <p>To send a plugin message to the server
* from the player, you should use the equivalent method on the instance returned by * from the player, you should use the equivalent method on the instance returned by
* {@link #getCurrentServer()}. * {@link #getCurrentServer()}.
* *
* @inheritDoc * <pre>
* final ChannelIdentifier identifier;
* final Player player;
* player.getCurrentServer()
* .map(ServerConnection::getServer)
* .ifPresent((RegisteredServer server) -> {
* server.sendPluginMessage(identifier, data);
* });
* </pre>
*
*/ */
@Override @Override
boolean sendPluginMessage(@NotNull ChannelIdentifier identifier, byte @NotNull[] data); boolean sendPluginMessage(@NotNull ChannelIdentifier identifier, byte @NotNull [] data);
/**
* {@inheritDoc}
* <p><strong>Note that this method does not send a plugin message to the server the player
* is connected to.</strong> You should only use this method if you are trying to communicate
* with a mod that is installed on the player's client.</p>
*
* <p>To send a plugin message to the server
* from the player, you should use the equivalent method on the instance returned by
* {@link #getCurrentServer()}.
*/
@Override
boolean sendPluginMessage(@NotNull ChannelIdentifier identifier, @NotNull PluginMessageEncoder dataEncoder);
@Override @Override
default @NotNull Key key() { default @NotNull Key key() {

Datei anzeigen

@ -7,6 +7,9 @@
package com.velocitypowered.api.proxy.messages; package com.velocitypowered.api.proxy.messages;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/** /**
* Represents something that can be sent plugin messages. * Represents something that can be sent plugin messages.
*/ */
@ -19,5 +22,26 @@ public interface ChannelMessageSink {
* @param data the data to send * @param data the data to send
* @return whether or not the message could be sent * @return whether or not the message could be sent
*/ */
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data); boolean sendPluginMessage(@NotNull ChannelIdentifier identifier, byte @NotNull[] data);
/**
* Sends a plugin message to this target.
*
* <pre>
* final ChannelMessageSink target;
* final ChannelIdentifier identifier;
* final boolean result = target.sendPluginMessage(identifier, (output) -> {
* output.writeUTF("some input");
* output.writeInt(1);
* });
* </pre>
*
* @param identifier the channel identifier to send the message on
* @param dataEncoder the encoder of the data to be sent
* @return whether the message could be sent
*/
@ApiStatus.Experimental
boolean sendPluginMessage(
@NotNull ChannelIdentifier identifier,
@NotNull PluginMessageEncoder dataEncoder);
} }

Datei anzeigen

@ -0,0 +1,29 @@
/*
* 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.proxy.messages;
import com.google.common.io.ByteArrayDataOutput;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* A data encoder to be sent via a plugin message.
*
* @since 3.3.0
*/
@FunctionalInterface
@ApiStatus.Experimental
public interface PluginMessageEncoder {
/**
* Encodes data into a {@link ByteArrayDataOutput} to be transmitted by plugin messages.
*
* @param output the {@link ByteArrayDataOutput} provided
*/
void encode(@NotNull ByteArrayDataOutput output);
}

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.provider; package com.velocitypowered.proxy.adventure;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import com.velocitypowered.proxy.util.TranslatableMapper; import com.velocitypowered.proxy.util.TranslatableMapper;

Datei anzeigen

@ -19,11 +19,13 @@ package com.velocitypowered.proxy.connection.backend;
import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN; import static com.velocitypowered.proxy.connection.forge.legacy.LegacyForgeConstants.HANDSHAKE_HOSTNAME_TOKEN;
import static com.velocitypowered.proxy.network.Connections.HANDLER; import static com.velocitypowered.proxy.network.Connections.HANDLER;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginMessageEncoder;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.VelocityServer;
@ -41,6 +43,7 @@ import com.velocitypowered.proxy.protocol.packet.HandshakePacket;
import com.velocitypowered.proxy.protocol.packet.JoinGamePacket; import com.velocitypowered.proxy.protocol.packet.JoinGamePacket;
import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket;
import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket; import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket;
import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput;
import com.velocitypowered.proxy.server.VelocityRegisteredServer; import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@ -52,6 +55,7 @@ import java.util.concurrent.CompletableFuture;
import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
/** /**
* Handles a connection from the proxy to some backend server. * Handles a connection from the proxy to some backend server.
@ -255,10 +259,31 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
} }
@Override @Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { public boolean sendPluginMessage(
final @NotNull ChannelIdentifier identifier,
final byte @NotNull [] data
) {
return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data)); return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data));
} }
@Override
public boolean sendPluginMessage(
final @NotNull ChannelIdentifier identifier,
final @NotNull PluginMessageEncoder dataEncoder
) {
requireNonNull(identifier);
requireNonNull(dataEncoder);
final ByteBuf buf = Unpooled.buffer();
final ByteBufDataOutput dataOutput = new ByteBufDataOutput(buf);
dataEncoder.encode(dataOutput);
if (buf.isReadable()) {
return sendPluginMessage(identifier, buf);
} else {
buf.release();
return false;
}
}
/** /**
* Sends a plugin message to the server through this connection. * Sends a plugin message to the server through this connection.
* *
@ -270,9 +295,9 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation,
Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data"); Preconditions.checkNotNull(data, "data");
MinecraftConnection mc = ensureConnected(); final MinecraftConnection mc = ensureConnected();
PluginMessagePacket message = new PluginMessagePacket(identifier.getId(), data); final PluginMessagePacket message = new PluginMessagePacket(identifier.getId(), data);
mc.write(message); mc.write(message);
return true; return true;
} }

Datei anzeigen

@ -19,6 +19,7 @@ package com.velocitypowered.proxy.connection.client;
import static com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status.ALREADY_CONNECTED; import static com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status.ALREADY_CONNECTED;
import static com.velocitypowered.proxy.connection.util.ConnectionRequestResults.plainResult; import static com.velocitypowered.proxy.connection.util.ConnectionRequestResults.plainResult;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.concurrent.CompletableFuture.completedFuture;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@ -44,6 +45,7 @@ import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.crypto.IdentifiedKey; import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
import com.velocitypowered.api.proxy.crypto.KeyIdentifiable; import com.velocitypowered.api.proxy.crypto.KeyIdentifiable;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginMessageEncoder;
import com.velocitypowered.api.proxy.player.PlayerSettings; import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
@ -75,6 +77,7 @@ 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.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.server.VelocityRegisteredServer; import com.velocitypowered.proxy.server.VelocityRegisteredServer;
import com.velocitypowered.proxy.tablist.InternalTabList; import com.velocitypowered.proxy.tablist.InternalTabList;
import com.velocitypowered.proxy.tablist.KeyedVelocityTabList; import com.velocitypowered.proxy.tablist.KeyedVelocityTabList;
@ -83,6 +86,7 @@ import com.velocitypowered.proxy.tablist.VelocityTabListLegacy;
import com.velocitypowered.proxy.util.ClosestLocaleMatcher; import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
import com.velocitypowered.proxy.util.DurationUtils; import com.velocitypowered.proxy.util.DurationUtils;
import com.velocitypowered.proxy.util.TranslatableMapper; import com.velocitypowered.proxy.util.TranslatableMapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.Collection; import java.util.Collection;
@ -947,12 +951,32 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
public boolean sendPluginMessage(@NotNull ChannelIdentifier identifier, byte @NotNull [] data) { public boolean sendPluginMessage(@NotNull ChannelIdentifier identifier, byte @NotNull [] data) {
Preconditions.checkNotNull(identifier, "identifier"); Preconditions.checkNotNull(identifier, "identifier");
Preconditions.checkNotNull(data, "data"); Preconditions.checkNotNull(data, "data");
PluginMessagePacket message = new PluginMessagePacket(identifier.getId(), final PluginMessagePacket message = new PluginMessagePacket(identifier.getId(),
Unpooled.wrappedBuffer(data)); Unpooled.wrappedBuffer(data));
connection.write(message); connection.write(message);
return true; return true;
} }
@Override
public boolean sendPluginMessage(
final @NotNull ChannelIdentifier identifier,
final @NotNull PluginMessageEncoder dataEncoder
) {
requireNonNull(identifier);
requireNonNull(dataEncoder);
final ByteBuf buf = Unpooled.buffer();
final ByteBufDataOutput dataOutput = new ByteBufDataOutput(buf);
dataEncoder.encode(dataOutput);
if (buf.isReadable()) {
final PluginMessagePacket message = new PluginMessagePacket(identifier.getId(), buf);
connection.write(message);
return true;
} else {
buf.release();
return false;
}
}
@Override @Override
@Nullable @Nullable
public String getClientBrand() { public String getClientBrand() {

Datei anzeigen

@ -23,11 +23,13 @@ import static com.velocitypowered.proxy.network.Connections.HANDLER;
import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER; import static com.velocitypowered.proxy.network.Connections.MINECRAFT_DECODER;
import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER; import static com.velocitypowered.proxy.network.Connections.MINECRAFT_ENCODER;
import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT; import static com.velocitypowered.proxy.network.Connections.READ_TIMEOUT;
import static java.util.Objects.requireNonNull;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginMessageEncoder;
import com.velocitypowered.api.proxy.server.PingOptions; import com.velocitypowered.api.proxy.server.PingOptions;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
@ -42,6 +44,7 @@ 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.MinecraftVarintFrameDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintFrameDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
import com.velocitypowered.proxy.protocol.util.ByteBufDataOutput;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
@ -59,6 +62,7 @@ import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience; import net.kyori.adventure.audience.ForwardingAudience;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.jetbrains.annotations.NotNull;
/** /**
* Represents a server registered on the proxy. * Represents a server registered on the proxy.
@ -107,9 +111,9 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
throw new IllegalStateException("No Velocity proxy instance available"); throw new IllegalStateException("No Velocity proxy instance available");
} }
CompletableFuture<ServerPing> pingFuture = new CompletableFuture<>(); CompletableFuture<ServerPing> pingFuture = new CompletableFuture<>();
server.createBootstrap(loop).handler(new ChannelInitializer<Channel>() { server.createBootstrap(loop).handler(new ChannelInitializer<>() {
@Override @Override
protected void initChannel(Channel ch) throws Exception { protected void initChannel(Channel ch) {
ch.pipeline().addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder()) ch.pipeline().addLast(FRAME_DECODER, new MinecraftVarintFrameDecoder())
.addLast(READ_TIMEOUT, new ReadTimeoutHandler( .addLast(READ_TIMEOUT, new ReadTimeoutHandler(
pingOptions.getTimeout() == 0 pingOptions.getTimeout() == 0
@ -143,10 +147,30 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
} }
@Override @Override
public boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data) { public boolean sendPluginMessage(final @NotNull ChannelIdentifier identifier, final byte @NotNull [] data) {
requireNonNull(identifier);
requireNonNull(data);
return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data)); return sendPluginMessage(identifier, Unpooled.wrappedBuffer(data));
} }
@Override
public boolean sendPluginMessage(
final @NotNull ChannelIdentifier identifier,
final @NotNull PluginMessageEncoder dataEncoder
) {
requireNonNull(identifier);
requireNonNull(dataEncoder);
final ByteBuf buf = Unpooled.buffer();
final ByteBufDataOutput dataInput = new ByteBufDataOutput(buf);
dataEncoder.encode(dataInput);
if (buf.isReadable()) {
return sendPluginMessage(identifier, buf);
} else {
buf.release();
return false;
}
}
/** /**
* Sends a plugin message to the server through this connection. The message will be released * Sends a plugin message to the server through this connection. The message will be released
* afterwards. * afterwards.
@ -156,8 +180,8 @@ public class VelocityRegisteredServer implements RegisteredServer, ForwardingAud
* @return whether or not the message was sent * @return whether or not the message was sent
*/ */
public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) { public boolean sendPluginMessage(ChannelIdentifier identifier, ByteBuf data) {
for (ConnectedPlayer player : players.values()) { for (final ConnectedPlayer player : players.values()) {
VelocityServerConnection serverConnection = player.getConnectedServer(); final VelocityServerConnection serverConnection = player.getConnectedServer();
if (serverConnection != null && serverConnection.getConnection() != null if (serverConnection != null && serverConnection.getConnection() != null
&& serverConnection.getServer() == this) { && serverConnection.getServer() == this) {
return serverConnection.sendPluginMessage(identifier, data); return serverConnection.sendPluginMessage(identifier, data);