Further improvements.

The main component here is the total revamp of the plugin channel identifier system - instead of Legacy/Modern channel IDs, you can have a modern channel or a modern channel paired with a legacy channel, which is much less confusing to work with.
This commit is contained in:
Andrew Steinborn
2021-04-17 06:04:12 -04:00
parent 0ed8352012
commit 9645fb59da
32 changed files with 307 additions and 404 deletions

View File

@@ -11,9 +11,9 @@ import com.google.common.io.ByteArrayDataInput;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.connection.Player;
import com.velocitypowered.api.proxy.connection.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import java.io.ByteArrayInputStream;
/**
@@ -26,7 +26,7 @@ public interface PluginMessageEvent extends ResultedEvent<PluginMessageEvent.For
ChannelMessageSink sink();
ChannelIdentifier channel();
PluginChannelId channel();
byte[] rawMessage();

View File

@@ -12,9 +12,9 @@ import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import com.velocitypowered.api.proxy.connection.Player;
import com.velocitypowered.api.proxy.connection.ServerConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
@@ -26,7 +26,7 @@ public final class PluginMessageEventImpl implements PluginMessageEvent {
private final ChannelMessageSource source;
private final ChannelMessageSink target;
private final ChannelIdentifier identifier;
private final PluginChannelId identifier;
private final byte[] data;
private ForwardResult result;
@@ -39,7 +39,7 @@ public final class PluginMessageEventImpl implements PluginMessageEvent {
* @param data the payload of the plugin message
*/
public PluginMessageEventImpl(ChannelMessageSource source, ChannelMessageSink target,
ChannelIdentifier identifier, byte[] data) {
PluginChannelId identifier, byte[] data) {
this.source = Preconditions.checkNotNull(source, "source");
this.target = Preconditions.checkNotNull(target, "target");
this.identifier = Preconditions.checkNotNull(identifier, "identifier");
@@ -68,7 +68,7 @@ public final class PluginMessageEventImpl implements PluginMessageEvent {
}
@Override
public ChannelIdentifier channel() {
public PluginChannelId channel() {
return identifier;
}

View File

@@ -8,7 +8,7 @@
package com.velocitypowered.api.event.player;
import com.velocitypowered.api.proxy.connection.Player;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import java.util.List;
/**
@@ -19,5 +19,5 @@ public interface PlayerChannelRegisterEvent {
Player player();
List<ChannelIdentifier> channels();
List<PluginChannelId> channels();
}

View File

@@ -9,7 +9,7 @@ package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.connection.Player;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.PluginChannelId;
import java.util.List;
/**
@@ -19,9 +19,9 @@ import java.util.List;
public final class PlayerChannelRegisterEventImpl implements PlayerChannelRegisterEvent {
private final Player player;
private final List<ChannelIdentifier> channels;
private final List<PluginChannelId> channels;
public PlayerChannelRegisterEventImpl(Player player, List<ChannelIdentifier> channels) {
public PlayerChannelRegisterEventImpl(Player player, List<PluginChannelId> channels) {
this.player = Preconditions.checkNotNull(player, "player");
this.channels = Preconditions.checkNotNull(channels, "channels");
}
@@ -32,7 +32,7 @@ public final class PlayerChannelRegisterEventImpl implements PlayerChannelRegist
}
@Override
public List<ChannelIdentifier> channels() {
public List<PluginChannelId> channels() {
return channels;
}

View File

@@ -39,7 +39,7 @@ public interface PluginManager {
*
* @return the plugins
*/
Collection<PluginContainer> getPlugins();
Collection<PluginContainer> plugins();
/**
* Checks if a plugin is loaded based on its ID.

View File

@@ -1,21 +0,0 @@
/*
* Copyright (C) 2018 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;
/**
* Represents a channel identifier for use with plugin messaging.
*/
public interface ChannelIdentifier {
/**
* Returns the textual representation of this identifier.
*
* @return the textual representation of the identifier
*/
String id();
}

View File

@@ -19,5 +19,5 @@ public interface ChannelMessageSink {
* @param data the data to send
* @return whether or not the message could be sent
*/
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data);
boolean sendPluginMessage(PluginChannelId identifier, byte[] data);
}

View File

@@ -10,7 +10,7 @@ package com.velocitypowered.api.proxy.messages;
import com.velocitypowered.api.event.connection.PluginMessageEventImpl;
/**
* Represents an interface to register and unregister {@link ChannelIdentifier}s for the proxy to
* Represents an interface to register and unregister {@link PluginChannelId}s for the proxy to
* listen on.
*/
public interface ChannelRegistrar {
@@ -21,12 +21,12 @@ public interface ChannelRegistrar {
*
* @param identifiers the channel identifiers to register
*/
void register(ChannelIdentifier... identifiers);
void register(PluginChannelId... identifiers);
/**
* Removes the intent to listen for the specified channel.
*
* @param identifiers the identifiers to unregister
*/
void unregister(ChannelIdentifier... identifiers);
void unregister(PluginChannelId... identifiers);
}

View File

@@ -1,64 +0,0 @@
/*
* Copyright (C) 2018 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.base.Preconditions;
import com.google.common.base.Strings;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Reperesents a legacy channel identifier (for Minecraft 1.12 and below). For modern 1.13 plugin
* messages, please see {@link MinecraftChannelIdentifier}. This class is immutable and safe for
* multi-threaded use.
*/
public final class LegacyChannelIdentifier implements ChannelIdentifier {
private final String name;
/**
* Creates a new legacy channel identifier.
*
* @param name the name for the channel
*/
public LegacyChannelIdentifier(String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "provided name is empty");
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name + " (legacy)";
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LegacyChannelIdentifier that = (LegacyChannelIdentifier) o;
return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String id() {
return this.getName();
}
}

View File

@@ -1,114 +0,0 @@
/*
* Copyright (C) 2018 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.base.Preconditions;
import com.google.common.base.Strings;
import java.util.Objects;
import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents a Minecraft 1.13+ channel identifier. This class is immutable and safe for
* multi-threaded use.
*/
public final class MinecraftChannelIdentifier implements ChannelIdentifier {
private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9/\\-_]*");
private final String namespace;
private final String name;
private MinecraftChannelIdentifier(String namespace, String name) {
this.namespace = namespace;
this.name = name;
}
/**
* Creates an identifier in the default namespace ({@code minecraft}). Plugins are strongly
* encouraged to provide their own namespace.
*
* @param name the name in the default namespace to use
* @return a new channel identifier
*/
public static MinecraftChannelIdentifier forDefaultNamespace(String name) {
return new MinecraftChannelIdentifier("minecraft", name);
}
/**
* Creates an identifier in the specified namespace.
*
* @param namespace the namespace to use
* @param name the channel name inside the specified namespace
* @return a new channel identifier
*/
public static MinecraftChannelIdentifier create(String namespace, String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(namespace), "namespace is null or empty");
Preconditions.checkArgument(name != null, "namespace is null or empty");
Preconditions.checkArgument(VALID_IDENTIFIER_REGEX.matcher(namespace).matches(),
"namespace is not valid");
Preconditions
.checkArgument(VALID_IDENTIFIER_REGEX.matcher(name).matches(), "name is not valid");
return new MinecraftChannelIdentifier(namespace, name);
}
/**
* Creates an channel identifier from the specified Minecraft identifier.
*
* @param identifier the Minecraft identifier
* @return a new channel identifier
*/
public static MinecraftChannelIdentifier from(String identifier) {
int colonPos = identifier.indexOf(':');
if (colonPos == -1) {
throw new IllegalArgumentException("Identifier does not contain a colon.");
}
if (colonPos + 1 == identifier.length()) {
throw new IllegalArgumentException("Identifier is empty.");
}
String namespace = identifier.substring(0, colonPos);
String name = identifier.substring(colonPos + 1);
return create(namespace, name);
}
public String getNamespace() {
return namespace;
}
public String getName() {
return name;
}
@Override
public String toString() {
return namespace + ":" + name + " (modern)";
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MinecraftChannelIdentifier that = (MinecraftChannelIdentifier) o;
return Objects.equals(namespace, that.namespace)
&& Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(namespace, name);
}
@Override
public String id() {
return namespace + ":" + name;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 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.base.Preconditions;
import com.google.common.base.Strings;
import java.util.Objects;
import java.util.regex.Pattern;
import net.kyori.adventure.key.Key;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents a Minecraft 1.13+ channel identifier.
*/
public final class MinecraftPluginChannelId implements PluginChannelId {
private final Key key;
MinecraftPluginChannelId(Key key) {
this.key = Preconditions.checkNotNull(key, "key");
}
public Key key() {
return key;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MinecraftPluginChannelId that = (MinecraftPluginChannelId) o;
return key.equals(that.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
@Override
public String toString() {
return key.asString();
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2018 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.base.Preconditions;
import com.google.common.base.Strings;
import net.kyori.adventure.key.Key;
/**
* Reperesents a legacy channel identifier (for Minecraft 1.12 and below). For modern 1.13 plugin
* messages, please see {@link MinecraftPluginChannelId}. This class is immutable and safe for
* multi-threaded use.
*/
public final class PairedPluginChannelId implements PluginChannelId {
private final String legacyChannel;
private final Key modernChannelKey;
/**
* Creates a new legacy channel identifier.
*
* @param legacyChannel the name for the legacy channel name
* @param modernChannelKey the modern channel key to use
*/
PairedPluginChannelId(String legacyChannel, Key modernChannelKey) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(legacyChannel), "provided name is empty");
this.legacyChannel = legacyChannel;
this.modernChannelKey = Preconditions.checkNotNull(modernChannelKey, "modernChannelKey");
}
public String legacyChannel() {
return legacyChannel;
}
public Key modernChannelKey() {
return modernChannelKey;
}
@Override
public String toString() {
return legacyChannel + "/" + modernChannelKey.asString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PairedPluginChannelId that = (PairedPluginChannelId) o;
if (!legacyChannel.equals(that.legacyChannel)) {
return false;
}
return modernChannelKey.equals(that.modernChannelKey);
}
@Override
public int hashCode() {
int result = legacyChannel.hashCode();
result = 31 * result + modernChannelKey.hashCode();
return result;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 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 net.kyori.adventure.key.Key;
/**
* Represents a channel identifier for use with plugin messaging.
*/
public interface PluginChannelId {
/**
* Wraps the specified Minecraft key so it can be used as a {@link PluginChannelId}.
* If the client is connected using Minecraft 1.12.2 or earlier, use the key as the
* channel name.
*
* @param key the key instance to wrap
* @return a wrapped plugin channel ID
*/
static MinecraftPluginChannelId wrap(Key key) {
return new MinecraftPluginChannelId(key);
}
/**
* Wraps the specified Minecraft key so it can be used as a {@link PluginChannelId},
* with the specified {@code legacyChannel} for clients onnected using Minecraft 1.12.2
* or earlier.
*
* @param legacyChannel the legacy channel name
* @param modernChannelKey the key instance to wrap
* @return a wrapped plugin channel ID
*/
static PairedPluginChannelId withLegacy(String legacyChannel, Key modernChannelKey) {
return new PairedPluginChannelId(legacyChannel, modernChannelKey);
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (C) 2018 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 static com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier.create;
import static com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier.from;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class MinecraftChannelIdentifierTest {
@Test
void createAllowsValidNamespaces() {
create("minecraft", "brand");
}
@Test
void createAllowsEmptyName() {
create("minecraft", "");
}
@Test
void createDisallowsNull() {
assertAll(
() -> assertThrows(IllegalArgumentException.class, () -> create(null, "")),
() -> assertThrows(IllegalArgumentException.class, () -> create("", "")),
() -> assertThrows(IllegalArgumentException.class, () -> create("minecraft", null))
);
}
@Test
void fromIdentifierIsCorrect() {
MinecraftChannelIdentifier expected = MinecraftChannelIdentifier.create("velocity", "test");
assertEquals(expected, MinecraftChannelIdentifier.from("velocity:test"));
}
@Test
void createAllowsSlashes() {
create("velocity", "test/test2");
}
@Test
void fromIdentifierThrowsOnBadValues() {
assertAll(
() -> assertThrows(IllegalArgumentException.class, () -> from("")),
() -> assertThrows(IllegalArgumentException.class, () -> from(":")),
() -> assertThrows(IllegalArgumentException.class, () -> from(":a")),
() -> assertThrows(IllegalArgumentException.class, () -> from("a:")),
() -> assertThrows(IllegalArgumentException.class, () -> from("hello:$$$$$$")),
() -> assertThrows(IllegalArgumentException.class, () -> from("hello::"))
);
}
}