3
0
Mirror von https://github.com/PaperMC/Velocity.git synchronisiert 2024-12-23 23:00:35 +01:00

Reformat with Google code style and enforce Checkstyle.

Fixes #125
Dieser Commit ist enthalten in:
Andrew Steinborn 2018-10-27 23:45:35 -04:00
Ursprung 53aa92db92
Commit 25b5e00125
208 geänderte Dateien mit 12851 neuen und 11620 gelöschten Zeilen

Datei anzeigen

@ -2,9 +2,11 @@ plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '2.0.4'
id 'maven-publish'
id 'checkstyle'
}
apply from: '../gradle/checkerframework.gradle'
apply from: '../gradle/checkstyle.gradle'
sourceSets {
ap {

Datei anzeigen

@ -3,7 +3,11 @@ package com.velocitypowered.api.plugin.ap;
import com.google.gson.Gson;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.PluginDescription;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Objects;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
@ -16,73 +20,76 @@ import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Objects;
import java.util.Set;
@SupportedAnnotationTypes({"com.velocitypowered.api.plugin.Plugin"})
public class PluginAnnotationProcessor extends AbstractProcessor {
private ProcessingEnvironment environment;
private String pluginClassFound;
private boolean warnedAboutMultiplePlugins;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
this.environment = processingEnv;
private ProcessingEnvironment environment;
private String pluginClassFound;
private boolean warnedAboutMultiplePlugins;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
this.environment = processingEnv;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
for (Element element : roundEnv.getElementsAnnotatedWith(Plugin.class)) {
if (element.getKind() != ElementKind.CLASS) {
environment.getMessager().printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated with "
+ Plugin.class.getCanonicalName());
return false;
}
Name qualifiedName = ((TypeElement) element).getQualifiedName();
if (Objects.equals(pluginClassFound, qualifiedName.toString())) {
if (!warnedAboutMultiplePlugins) {
environment.getMessager().printMessage(Diagnostic.Kind.WARNING, "Velocity does not yet currently support " +
"multiple plugins. We are using " + pluginClassFound + " for your plugin's main class.");
warnedAboutMultiplePlugins = true;
}
return false;
}
Plugin plugin = element.getAnnotation(Plugin.class);
if (!PluginDescription.ID_PATTERN.matcher(plugin.id()).matches()) {
environment.getMessager().printMessage(Diagnostic.Kind.ERROR, "Invalid ID for plugin "
+ qualifiedName + ". IDs must start alphabetically, have alphanumeric characters, and can " +
"contain dashes or underscores.");
return false;
}
// All good, generate the velocity-plugin.json.
SerializedPluginDescription description = SerializedPluginDescription.from(plugin, qualifiedName.toString());
try {
FileObject object = environment.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "velocity-plugin.json");
try (Writer writer = new BufferedWriter(object.openWriter())) {
new Gson().toJson(description, writer);
}
pluginClassFound = qualifiedName.toString();
} catch (IOException e) {
environment.getMessager().printMessage(Diagnostic.Kind.ERROR, "Unable to generate plugin file");
}
}
for (Element element : roundEnv.getElementsAnnotatedWith(Plugin.class)) {
if (element.getKind() != ElementKind.CLASS) {
environment.getMessager()
.printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated with "
+ Plugin.class.getCanonicalName());
return false;
}
Name qualifiedName = ((TypeElement) element).getQualifiedName();
if (Objects.equals(pluginClassFound, qualifiedName.toString())) {
if (!warnedAboutMultiplePlugins) {
environment.getMessager()
.printMessage(Diagnostic.Kind.WARNING, "Velocity does not yet currently support " +
"multiple plugins. We are using " + pluginClassFound
+ " for your plugin's main class.");
warnedAboutMultiplePlugins = true;
}
return false;
}
Plugin plugin = element.getAnnotation(Plugin.class);
if (!PluginDescription.ID_PATTERN.matcher(plugin.id()).matches()) {
environment.getMessager().printMessage(Diagnostic.Kind.ERROR, "Invalid ID for plugin "
+ qualifiedName
+ ". IDs must start alphabetically, have alphanumeric characters, and can " +
"contain dashes or underscores.");
return false;
}
// All good, generate the velocity-plugin.json.
SerializedPluginDescription description = SerializedPluginDescription
.from(plugin, qualifiedName.toString());
try {
FileObject object = environment.getFiler()
.createResource(StandardLocation.CLASS_OUTPUT, "", "velocity-plugin.json");
try (Writer writer = new BufferedWriter(object.openWriter())) {
new Gson().toJson(description, writer);
}
pluginClassFound = qualifiedName.toString();
} catch (IOException e) {
environment.getMessager()
.printMessage(Diagnostic.Kind.ERROR, "Unable to generate plugin file");
}
}
return false;
}
}

Datei anzeigen

@ -4,149 +4,162 @@ import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.Nullable;
public class SerializedPluginDescription {
// @Nullable is used here to make GSON skip these in the serialized file
private final String id;
private final @Nullable String name;
private final @Nullable String version;
private final @Nullable String description;
private final @Nullable String url;
private final @Nullable List<String> authors;
private final @Nullable List<Dependency> dependencies;
private final String main;
public SerializedPluginDescription(String id, String name, String version, String description, String url,
List<String> authors, List<Dependency> dependencies, String main) {
this.id = Preconditions.checkNotNull(id, "id");
this.name = Strings.emptyToNull(name);
this.version = Strings.emptyToNull(version);
this.description = Strings.emptyToNull(description);
this.url = Strings.emptyToNull(url);
this.authors = authors == null || authors.isEmpty() ? ImmutableList.of() : authors;
this.dependencies = dependencies == null || dependencies.isEmpty() ? ImmutableList.of() : dependencies;
this.main = Preconditions.checkNotNull(main, "main");
// @Nullable is used here to make GSON skip these in the serialized file
private final String id;
private final @Nullable String name;
private final @Nullable String version;
private final @Nullable String description;
private final @Nullable String url;
private final @Nullable List<String> authors;
private final @Nullable List<Dependency> dependencies;
private final String main;
public SerializedPluginDescription(String id, String name, String version, String description,
String url,
List<String> authors, List<Dependency> dependencies, String main) {
this.id = Preconditions.checkNotNull(id, "id");
this.name = Strings.emptyToNull(name);
this.version = Strings.emptyToNull(version);
this.description = Strings.emptyToNull(description);
this.url = Strings.emptyToNull(url);
this.authors = authors == null || authors.isEmpty() ? ImmutableList.of() : authors;
this.dependencies =
dependencies == null || dependencies.isEmpty() ? ImmutableList.of() : dependencies;
this.main = Preconditions.checkNotNull(main, "main");
}
public static SerializedPluginDescription from(Plugin plugin, String qualifiedName) {
List<Dependency> dependencies = new ArrayList<>();
for (com.velocitypowered.api.plugin.Dependency dependency : plugin.dependencies()) {
dependencies.add(new Dependency(dependency.id(), dependency.optional()));
}
return new SerializedPluginDescription(plugin.id(), plugin.name(), plugin.version(),
plugin.description(), plugin.url(),
Arrays.stream(plugin.authors()).filter(author -> !author.isEmpty())
.collect(Collectors.toList()), dependencies, qualifiedName);
}
public static SerializedPluginDescription from(Plugin plugin, String qualifiedName) {
List<Dependency> dependencies = new ArrayList<>();
for (com.velocitypowered.api.plugin.Dependency dependency : plugin.dependencies()) {
dependencies.add(new Dependency(dependency.id(), dependency.optional()));
}
return new SerializedPluginDescription(plugin.id(), plugin.name(), plugin.version(), plugin.description(), plugin.url(),
Arrays.stream(plugin.authors()).filter(author -> !author.isEmpty()).collect(Collectors.toList()), dependencies, qualifiedName);
public String getId() {
return id;
}
public @Nullable String getName() {
return name;
}
public @Nullable String getVersion() {
return version;
}
public @Nullable String getDescription() {
return description;
}
public @Nullable String getUrl() {
return url;
}
public List<String> getAuthors() {
return authors == null ? ImmutableList.of() : authors;
}
public List<Dependency> getDependencies() {
return dependencies == null ? ImmutableList.of() : dependencies;
}
public String getMain() {
return main;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SerializedPluginDescription that = (SerializedPluginDescription) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
Objects.equals(version, that.version) &&
Objects.equals(description, that.description) &&
Objects.equals(url, that.url) &&
Objects.equals(authors, that.authors) &&
Objects.equals(dependencies, that.dependencies) &&
Objects.equals(main, that.main);
}
@Override
public int hashCode() {
return Objects.hash(id, name, version, description, url, authors, dependencies);
}
@Override
public String toString() {
return "SerializedPluginDescription{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", version='" + version + '\'' +
", description='" + description + '\'' +
", url='" + url + '\'' +
", authors=" + authors +
", dependencies=" + dependencies +
", main='" + main + '\'' +
'}';
}
public static class Dependency {
private final String id;
private final boolean optional;
public Dependency(String id, boolean optional) {
this.id = id;
this.optional = optional;
}
public String getId() {
return id;
return id;
}
public @Nullable String getName() {
return name;
}
public @Nullable String getVersion() {
return version;
}
public @Nullable String getDescription() {
return description;
}
public @Nullable String getUrl() {
return url;
}
public List<String> getAuthors() {
return authors == null ? ImmutableList.of() : authors;
}
public List<Dependency> getDependencies() {
return dependencies == null ? ImmutableList.of() : dependencies;
}
public String getMain() {
return main;
public boolean isOptional() {
return optional;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SerializedPluginDescription that = (SerializedPluginDescription) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
Objects.equals(version, that.version) &&
Objects.equals(description, that.description) &&
Objects.equals(url, that.url) &&
Objects.equals(authors, that.authors) &&
Objects.equals(dependencies, that.dependencies) &&
Objects.equals(main, that.main);
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Dependency that = (Dependency) o;
return optional == that.optional &&
Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id, name, version, description, url, authors, dependencies);
return Objects.hash(id, optional);
}
@Override
public String toString() {
return "SerializedPluginDescription{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", version='" + version + '\'' +
", description='" + description + '\'' +
", url='" + url + '\'' +
", authors=" + authors +
", dependencies=" + dependencies +
", main='" + main + '\'' +
'}';
}
public static class Dependency {
private final String id;
private final boolean optional;
public Dependency(String id, boolean optional) {
this.id = id;
this.optional = optional;
}
public String getId() {
return id;
}
public boolean isOptional() {
return optional;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Dependency that = (Dependency) o;
return optional == that.optional &&
Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id, optional);
}
@Override
public String toString() {
return "Dependency{" +
"id='" + id + '\'' +
", optional=" + optional +
'}';
}
return "Dependency{" +
"id='" + id + '\'' +
", optional=" + optional +
'}';
}
}
}

Datei anzeigen

@ -1,44 +1,46 @@
package com.velocitypowered.api.command;
import com.google.common.collect.ImmutableList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
/**
* Represents a command that can be executed by a {@link CommandSource}, such as a {@link com.velocitypowered.api.proxy.Player}
* or the console.
* Represents a command that can be executed by a {@link CommandSource}, such as a {@link
* com.velocitypowered.api.proxy.Player} or the console.
*/
public interface Command {
/**
* Executes the command for the specified {@link CommandSource}.
* @param source the source of this command
* @param args the arguments for this command
*/
void execute(CommandSource source, String @NonNull [] args);
/**
* Provides tab complete suggestions for a command for a specified {@link CommandSource}.
* @param source the source to run the command for
* @param currentArgs the current, partial arguments for this command
* @return tab complete suggestions
*/
default List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
return ImmutableList.of();
}
/**
* Executes the command for the specified {@link CommandSource}.
*
* @param source the source of this command
* @param args the arguments for this command
*/
void execute(CommandSource source, String @NonNull [] args);
/**
* Tests to check if the {@code source} has permission to use this command
* with the provided {@code args}.
*
* <p>If this method returns false, the handling will be forwarded onto
* the players current server.</p>
*
* @param source the source of the command
* @param args the arguments for this command
* @return whether the source has permission
*/
default boolean hasPermission(CommandSource source, String @NonNull [] args) {
return true;
}
/**
* Provides tab complete suggestions for a command for a specified {@link CommandSource}.
*
* @param source the source to run the command for
* @param currentArgs the current, partial arguments for this command
* @return tab complete suggestions
*/
default List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
return ImmutableList.of();
}
/**
* Tests to check if the {@code source} has permission to use this command with the provided
* {@code args}.
*
* <p>If this method returns false, the handling will be forwarded onto
* the players current server.</p>
*
* @param source the source of the command
* @param args the arguments for this command
* @return whether the source has permission
*/
default boolean hasPermission(CommandSource source, String @NonNull [] args) {
return true;
}
}

Datei anzeigen

@ -4,24 +4,28 @@ package com.velocitypowered.api.command;
* Represents an interface to register a command executor with the proxy.
*/
public interface CommandManager {
/**
* Registers the specified command with the manager with the specified aliases.
* @param command the command to register
* @param aliases the alias to use
*/
void register(Command command, String... aliases);
/**
* Unregisters a command.
* @param alias the command alias to unregister
*/
void unregister(String alias);
/**
* Registers the specified command with the manager with the specified aliases.
*
* @param command the command to register
* @param aliases the alias to use
*/
void register(Command command, String... aliases);
/**
* Attempts to execute a command from the specified {@code cmdLine}.
* @param source the command's source
* @param cmdLine the command to run
* @return true if the command was found and executed, false if it was not
*/
boolean execute(CommandSource source, String cmdLine);
/**
* Unregisters a command.
*
* @param alias the command alias to unregister
*/
void unregister(String alias);
/**
* Attempts to execute a command from the specified {@code cmdLine}.
*
* @param source the command's source
* @param cmdLine the command to run
* @return true if the command was found and executed, false if it was not
*/
boolean execute(CommandSource source, String cmdLine);
}

Datei anzeigen

@ -7,9 +7,11 @@ import net.kyori.text.Component;
* Represents something that can be used to run a {@link Command}.
*/
public interface CommandSource extends PermissionSubject {
/**
* Sends the specified {@code component} to the invoker.
* @param component the text component to send
*/
void sendMessage(Component component);
/**
* Sends the specified {@code component} to the invoker.
*
* @param component the text component to send
*/
void sendMessage(Component component);
}

Datei anzeigen

@ -1,10 +1,11 @@
package com.velocitypowered.api.event;
/**
* Represents an interface to perform direct dispatch of an event. This makes integration easier to achieve with platforms
* such as RxJava.
* Represents an interface to perform direct dispatch of an event. This makes integration easier to
* achieve with platforms such as RxJava.
*/
@FunctionalInterface
public interface EventHandler<E> {
void execute(E event);
void execute(E event);
}

Datei anzeigen

@ -6,68 +6,82 @@ import java.util.concurrent.CompletableFuture;
* Allows plugins to register and deregister listeners for event handlers.
*/
public interface EventManager {
/**
* Requests that the specified {@code listener} listen for events and associate it with the {@code plugin}.
* @param plugin the plugin to associate with the listener
* @param listener the listener to register
*/
void register(Object plugin, Object listener);
/**
* Requests that the specified {@code handler} listen for events and associate it with the {@code plugin}.
* @param plugin the plugin to associate with the handler
* @param eventClass the class for the event handler to register
* @param handler the handler to register
* @param <E> the event type to handle
*/
default <E> void register(Object plugin, Class<E> eventClass, EventHandler<E> handler) {
register(plugin, eventClass, PostOrder.NORMAL, handler);
}
/**
* Requests that the specified {@code listener} listen for events and associate it with the {@code
* plugin}.
*
* @param plugin the plugin to associate with the listener
* @param listener the listener to register
*/
void register(Object plugin, Object listener);
/**
* Requests that the specified {@code handler} listen for events and associate it with the {@code plugin}.
* @param plugin the plugin to associate with the handler
* @param eventClass the class for the event handler to register
* @param postOrder the order in which events should be posted to the handler
* @param handler the handler to register
* @param <E> the event type to handle
*/
<E> void register(Object plugin, Class<E> eventClass, PostOrder postOrder, EventHandler<E> handler);
/**
* Requests that the specified {@code handler} listen for events and associate it with the {@code
* plugin}.
*
* @param plugin the plugin to associate with the handler
* @param eventClass the class for the event handler to register
* @param handler the handler to register
* @param <E> the event type to handle
*/
default <E> void register(Object plugin, Class<E> eventClass, EventHandler<E> handler) {
register(plugin, eventClass, PostOrder.NORMAL, handler);
}
/**
* Fires the specified event to the event bus asynchronously. This allows Velocity to continue servicing connections
* while a plugin handles a potentially long-running operation such as a database query.
* @param event the event to fire
* @return a {@link CompletableFuture} representing the posted event
*/
<E> CompletableFuture<E> fire(E event);
/**
* Requests that the specified {@code handler} listen for events and associate it with the {@code
* plugin}.
*
* @param plugin the plugin to associate with the handler
* @param eventClass the class for the event handler to register
* @param postOrder the order in which events should be posted to the handler
* @param handler the handler to register
* @param <E> the event type to handle
*/
<E> void register(Object plugin, Class<E> eventClass, PostOrder postOrder,
EventHandler<E> handler);
/**
* Posts the specified event to the event bus and discards the result.
* @param event the event to fire
*/
default void fireAndForget(Object event) {
fire(event);
}
/**
* Fires the specified event to the event bus asynchronously. This allows Velocity to continue
* servicing connections while a plugin handles a potentially long-running operation such as a
* database query.
*
* @param event the event to fire
* @return a {@link CompletableFuture} representing the posted event
*/
<E> CompletableFuture<E> fire(E event);
/**
* Unregisters all listeners for the specified {@code plugin}.
* @param plugin the plugin to deregister listeners for
*/
void unregisterListeners(Object plugin);
/**
* Posts the specified event to the event bus and discards the result.
*
* @param event the event to fire
*/
default void fireAndForget(Object event) {
fire(event);
}
/**
* Unregisters a specific listener for a specific plugin.
* @param plugin the plugin associated with the listener
* @param listener the listener to deregister
*/
void unregisterListener(Object plugin, Object listener);
/**
* Unregisters all listeners for the specified {@code plugin}.
*
* @param plugin the plugin to deregister listeners for
*/
void unregisterListeners(Object plugin);
/**
* Unregisters a specific event handler for a specific plugin.
* @param plugin the plugin to associate with the handler
* @param handler the handler to register
* @param <E> the event type to handle
*/
<E> void unregister(Object plugin, EventHandler<E> handler);
/**
* Unregisters a specific listener for a specific plugin.
*
* @param plugin the plugin associated with the listener
* @param listener the listener to deregister
*/
void unregisterListener(Object plugin, Object listener);
/**
* Unregisters a specific event handler for a specific plugin.
*
* @param plugin the plugin to associate with the handler
* @param handler the handler to register
* @param <E> the event type to handle
*/
<E> void unregister(Object plugin, EventHandler<E> handler);
}

Datei anzeigen

@ -1,11 +1,10 @@
package com.velocitypowered.api.event;
/**
* Represents the order an event will be posted to a listener method, relative
* to other listeners.
* Represents the order an event will be posted to a listener method, relative to other listeners.
*/
public enum PostOrder {
FIRST, EARLY, NORMAL, LATE, LAST;
FIRST, EARLY, NORMAL, LATE, LAST;
}

Datei anzeigen

@ -1,114 +1,119 @@
package com.velocitypowered.api.event;
import com.google.common.base.Preconditions;
import java.util.Optional;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
/**
* Indicates an event that has a result attached to it.
*/
public interface ResultedEvent<R extends ResultedEvent.Result> {
/**
* Returns the result associated with this event.
* @return the result of this event
*/
R getResult();
/**
* Returns the result associated with this event.
*
* @return the result of this event
*/
R getResult();
/**
* Sets the result of this event. The result must be non-null.
*
* @param result the new result
*/
void setResult(R result);
/**
* Represents a result for an event.
*/
interface Result {
/**
* Sets the result of this event. The result must be non-null.
* @param result the new result
* Returns whether or not the event is allowed to proceed. Plugins may choose to skip denied
* events, and the proxy will respect the result of this method.
*
* @return whether or not the event is allowed to proceed
*/
void setResult(R result);
boolean isAllowed();
}
/**
* Represents a result for an event.
*/
interface Result {
/**
* Returns whether or not the event is allowed to proceed. Plugins may choose to skip denied events, and the
* proxy will respect the result of this method.
* @return whether or not the event is allowed to proceed
*/
boolean isAllowed();
/**
* A generic "allowed/denied" result.
*/
final class GenericResult implements Result {
private static final GenericResult ALLOWED = new GenericResult(true);
private static final GenericResult DENIED = new GenericResult(false);
private final boolean status;
private GenericResult(boolean b) {
this.status = b;
}
/**
* A generic "allowed/denied" result.
*/
final class GenericResult implements Result {
private static final GenericResult ALLOWED = new GenericResult(true);
private static final GenericResult DENIED = new GenericResult(false);
private final boolean status;
private GenericResult(boolean b) {
this.status = b;
}
@Override
public boolean isAllowed() {
return status;
}
@Override
public String toString() {
return status ? "allowed" : "denied";
}
public static GenericResult allowed() {
return ALLOWED;
}
public static GenericResult denied() {
return DENIED;
}
@Override
public boolean isAllowed() {
return status;
}
/**
* Represents an "allowed/denied" result with a reason allowed for denial.
*/
final class ComponentResult implements Result {
private static final ComponentResult ALLOWED = new ComponentResult(true, null);
private final boolean status;
private final @Nullable Component reason;
protected ComponentResult(boolean status, @Nullable Component reason) {
this.status = status;
this.reason = reason;
}
@Override
public boolean isAllowed() {
return status;
}
public Optional<Component> getReason() {
return Optional.ofNullable(reason);
}
@Override
public String toString() {
if (status) {
return "allowed";
}
if (reason != null) {
return "denied: " + ComponentSerializers.PLAIN.serialize(reason);
}
return "denied";
}
public static ComponentResult allowed() {
return ALLOWED;
}
public static ComponentResult denied(Component reason) {
Preconditions.checkNotNull(reason, "reason");
return new ComponentResult(false, reason);
}
@Override
public String toString() {
return status ? "allowed" : "denied";
}
public static GenericResult allowed() {
return ALLOWED;
}
public static GenericResult denied() {
return DENIED;
}
}
/**
* Represents an "allowed/denied" result with a reason allowed for denial.
*/
final class ComponentResult implements Result {
private static final ComponentResult ALLOWED = new ComponentResult(true, null);
private final boolean status;
private final @Nullable Component reason;
protected ComponentResult(boolean status, @Nullable Component reason) {
this.status = status;
this.reason = reason;
}
@Override
public boolean isAllowed() {
return status;
}
public Optional<Component> getReason() {
return Optional.ofNullable(reason);
}
@Override
public String toString() {
if (status) {
return "allowed";
}
if (reason != null) {
return "denied: " + ComponentSerializers.PLAIN.serialize(reason);
}
return "denied";
}
public static ComponentResult allowed() {
return ALLOWED;
}
public static ComponentResult denied(Component reason) {
Preconditions.checkNotNull(reason, "reason");
return new ComponentResult(false, reason);
}
}
}

Datei anzeigen

@ -12,11 +12,11 @@ import java.lang.annotation.Target;
@Target(ElementType.METHOD)
public @interface Subscribe {
/**
* The order events will be posted to this listener.
*
* @return the order
*/
PostOrder order() default PostOrder.NORMAL;
/**
* The order events will be posted to this listener.
*
* @return the order
*/
PostOrder order() default PostOrder.NORMAL;
}

Datei anzeigen

@ -7,20 +7,21 @@ import com.velocitypowered.api.proxy.InboundConnection;
* This event is fired when a handshake is established between a client and Velocity.
*/
public final class ConnectionHandshakeEvent {
private final InboundConnection connection;
public ConnectionHandshakeEvent(InboundConnection connection) {
this.connection = Preconditions.checkNotNull(connection, "connection");
}
private final InboundConnection connection;
public InboundConnection getConnection() {
return connection;
}
public ConnectionHandshakeEvent(InboundConnection connection) {
this.connection = Preconditions.checkNotNull(connection, "connection");
}
@Override
public String toString() {
return "ConnectionHandshakeEvent{" +
"connection=" + connection +
'}';
}
public InboundConnection getConnection() {
return connection;
}
@Override
public String toString() {
return "ConnectionHandshakeEvent{" +
"connection=" + connection +
'}';
}
}

Datei anzeigen

@ -4,24 +4,25 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
/**
* This event is fired when a player disconnects from the proxy. Operations on the provided player, aside from basic
* data retrieval operations, may behave in undefined ways.
* This event is fired when a player disconnects from the proxy. Operations on the provided player,
* aside from basic data retrieval operations, may behave in undefined ways.
*/
public final class DisconnectEvent {
private final Player player;
public DisconnectEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
}
private final Player player;
public Player getPlayer() {
return player;
}
public DisconnectEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
}
@Override
public String toString() {
return "DisconnectEvent{" +
"player=" + player +
'}';
}
public Player getPlayer() {
return player;
}
@Override
public String toString() {
return "DisconnectEvent{" +
"player=" + player +
'}';
}
}

Datei anzeigen

@ -5,36 +5,38 @@ import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
/**
* This event is fired once the player has been authenticated but before they connect to a server on the proxy.
* This event is fired once the player has been authenticated but before they connect to a server on
* the proxy.
*/
public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentResult> {
private final Player player;
private ComponentResult result;
public LoginEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
this.result = ComponentResult.allowed();
}
private final Player player;
private ComponentResult result;
public Player getPlayer() {
return player;
}
public LoginEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
this.result = ComponentResult.allowed();
}
@Override
public ComponentResult getResult() {
return result;
}
public Player getPlayer() {
return player;
}
@Override
public void setResult(ComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
@Override
public ComponentResult getResult() {
return result;
}
@Override
public String toString() {
return "LoginEvent{" +
"player=" + player +
", result=" + result +
'}';
}
@Override
public void setResult(ComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
@Override
public String toString() {
return "LoginEvent{" +
"player=" + player +
", result=" + result +
'}';
}
}

Datei anzeigen

@ -7,99 +7,100 @@ import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays;
/**
* This event is fired when a plugin message is sent to the proxy, either from a client ({@link com.velocitypowered.api.proxy.Player})
* or a server ({@link com.velocitypowered.api.proxy.ServerConnection}).
* This event is fired when a plugin message is sent to the proxy, either from a client ({@link
* com.velocitypowered.api.proxy.Player}) or a server ({@link com.velocitypowered.api.proxy.ServerConnection}).
*/
public final class PluginMessageEvent implements ResultedEvent<PluginMessageEvent.ForwardResult> {
private final ChannelMessageSource source;
private final ChannelMessageSink target;
private final ChannelIdentifier identifier;
private final byte[] data;
private ForwardResult result;
public PluginMessageEvent(ChannelMessageSource source, ChannelMessageSink target, ChannelIdentifier identifier, byte[] data) {
this.source = Preconditions.checkNotNull(source, "source");
this.target = Preconditions.checkNotNull(target, "target");
this.identifier = Preconditions.checkNotNull(identifier, "identifier");
this.data = Preconditions.checkNotNull(data, "data");
this.result = ForwardResult.forward();
private final ChannelMessageSource source;
private final ChannelMessageSink target;
private final ChannelIdentifier identifier;
private final byte[] data;
private ForwardResult result;
public PluginMessageEvent(ChannelMessageSource source, ChannelMessageSink target,
ChannelIdentifier identifier, byte[] data) {
this.source = Preconditions.checkNotNull(source, "source");
this.target = Preconditions.checkNotNull(target, "target");
this.identifier = Preconditions.checkNotNull(identifier, "identifier");
this.data = Preconditions.checkNotNull(data, "data");
this.result = ForwardResult.forward();
}
@Override
public ForwardResult getResult() {
return result;
}
@Override
public void setResult(ForwardResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
public ChannelMessageSource getSource() {
return source;
}
public ChannelMessageSink getTarget() {
return target;
}
public ChannelIdentifier getIdentifier() {
return identifier;
}
public byte[] getData() {
return Arrays.copyOf(data, data.length);
}
public ByteArrayDataInput dataAsDataStream() {
return ByteStreams.newDataInput(data);
}
@Override
public String toString() {
return "PluginMessageEvent{" +
"source=" + source +
", target=" + target +
", identifier=" + identifier +
", data=" + Arrays.toString(data) +
", result=" + result +
'}';
}
/**
* A result determining whether or not to forward this message on.
*/
public static final class ForwardResult implements ResultedEvent.Result {
private static final ForwardResult ALLOWED = new ForwardResult(true);
private static final ForwardResult DENIED = new ForwardResult(false);
private final boolean status;
private ForwardResult(boolean b) {
this.status = b;
}
@Override
public ForwardResult getResult() {
return result;
}
@Override
public void setResult(ForwardResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
public ChannelMessageSource getSource() {
return source;
}
public ChannelMessageSink getTarget() {
return target;
}
public ChannelIdentifier getIdentifier() {
return identifier;
}
public byte[] getData() {
return Arrays.copyOf(data, data.length);
}
public ByteArrayDataInput dataAsDataStream() {
return ByteStreams.newDataInput(data);
public boolean isAllowed() {
return status;
}
@Override
public String toString() {
return "PluginMessageEvent{" +
"source=" + source +
", target=" + target +
", identifier=" + identifier +
", data=" + Arrays.toString(data) +
", result=" + result +
'}';
return status ? "forward to sink" : "handled message at proxy";
}
/**
* A result determining whether or not to forward this message on.
*/
public static final class ForwardResult implements ResultedEvent.Result {
private static final ForwardResult ALLOWED = new ForwardResult(true);
private static final ForwardResult DENIED = new ForwardResult(false);
private final boolean status;
private ForwardResult(boolean b) {
this.status = b;
}
@Override
public boolean isAllowed() {
return status;
}
@Override
public String toString() {
return status ? "forward to sink" : "handled message at proxy";
}
public static ForwardResult forward() {
return ALLOWED;
}
public static ForwardResult handled() {
return DENIED;
}
public static ForwardResult forward() {
return ALLOWED;
}
public static ForwardResult handled() {
return DENIED;
}
}
}

Datei anzeigen

@ -4,25 +4,25 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.Player;
/**
* This event is fired once the player has been successfully authenticated and
* fully initialized and player will be connected to server after this event
* This event is fired once the player has been successfully authenticated and fully initialized and
* player will be connected to server after this event
*/
public final class PostLoginEvent {
private final Player player;
private final Player player;
public PostLoginEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
}
public PostLoginEvent(Player player) {
this.player = Preconditions.checkNotNull(player, "player");
}
public Player getPlayer() {
return player;
}
public Player getPlayer() {
return player;
}
@Override
public String toString() {
return "PostLoginEvent{"
+ "player=" + player
+ '}';
}
@Override
public String toString() {
return "PostLoginEvent{"
+ "player=" + player
+ '}';
}
}

Datei anzeigen

@ -3,147 +3,153 @@ package com.velocitypowered.api.event.connection;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.InboundConnection;
import java.util.Optional;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
/**
* This event is fired when a player has initiated a connection with the proxy but before the proxy authenticates the
* player with Mojang or before the player's proxy connection is fully established (for offline mode).
* This event is fired when a player has initiated a connection with the proxy but before the proxy
* authenticates the player with Mojang or before the player's proxy connection is fully established
* (for offline mode).
*/
public final class PreLoginEvent implements ResultedEvent<PreLoginEvent.PreLoginComponentResult> {
private final InboundConnection connection;
private final String username;
private PreLoginComponentResult result;
public PreLoginEvent(InboundConnection connection, String username) {
this.connection = Preconditions.checkNotNull(connection, "connection");
this.username = Preconditions.checkNotNull(username, "username");
this.result = PreLoginComponentResult.allowed();
}
private final InboundConnection connection;
private final String username;
private PreLoginComponentResult result;
public InboundConnection getConnection() {
return connection;
}
public PreLoginEvent(InboundConnection connection, String username) {
this.connection = Preconditions.checkNotNull(connection, "connection");
this.username = Preconditions.checkNotNull(username, "username");
this.result = PreLoginComponentResult.allowed();
}
public String getUsername() {
return username;
public InboundConnection getConnection() {
return connection;
}
public String getUsername() {
return username;
}
@Override
public PreLoginComponentResult getResult() {
return result;
}
@Override
public void setResult(@NonNull PreLoginComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
@Override
public String toString() {
return "PreLoginEvent{" +
"connection=" + connection +
", username='" + username + '\'' +
", result=" + result +
'}';
}
/**
* Represents an "allowed/allowed with forced online\offline mode/denied" result with a reason
* allowed for denial.
*/
public static final class PreLoginComponentResult implements ResultedEvent.Result {
private static final PreLoginComponentResult ALLOWED = new PreLoginComponentResult(
Result.ALLOWED, null);
private static final PreLoginComponentResult FORCE_ONLINEMODE = new PreLoginComponentResult(
Result.FORCE_ONLINE, null);
private static final PreLoginComponentResult FORCE_OFFLINEMODE = new PreLoginComponentResult(
Result.FORCE_OFFLINE, null);
private final Result result;
private final @Nullable Component reason;
private PreLoginComponentResult(Result result, @Nullable Component reason) {
this.result = result;
this.reason = reason;
}
@Override
public PreLoginComponentResult getResult() {
return result;
public boolean isAllowed() {
return result != Result.DISALLOWED;
}
@Override
public void setResult(@NonNull PreLoginComponentResult result) {
this.result = Preconditions.checkNotNull(result, "result");
public Optional<Component> getReason() {
return Optional.ofNullable(reason);
}
public boolean isOnlineModeAllowed() {
return result == Result.FORCE_ONLINE;
}
public boolean isForceOfflineMode() {
return result == Result.FORCE_OFFLINE;
}
@Override
public String toString() {
return "PreLoginEvent{" +
"connection=" + connection +
", username='" + username + '\'' +
", result=" + result +
'}';
switch (result) {
case ALLOWED:
return "allowed";
case FORCE_OFFLINE:
return "allowed with force offline mode";
case FORCE_ONLINE:
return "allowed with online mode";
default:
return "denied";
}
}
/**
* Represents an "allowed/allowed with forced online\offline mode/denied" result with a reason allowed for denial.
* Returns a result indicating the connection will be allowed through the proxy.
*
* @return the allowed result
*/
public static final class PreLoginComponentResult implements ResultedEvent.Result {
private static final PreLoginComponentResult ALLOWED = new PreLoginComponentResult(Result.ALLOWED, null);
private static final PreLoginComponentResult FORCE_ONLINEMODE = new PreLoginComponentResult(Result.FORCE_ONLINE, null);
private static final PreLoginComponentResult FORCE_OFFLINEMODE = new PreLoginComponentResult(Result.FORCE_OFFLINE, null);
private final Result result;
private final @Nullable Component reason;
private PreLoginComponentResult(Result result, @Nullable Component reason) {
this.result = result;
this.reason = reason;
}
@Override
public boolean isAllowed() {
return result != Result.DISALLOWED;
}
public Optional<Component> getReason() {
return Optional.ofNullable(reason);
}
public boolean isOnlineModeAllowed() {
return result == Result.FORCE_ONLINE;
}
public boolean isForceOfflineMode() {
return result == Result.FORCE_OFFLINE;
}
@Override
public String toString() {
switch (result) {
case ALLOWED:
return "allowed";
case FORCE_OFFLINE:
return "allowed with force offline mode";
case FORCE_ONLINE:
return "allowed with online mode";
default:
return "denied";
}
}
/**
* Returns a result indicating the connection will be allowed through
* the proxy.
* @return the allowed result
*/
public static PreLoginComponentResult allowed() {
return ALLOWED;
}
/**
* Returns a result indicating the connection will be allowed through
* the proxy, but the connection will be forced to use online mode
* provided that the proxy is in offline mode. This acts similarly to
* {@link #allowed()} on an online-mode proxy.
* @return the result
*/
public static PreLoginComponentResult forceOnlineMode() {
return FORCE_ONLINEMODE;
}
/**
* Returns a result indicating the connection will be allowed through
* the proxy, but the connection will be forced to use offline mode even
* when proxy running in online mode
* @return the result
*/
public static PreLoginComponentResult forceOfflineMode() {
return FORCE_OFFLINEMODE;
}
/**
* Denies the login with the specified reason.
* @param reason the reason for disallowing the connection
* @return a new result
*/
public static PreLoginComponentResult denied(Component reason) {
Preconditions.checkNotNull(reason, "reason");
return new PreLoginComponentResult(Result.DISALLOWED, reason);
}
private enum Result {
ALLOWED,
FORCE_ONLINE,
FORCE_OFFLINE,
DISALLOWED
}
public static PreLoginComponentResult allowed() {
return ALLOWED;
}
/**
* Returns a result indicating the connection will be allowed through the proxy, but the
* connection will be forced to use online mode provided that the proxy is in offline mode. This
* acts similarly to {@link #allowed()} on an online-mode proxy.
*
* @return the result
*/
public static PreLoginComponentResult forceOnlineMode() {
return FORCE_ONLINEMODE;
}
/**
* Returns a result indicating the connection will be allowed through the proxy, but the
* connection will be forced to use offline mode even when proxy running in online mode
*
* @return the result
*/
public static PreLoginComponentResult forceOfflineMode() {
return FORCE_OFFLINEMODE;
}
/**
* Denies the login with the specified reason.
*
* @param reason the reason for disallowing the connection
* @return a new result
*/
public static PreLoginComponentResult denied(Component reason) {
Preconditions.checkNotNull(reason, "reason");
return new PreLoginComponentResult(Result.DISALLOWED, reason);
}
private enum Result {
ALLOWED,
FORCE_ONLINE,
FORCE_OFFLINE,
DISALLOWED
}
}
}

Datei anzeigen

@ -12,52 +12,52 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* <p>This event is only called once per subject, on initialisation.</p>
*/
public final class PermissionsSetupEvent {
private final PermissionSubject subject;
private final PermissionProvider defaultProvider;
private PermissionProvider provider;
public PermissionsSetupEvent(PermissionSubject subject, PermissionProvider provider) {
this.subject = Preconditions.checkNotNull(subject, "subject");
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
}
private final PermissionSubject subject;
private final PermissionProvider defaultProvider;
private PermissionProvider provider;
public PermissionSubject getSubject() {
return this.subject;
}
public PermissionsSetupEvent(PermissionSubject subject, PermissionProvider provider) {
this.subject = Preconditions.checkNotNull(subject, "subject");
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
}
/**
* Uses the provider function to obtain a {@link PermissionFunction} for
* the subject.
*
* @param subject the subject
* @return the obtained permission function
*/
public PermissionFunction createFunction(PermissionSubject subject) {
return this.provider.createFunction(subject);
}
public PermissionSubject getSubject() {
return this.subject;
}
public PermissionProvider getProvider() {
return this.provider;
}
/**
* Uses the provider function to obtain a {@link PermissionFunction} for the subject.
*
* @param subject the subject
* @return the obtained permission function
*/
public PermissionFunction createFunction(PermissionSubject subject) {
return this.provider.createFunction(subject);
}
/**
* Sets the {@link PermissionFunction} that should be used for the subject.
*
* <p>Specifying <code>null</code> will reset the provider to the default
* instance given when the event was posted.</p>
*
* @param provider the provider
*/
public void setProvider(@Nullable PermissionProvider provider) {
this.provider = provider == null ? this.defaultProvider : provider;
}
public PermissionProvider getProvider() {
return this.provider;
}
@Override
public String toString() {
return "PermissionsSetupEvent{" +
"subject=" + subject +
", defaultProvider=" + defaultProvider +
", provider=" + provider +
'}';
}
/**
* Sets the {@link PermissionFunction} that should be used for the subject.
*
* <p>Specifying <code>null</code> will reset the provider to the default
* instance given when the event was posted.</p>
*
* @param provider the provider
*/
public void setProvider(@Nullable PermissionProvider provider) {
this.provider = provider == null ? this.defaultProvider : provider;
}
@Override
public String toString() {
return "PermissionsSetupEvent{" +
"subject=" + subject +
", defaultProvider=" + defaultProvider +
", provider=" + provider +
'}';
}
}

Datei anzeigen

@ -6,65 +6,73 @@ import com.velocitypowered.api.util.GameProfile;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* This event is fired after the {@link com.velocitypowered.api.event.connection.PreLoginEvent} in order to set up the
* game profile for the user. This can be used to configure a custom profile for a user, i.e. skin replacement.
* This event is fired after the {@link com.velocitypowered.api.event.connection.PreLoginEvent} in
* order to set up the game profile for the user. This can be used to configure a custom profile for
* a user, i.e. skin replacement.
*/
public final class GameProfileRequestEvent {
private final String username;
private final InboundConnection connection;
private final GameProfile originalProfile;
private final boolean onlineMode;
private @Nullable GameProfile gameProfile;
public GameProfileRequestEvent(InboundConnection connection, GameProfile originalProfile, boolean onlineMode) {
this.connection = Preconditions.checkNotNull(connection, "connection");
this.originalProfile = Preconditions.checkNotNull(originalProfile, "originalProfile");
this.username = originalProfile.getName();
this.onlineMode = onlineMode;
}
private final String username;
private final InboundConnection connection;
private final GameProfile originalProfile;
private final boolean onlineMode;
private @Nullable GameProfile gameProfile;
public InboundConnection getConnection() {
return connection;
}
public GameProfileRequestEvent(InboundConnection connection, GameProfile originalProfile,
boolean onlineMode) {
this.connection = Preconditions.checkNotNull(connection, "connection");
this.originalProfile = Preconditions.checkNotNull(originalProfile, "originalProfile");
this.username = originalProfile.getName();
this.onlineMode = onlineMode;
}
public String getUsername() {
return username;
}
public GameProfile getOriginalProfile() {
return originalProfile;
}
public boolean isOnlineMode() {
return onlineMode;
}
public InboundConnection getConnection() {
return connection;
}
/**
* Returns the game profile that will be used to initialize the connection with. Should no profile be currently
* specified, the one generated by the proxy (for offline mode) or retrieved from the Mojang session servers (for
* online mode) will be returned instead.
* @return the user's {@link GameProfile}
*/
public GameProfile getGameProfile() {
return gameProfile == null ? originalProfile : gameProfile;
}
public String getUsername() {
return username;
}
public GameProfile getOriginalProfile() {
return originalProfile;
}
public boolean isOnlineMode() {
return onlineMode;
}
/**
* Returns the game profile that will be used to initialize the connection with. Should no profile
* be currently specified, the one generated by the proxy (for offline mode) or retrieved from the
* Mojang session servers (for online mode) will be returned instead.
*
* @return the user's {@link GameProfile}
*/
public GameProfile getGameProfile() {
return gameProfile == null ? originalProfile : gameProfile;
}
/**
* Sets the game profile to use for this connection. It is invalid to use this method on an
* online-mode connection.
*
* @param gameProfile the profile to use for the connection, {@code null} uses the original
* profile
*/
public void setGameProfile(@Nullable GameProfile gameProfile) {
Preconditions
.checkState(!onlineMode, "Connection is in online mode, profiles can not be faked");
this.gameProfile = gameProfile;
}
@Override
public String toString() {
return "GameProfileRequestEvent{" +
"username=" + username +
", gameProfile=" + gameProfile +
"}";
}
/**
* Sets the game profile to use for this connection. It is invalid to use this method on an online-mode connection.
* @param gameProfile the profile to use for the connection, {@code null} uses the original profile
*/
public void setGameProfile(@Nullable GameProfile gameProfile) {
Preconditions.checkState(!onlineMode, "Connection is in online mode, profiles can not be faked");
this.gameProfile = gameProfile;
}
@Override
public String toString() {
return "GameProfileRequestEvent{"+
"username=" + username +
", gameProfile=" + gameProfile +
"}";
}
}

Datei anzeigen

@ -8,111 +8,120 @@ import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* Fired when a player is kicked from a server. You may either allow Velocity to kick the player (with an optional reason
* override) or redirect the player to a separate server.
* Fired when a player is kicked from a server. You may either allow Velocity to kick the player
* (with an optional reason override) or redirect the player to a separate server.
*/
public final class KickedFromServerEvent implements ResultedEvent<KickedFromServerEvent.ServerKickResult> {
private final Player player;
public final class KickedFromServerEvent implements
ResultedEvent<KickedFromServerEvent.ServerKickResult> {
private final Player player;
private final RegisteredServer server;
private final Component originalReason;
private final boolean duringLogin;
private ServerKickResult result;
public KickedFromServerEvent(Player player, RegisteredServer server, Component originalReason,
boolean duringLogin, Component fancyReason) {
this.player = Preconditions.checkNotNull(player, "player");
this.server = Preconditions.checkNotNull(server, "server");
this.originalReason = Preconditions.checkNotNull(originalReason, "originalReason");
this.duringLogin = duringLogin;
this.result = new DisconnectPlayer(fancyReason);
}
@Override
public ServerKickResult getResult() {
return result;
}
@Override
public void setResult(@NonNull ServerKickResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
public Player getPlayer() {
return player;
}
public RegisteredServer getServer() {
return server;
}
public Component getOriginalReason() {
return originalReason;
}
public boolean kickedDuringLogin() {
return duringLogin;
}
/**
* Represents the base interface for {@link KickedFromServerEvent} results.
*/
public interface ServerKickResult extends ResultedEvent.Result {
}
/**
* Tells the proxy to disconnect the player with the specified reason.
*/
public static final class DisconnectPlayer implements ServerKickResult {
private final Component component;
private DisconnectPlayer(Component component) {
this.component = Preconditions.checkNotNull(component, "component");
}
@Override
public boolean isAllowed() {
return true;
}
public Component getReason() {
return component;
}
/**
* Creates a new {@link DisconnectPlayer} with the specified reason.
*
* @param reason the reason to use when disconnecting the player
* @return the disconnect result
*/
public static DisconnectPlayer create(Component reason) {
return new DisconnectPlayer(reason);
}
}
/**
* Tells the proxy to redirect the player to another server. No messages will be sent from the
* proxy when this result is used.
*/
public static final class RedirectPlayer implements ServerKickResult {
private final RegisteredServer server;
private final Component originalReason;
private final boolean duringLogin;
private ServerKickResult result;
public KickedFromServerEvent(Player player, RegisteredServer server, Component originalReason, boolean duringLogin, Component fancyReason) {
this.player = Preconditions.checkNotNull(player, "player");
this.server = Preconditions.checkNotNull(server, "server");
this.originalReason = Preconditions.checkNotNull(originalReason, "originalReason");
this.duringLogin = duringLogin;
this.result = new DisconnectPlayer(fancyReason);
private RedirectPlayer(RegisteredServer server) {
this.server = Preconditions.checkNotNull(server, "server");
}
@Override
public ServerKickResult getResult() {
return result;
}
@Override
public void setResult(@NonNull ServerKickResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
public Player getPlayer() {
return player;
public boolean isAllowed() {
return false;
}
public RegisteredServer getServer() {
return server;
}
public Component getOriginalReason() {
return originalReason;
}
public boolean kickedDuringLogin() {
return duringLogin;
return server;
}
/**
* Represents the base interface for {@link KickedFromServerEvent} results.
* Creates a new redirect result to forward the player to the specified {@code server}.
*
* @param server the server to send the player to
* @return the redirect result
*/
public interface ServerKickResult extends ResultedEvent.Result {}
/**
* Tells the proxy to disconnect the player with the specified reason.
*/
public static final class DisconnectPlayer implements ServerKickResult {
private final Component component;
private DisconnectPlayer(Component component) {
this.component = Preconditions.checkNotNull(component, "component");
}
@Override
public boolean isAllowed() {
return true;
}
public Component getReason() {
return component;
}
/**
* Creates a new {@link DisconnectPlayer} with the specified reason.
* @param reason the reason to use when disconnecting the player
* @return the disconnect result
*/
public static DisconnectPlayer create(Component reason) {
return new DisconnectPlayer(reason);
}
}
/**
* Tells the proxy to redirect the player to another server. No messages will be sent from the proxy
* when this result is used.
*/
public static final class RedirectPlayer implements ServerKickResult {
private final RegisteredServer server;
private RedirectPlayer(RegisteredServer server) {
this.server = Preconditions.checkNotNull(server, "server");
}
@Override
public boolean isAllowed() {
return false;
}
public RegisteredServer getServer() {
return server;
}
/**
* Creates a new redirect result to forward the player to the specified {@code server}.
* @param server the server to send the player to
* @return the redirect result
*/
public static RedirectPlayer create(RegisteredServer server) {
return new RedirectPlayer(server);
}
public static RedirectPlayer create(RegisteredServer server) {
return new RedirectPlayer(server);
}
}
}

Datei anzeigen

@ -3,95 +3,96 @@ package com.velocitypowered.api.event.player;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
/**
* This event is fired when a player types in a chat message.
*/
public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.ChatResult> {
private final Player player;
private final String message;
private ChatResult result;
public PlayerChatEvent(Player player, String message) {
this.player = Preconditions.checkNotNull(player, "player");
this.message = Preconditions.checkNotNull(message, "message");
this.result = ChatResult.allowed();
}
private final Player player;
private final String message;
private ChatResult result;
public Player getPlayer() {
return player;
}
public PlayerChatEvent(Player player, String message) {
this.player = Preconditions.checkNotNull(player, "player");
this.message = Preconditions.checkNotNull(message, "message");
this.result = ChatResult.allowed();
}
public String getMessage() {
return message;
public Player getPlayer() {
return player;
}
public String getMessage() {
return message;
}
@Override
public ChatResult getResult() {
return result;
}
@Override
public void setResult(ChatResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
@Override
public String toString() {
return "PlayerChatEvent{" +
"player=" + player +
", message=" + message +
", result=" + result +
'}';
}
/**
* Represents the result of the {@link PlayerChatEvent}.
*/
public static final class ChatResult implements ResultedEvent.Result {
private static final ChatResult ALLOWED = new ChatResult(true, null);
private static final ChatResult DENIED = new ChatResult(false, null);
// The server can not accept formatted text from clients!
private @Nullable String message;
private final boolean status;
protected ChatResult(boolean status, @Nullable String message) {
this.status = status;
this.message = message;
}
@Override
public ChatResult getResult() {
return result;
}
@Override
public void setResult(ChatResult result) {
this.result = Preconditions.checkNotNull(result, "result");
public boolean isAllowed() {
return status;
}
@Override
public String toString() {
return "PlayerChatEvent{" +
"player=" + player +
", message=" + message +
", result=" + result +
'}';
return status ? "allowed" : "denied";
}
/**
* Represents the result of the {@link PlayerChatEvent}.
*/
public static final class ChatResult implements ResultedEvent.Result {
private static final ChatResult ALLOWED = new ChatResult(true, null);
private static final ChatResult DENIED = new ChatResult(false, null);
// The server can not accept formatted text from clients!
private @Nullable String message;
private final boolean status;
protected ChatResult(boolean status, @Nullable String message) {
this.status = status;
this.message = message;
}
@Override
public boolean isAllowed() {
return status;
}
@Override
public String toString() {
return status ? "allowed" : "denied";
}
public static ChatResult allowed() {
return ALLOWED;
}
public static ChatResult denied() {
return DENIED;
}
public Optional<String> getMessage() {
return Optional.ofNullable(message);
}
public static ChatResult message(@NonNull String message) {
Preconditions.checkNotNull(message, "message");
return new ChatResult(true, message);
}
public static ChatResult allowed() {
return ALLOWED;
}
public static ChatResult denied() {
return DENIED;
}
public Optional<String> getMessage() {
return Optional.ofNullable(message);
}
public static ChatResult message(@NonNull String message) {
Preconditions.checkNotNull(message, "message");
return new ChatResult(true, message);
}
}
}

Datei anzeigen

@ -8,19 +8,20 @@ import com.velocitypowered.api.util.ModInfo;
* This event is fired when the players ModInfo is changed.
*/
public final class PlayerModInfoEvent {
private final Player player;
private final ModInfo modInfo;
public PlayerModInfoEvent(Player player, ModInfo modInfo) {
this.player = Preconditions.checkNotNull(player, "player");
this.modInfo = Preconditions.checkNotNull(modInfo, "modInfo");
}
public Player getPlayer() {
return player;
}
public ModInfo getModInfo() {
return modInfo;
}
private final Player player;
private final ModInfo modInfo;
public PlayerModInfoEvent(Player player, ModInfo modInfo) {
this.player = Preconditions.checkNotNull(player, "player");
this.modInfo = Preconditions.checkNotNull(modInfo, "modInfo");
}
public Player getPlayer() {
return player;
}
public ModInfo getModInfo() {
return modInfo;
}
}

Datei anzeigen

@ -5,19 +5,20 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.player.PlayerSettings;
public final class PlayerSettingsChangedEvent {
private final Player player;
private final PlayerSettings playerSettings;
public PlayerSettingsChangedEvent(Player player, PlayerSettings playerSettings) {
this.player = Preconditions.checkNotNull(player, "player");
this.playerSettings = Preconditions.checkNotNull(playerSettings, "playerSettings");
}
private final Player player;
private final PlayerSettings playerSettings;
public Player getPlayer() {
return player;
}
public PlayerSettingsChangedEvent(Player player, PlayerSettings playerSettings) {
this.player = Preconditions.checkNotNull(player, "player");
this.playerSettings = Preconditions.checkNotNull(playerSettings, "playerSettings");
}
public PlayerSettings getPlayerSettings() {
return playerSettings;
}
public Player getPlayer() {
return player;
}
public PlayerSettings getPlayerSettings() {
return playerSettings;
}
}

Datei anzeigen

@ -5,31 +5,32 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
/**
* This event is fired once the player has successfully connected to the target server and the connection to the previous
* server has been de-established.
* This event is fired once the player has successfully connected to the target server and the
* connection to the previous server has been de-established.
*/
public final class ServerConnectedEvent {
private final Player player;
private final RegisteredServer server;
public ServerConnectedEvent(Player player, RegisteredServer server) {
this.player = Preconditions.checkNotNull(player, "player");
this.server = Preconditions.checkNotNull(server, "server");
}
private final Player player;
private final RegisteredServer server;
public Player getPlayer() {
return player;
}
public ServerConnectedEvent(Player player, RegisteredServer server) {
this.player = Preconditions.checkNotNull(player, "player");
this.server = Preconditions.checkNotNull(server, "server");
}
public RegisteredServer getServer() {
return server;
}
public Player getPlayer() {
return player;
}
@Override
public String toString() {
return "ServerConnectedEvent{" +
"player=" + player +
", server=" + server +
'}';
}
public RegisteredServer getServer() {
return server;
}
@Override
public String toString() {
return "ServerConnectedEvent{" +
"player=" + player +
", server=" + server +
'}';
}
}

Datei anzeigen

@ -4,87 +4,89 @@ import com.google.common.base.Preconditions;
import com.velocitypowered.api.event.ResultedEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* This event is fired before the player connects to a server.
*/
public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEvent.ServerResult> {
private final Player player;
private final RegisteredServer originalServer;
private ServerResult result;
public final class ServerPreConnectEvent implements
ResultedEvent<ServerPreConnectEvent.ServerResult> {
public ServerPreConnectEvent(Player player, RegisteredServer originalServer) {
this.player = Preconditions.checkNotNull(player, "player");
this.originalServer = Preconditions.checkNotNull(originalServer, "originalServer");
this.result = ServerResult.allowed(originalServer);
}
private final Player player;
private final RegisteredServer originalServer;
private ServerResult result;
public Player getPlayer() {
return player;
public ServerPreConnectEvent(Player player, RegisteredServer originalServer) {
this.player = Preconditions.checkNotNull(player, "player");
this.originalServer = Preconditions.checkNotNull(originalServer, "originalServer");
this.result = ServerResult.allowed(originalServer);
}
public Player getPlayer() {
return player;
}
@Override
public ServerResult getResult() {
return result;
}
@Override
public void setResult(ServerResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
public RegisteredServer getOriginalServer() {
return originalServer;
}
@Override
public String toString() {
return "ServerPreConnectEvent{" +
"player=" + player +
", originalServer=" + originalServer +
", result=" + result +
'}';
}
/**
* Represents the result of the {@link ServerPreConnectEvent}.
*/
public static class ServerResult implements ResultedEvent.Result {
private static final ServerResult DENIED = new ServerResult(null);
private final @Nullable RegisteredServer server;
private ServerResult(@Nullable RegisteredServer server) {
this.server = server;
}
@Override
public ServerResult getResult() {
return result;
public boolean isAllowed() {
return server != null;
}
@Override
public void setResult(ServerResult result) {
this.result = Preconditions.checkNotNull(result, "result");
}
public RegisteredServer getOriginalServer() {
return originalServer;
public Optional<RegisteredServer> getServer() {
return Optional.ofNullable(server);
}
@Override
public String toString() {
return "ServerPreConnectEvent{" +
"player=" + player +
", originalServer=" + originalServer +
", result=" + result +
'}';
if (server != null) {
return "allowed: connect to " + server.getServerInfo().getName();
}
return "denied";
}
/**
* Represents the result of the {@link ServerPreConnectEvent}.
*/
public static class ServerResult implements ResultedEvent.Result {
private static final ServerResult DENIED = new ServerResult(null);
private final @Nullable RegisteredServer server;
private ServerResult(@Nullable RegisteredServer server) {
this.server = server;
}
@Override
public boolean isAllowed() {
return server != null;
}
public Optional<RegisteredServer> getServer() {
return Optional.ofNullable(server);
}
@Override
public String toString() {
if (server != null) {
return "allowed: connect to " + server.getServerInfo().getName();
}
return "denied";
}
public static ServerResult denied() {
return DENIED;
}
public static ServerResult allowed(RegisteredServer server) {
Preconditions.checkNotNull(server, "server");
return new ServerResult(server);
}
public static ServerResult denied() {
return DENIED;
}
public static ServerResult allowed(RegisteredServer server) {
Preconditions.checkNotNull(server, "server");
return new ServerResult(server);
}
}
}

Datei anzeigen

@ -1,11 +1,13 @@
package com.velocitypowered.api.event.proxy;
/**
* This event is fired by the proxy after plugins have been loaded but before the proxy starts accepting connections.
* This event is fired by the proxy after plugins have been loaded but before the proxy starts
* accepting connections.
*/
public final class ProxyInitializeEvent {
@Override
public String toString() {
return "ProxyInitializeEvent";
}
@Override
public String toString() {
return "ProxyInitializeEvent";
}
}

Datei anzeigen

@ -8,31 +8,32 @@ import com.velocitypowered.api.proxy.server.ServerPing;
* This event is fired when a server list ping request is sent by a remote client.
*/
public final class ProxyPingEvent {
private final InboundConnection connection;
private ServerPing ping;
public ProxyPingEvent(InboundConnection connection, ServerPing ping) {
this.connection = Preconditions.checkNotNull(connection, "connection");
this.ping = Preconditions.checkNotNull(ping, "ping");
}
private final InboundConnection connection;
private ServerPing ping;
public InboundConnection getConnection() {
return connection;
}
public ProxyPingEvent(InboundConnection connection, ServerPing ping) {
this.connection = Preconditions.checkNotNull(connection, "connection");
this.ping = Preconditions.checkNotNull(ping, "ping");
}
public ServerPing getPing() {
return ping;
}
public InboundConnection getConnection() {
return connection;
}
public void setPing(ServerPing ping) {
this.ping = Preconditions.checkNotNull(ping, "ping");
}
public ServerPing getPing() {
return ping;
}
@Override
public String toString() {
return "ProxyPingEvent{" +
"connection=" + connection +
", ping=" + ping +
'}';
}
public void setPing(ServerPing ping) {
this.ping = Preconditions.checkNotNull(ping, "ping");
}
@Override
public String toString() {
return "ProxyPingEvent{" +
"connection=" + connection +
", ping=" + ping +
'}';
}
}

Datei anzeigen

@ -1,12 +1,13 @@
package com.velocitypowered.api.event.proxy;
/**
* This event is fired by the proxy after the proxy has stopped accepting connections but before the proxy process
* exits.
* This event is fired by the proxy after the proxy has stopped accepting connections but before the
* proxy process exits.
*/
public final class ProxyShutdownEvent {
@Override
public String toString() {
return "ProxyShutdownEvent";
}
@Override
public String toString() {
return "ProxyShutdownEvent";
}
}

Datei anzeigen

@ -2,82 +2,86 @@ package com.velocitypowered.api.event.query;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.server.QueryResponse;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.net.InetAddress;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* This event is fired if proxy is getting queried over GS4 Query protocol
*/
public final class ProxyQueryEvent {
private final QueryType queryType;
private final InetAddress querierAddress;
private QueryResponse response;
public ProxyQueryEvent(QueryType queryType, InetAddress querierAddress, QueryResponse response) {
this.queryType = Preconditions.checkNotNull(queryType, "queryType");
this.querierAddress = Preconditions.checkNotNull(querierAddress, "querierAddress");
this.response = Preconditions.checkNotNull(response, "response");
}
private final QueryType queryType;
private final InetAddress querierAddress;
private QueryResponse response;
public ProxyQueryEvent(QueryType queryType, InetAddress querierAddress, QueryResponse response) {
this.queryType = Preconditions.checkNotNull(queryType, "queryType");
this.querierAddress = Preconditions.checkNotNull(querierAddress, "querierAddress");
this.response = Preconditions.checkNotNull(response, "response");
}
/**
* Get query type
*
* @return query type
*/
@NonNull
public QueryType getQueryType() {
return queryType;
}
/**
* Get querier address
*
* @return querier address
*/
@NonNull
public InetAddress getQuerierAddress() {
return querierAddress;
}
/**
* Get query response
*
* @return query response
*/
@NonNull
public QueryResponse getResponse() {
return response;
}
/**
* Set query response
*
* @param response query response
*/
public void setResponse(@NonNull QueryResponse response) {
this.response = Preconditions.checkNotNull(response, "response");
}
@Override
public String toString() {
return "ProxyQueryEvent{" +
"queryType=" + queryType +
", querierAddress=" + querierAddress +
", response=" + response +
'}';
}
/**
* The type of query
*/
public enum QueryType {
/**
* Basic query asks only a subset of information, such as hostname, game type (hardcoded to
* <pre>MINECRAFT</pre>), map, current players, max players, proxy port and proxy hostname
*/
BASIC,
/**
* Get query type
* @return query type
* Full query asks pretty much everything present on this event (only hardcoded values cannot be
* modified here).
*/
@NonNull
public QueryType getQueryType() {
return queryType;
}
/**
* Get querier address
* @return querier address
*/
@NonNull
public InetAddress getQuerierAddress() {
return querierAddress;
}
/**
* Get query response
* @return query response
*/
@NonNull
public QueryResponse getResponse() {
return response;
}
/**
* Set query response
* @param response query response
*/
public void setResponse(@NonNull QueryResponse response) {
this.response = Preconditions.checkNotNull(response, "response");
}
@Override
public String toString() {
return "ProxyQueryEvent{" +
"queryType=" + queryType +
", querierAddress=" + querierAddress +
", response=" + response +
'}';
}
/**
* The type of query
*/
public enum QueryType {
/**
* Basic query asks only a subset of information, such as hostname, game type (hardcoded to <pre>MINECRAFT</pre>), map,
* current players, max players, proxy port and proxy hostname
*/
BASIC,
/**
* Full query asks pretty much everything present on this event (only hardcoded values cannot be modified here).
*/
FULL
;
}
FULL;
}
}

Datei anzeigen

@ -1,31 +1,31 @@
package com.velocitypowered.api.permission;
/**
* Function that calculates the permission settings for a given
* {@link PermissionSubject}.
* Function that calculates the permission settings for a given {@link PermissionSubject}.
*/
@FunctionalInterface
public interface PermissionFunction {
/**
* A permission function that always returns {@link Tristate#TRUE}.
*/
PermissionFunction ALWAYS_TRUE = p -> Tristate.TRUE;
/**
* A permission function that always returns {@link Tristate#FALSE}.
*/
PermissionFunction ALWAYS_FALSE = p -> Tristate.FALSE;
/**
* A permission function that always returns {@link Tristate#TRUE}.
*/
PermissionFunction ALWAYS_TRUE = p -> Tristate.TRUE;
/**
* A permission function that always returns {@link Tristate#UNDEFINED}.
*/
PermissionFunction ALWAYS_UNDEFINED = p -> Tristate.UNDEFINED;
/**
* A permission function that always returns {@link Tristate#FALSE}.
*/
PermissionFunction ALWAYS_FALSE = p -> Tristate.FALSE;
/**
* Gets the subjects setting for a particular permission.
*
* @param permission the permission
* @return the value the permission is set to
*/
Tristate getPermissionValue(String permission);
/**
* A permission function that always returns {@link Tristate#UNDEFINED}.
*/
PermissionFunction ALWAYS_UNDEFINED = p -> Tristate.UNDEFINED;
/**
* Gets the subjects setting for a particular permission.
*
* @param permission the permission
* @return the value the permission is set to
*/
Tristate getPermissionValue(String permission);
}

Datei anzeigen

@ -5,11 +5,12 @@ package com.velocitypowered.api.permission;
*/
@FunctionalInterface
public interface PermissionProvider {
/**
* Creates a {@link PermissionFunction} for the subject.
*
* @param subject the subject
* @return the function
*/
PermissionFunction createFunction(PermissionSubject subject);
/**
* Creates a {@link PermissionFunction} for the subject.
*
* @param subject the subject
* @return the function
*/
PermissionFunction createFunction(PermissionSubject subject);
}

Datei anzeigen

@ -4,21 +4,22 @@ package com.velocitypowered.api.permission;
* Represents a object that has a set of queryable permissions.
*/
public interface PermissionSubject {
/**
* Determines whether or not the subject has a particular permission.
*
* @param permission the permission to check for
* @return whether or not the subject has the permission
*/
default boolean hasPermission(String permission) {
return getPermissionValue(permission).asBoolean();
}
/**
* Gets the subjects setting for a particular permission.
*
* @param permission the permission
* @return the value the permission is set to
*/
Tristate getPermissionValue(String permission);
/**
* Determines whether or not the subject has a particular permission.
*
* @param permission the permission to check for
* @return whether or not the subject has the permission
*/
default boolean hasPermission(String permission) {
return getPermissionValue(permission).asBoolean();
}
/**
* Gets the subjects setting for a particular permission.
*
* @param permission the permission
* @return the value the permission is set to
*/
Tristate getPermissionValue(String permission);
}

Datei anzeigen

@ -8,69 +8,70 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* <p>Possible values:</p>
* <p></p>
* <ul>
* <li>{@link #TRUE} - a positive setting</li>
* <li>{@link #FALSE} - a negative (negated) setting</li>
* <li>{@link #UNDEFINED} - a non-existent setting</li>
* <li>{@link #TRUE} - a positive setting</li>
* <li>{@link #FALSE} - a negative (negated) setting</li>
* <li>{@link #UNDEFINED} - a non-existent setting</li>
* </ul>
*/
public enum Tristate {
/**
* A value indicating a positive setting
*/
TRUE(true),
/**
* A value indicating a positive setting
*/
TRUE(true),
/**
* A value indicating a negative (negated) setting
*/
FALSE(false),
/**
* A value indicating a negative (negated) setting
*/
FALSE(false),
/**
* A value indicating a non-existent setting
*/
UNDEFINED(false);
/**
* A value indicating a non-existent setting
*/
UNDEFINED(false);
/**
* Returns a {@link Tristate} from a boolean
*
* @param val the boolean value
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or <code>false</code>, respectively.
*/
public static Tristate fromBoolean(boolean val) {
return val ? TRUE : FALSE;
/**
* Returns a {@link Tristate} from a boolean
*
* @param val the boolean value
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or
* <code>false</code>, respectively.
*/
public static Tristate fromBoolean(boolean val) {
return val ? TRUE : FALSE;
}
/**
* Returns a {@link Tristate} from a nullable boolean.
*
* <p>Unlike {@link #fromBoolean(boolean)}, this method returns {@link #UNDEFINED}
* if the value is null.</p>
*
* @param val the boolean value
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value is <code>null</code>,
* <code>true</code> or <code>false</code>, respectively.
*/
public static Tristate fromNullableBoolean(@Nullable Boolean val) {
if (val == null) {
return UNDEFINED;
}
return val ? TRUE : FALSE;
}
/**
* Returns a {@link Tristate} from a nullable boolean.
*
* <p>Unlike {@link #fromBoolean(boolean)}, this method returns {@link #UNDEFINED}
* if the value is null.</p>
*
* @param val the boolean value
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value
* is <code>null</code>, <code>true</code> or <code>false</code>, respectively.
*/
public static Tristate fromNullableBoolean(@Nullable Boolean val) {
if (val == null) {
return UNDEFINED;
}
return val ? TRUE : FALSE;
}
private final boolean booleanValue;
private final boolean booleanValue;
Tristate(boolean booleanValue) {
this.booleanValue = booleanValue;
}
Tristate(boolean booleanValue) {
this.booleanValue = booleanValue;
}
/**
* Returns the value of the Tristate as a boolean.
*
* <p>A value of {@link #UNDEFINED} converts to false.</p>
*
* @return a boolean representation of the Tristate.
*/
public boolean asBoolean() {
return this.booleanValue;
}
/**
* Returns the value of the Tristate as a boolean.
*
* <p>A value of {@link #UNDEFINED} converts to false.</p>
*
* @return a boolean representation of the Tristate.
*/
public boolean asBoolean() {
return this.booleanValue;
}
}

Datei anzeigen

@ -10,19 +10,19 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Dependency {
/**
* The plugin ID of the dependency.
*
* @return The dependency plugin ID
* @see Plugin#id()
*/
String id();
/**
* If this dependency is optional for the plugin to work. By default
* this is {@code false}.
*
* @return true if the dependency is optional for the plugin to work
*/
boolean optional() default false;
/**
* The plugin ID of the dependency.
*
* @return The dependency plugin ID
* @see Plugin#id()
*/
String id();
/**
* If this dependency is optional for the plugin to work. By default this is {@code false}.
*
* @return true if the dependency is optional for the plugin to work
*/
boolean optional() default false;
}

Datei anzeigen

@ -1,19 +1,20 @@
package com.velocitypowered.api.plugin;
public class InvalidPluginException extends Exception {
public InvalidPluginException() {
super();
}
public InvalidPluginException(String message) {
super(message);
}
public InvalidPluginException() {
super();
}
public InvalidPluginException(String message, Throwable cause) {
super(message, cause);
}
public InvalidPluginException(String message) {
super(message);
}
public InvalidPluginException(Throwable cause) {
super(cause);
}
public InvalidPluginException(String message, Throwable cause) {
super(message, cause);
}
public InvalidPluginException(Throwable cause) {
super(cause);
}
}

Datei anzeigen

@ -11,56 +11,55 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Plugin {
/**
* The ID of the plugin. This ID should be unique as to
* not conflict with other plugins.
*
* The plugin ID must match the {@link PluginDescription#ID_PATTERN}.
*
* @return the ID for this plugin
*/
String id();
/**
* The human readable name of the plugin as to be used in descriptions and
* similar things.
*
* @return The plugin name, or an empty string if unknown
*/
String name() default "";
/**
* The ID of the plugin. This ID should be unique as to not conflict with other plugins.
*
* The plugin ID must match the {@link PluginDescription#ID_PATTERN}.
*
* @return the ID for this plugin
*/
String id();
/**
* The version of the plugin.
*
* @return the version of the plugin, or an empty string if unknown
*/
String version() default "";
/**
* The human readable name of the plugin as to be used in descriptions and similar things.
*
* @return The plugin name, or an empty string if unknown
*/
String name() default "";
/**
* The description of the plugin, explaining what it can be used for.
*
* @return The plugin description, or an empty string if unknown
*/
String description() default "";
/**
* The version of the plugin.
*
* @return the version of the plugin, or an empty string if unknown
*/
String version() default "";
/**
* The URL or website of the plugin.
*
* @return The plugin url, or an empty string if unknown
*/
String url() default "";
/**
* The description of the plugin, explaining what it can be used for.
*
* @return The plugin description, or an empty string if unknown
*/
String description() default "";
/**
* The author of the plugin.
*
* @return the plugin's author, or empty if unknown
*/
String[] authors() default "";
/**
* The URL or website of the plugin.
*
* @return The plugin url, or an empty string if unknown
*/
String url() default "";
/**
* The dependencies required to load before this plugin.
*
* @return the plugin dependencies
*/
Dependency[] dependencies() default {};
/**
* The author of the plugin.
*
* @return the plugin's author, or empty if unknown
*/
String[] authors() default "";
/**
* The dependencies required to load before this plugin.
*
* @return the plugin dependencies
*/
Dependency[] dependencies() default {};
}

Datei anzeigen

@ -6,19 +6,20 @@ import java.util.Optional;
* A wrapper around a plugin loaded by the proxy.
*/
public interface PluginContainer {
/**
* Returns the plugin's description.
*
* @return the plugin's description
*/
PluginDescription getDescription();
/**
* Returns the created plugin if it is available.
*
* @return the instance if available
*/
default Optional<?> getInstance() {
return Optional.empty();
}
/**
* Returns the plugin's description.
*
* @return the plugin's description
*/
PluginDescription getDescription();
/**
* Returns the created plugin if it is available.
*
* @return the instance if available
*/
default Optional<?> getInstance() {
return Optional.empty();
}
}

Datei anzeigen

@ -3,7 +3,6 @@ package com.velocitypowered.api.plugin;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.velocitypowered.api.plugin.meta.PluginDependency;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
@ -14,93 +13,92 @@ import java.util.regex.Pattern;
* Represents metadata for a specific version of a plugin.
*/
public interface PluginDescription {
/**
* The pattern plugin IDs must match. Plugin IDs may only contain
* alphanumeric characters, dashes or underscores, must start with
* an alphabetic character and cannot be longer than 64 characters.
*/
Pattern ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{0,63}");
/**
* Gets the qualified ID of the {@link Plugin} within this container.
*
* @return the plugin ID
* @see Plugin#id()
*/
String getId();
/**
* The pattern plugin IDs must match. Plugin IDs may only contain alphanumeric characters, dashes
* or underscores, must start with an alphabetic character and cannot be longer than 64
* characters.
*/
Pattern ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{0,63}");
/**
* Gets the name of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin name, may be empty
* @see Plugin#name()
*/
default Optional<String> getName() {
return Optional.empty();
}
/**
* Gets the qualified ID of the {@link Plugin} within this container.
*
* @return the plugin ID
* @see Plugin#id()
*/
String getId();
/**
* Gets the version of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin version, may be empty
* @see Plugin#version()
*/
default Optional<String> getVersion() {
return Optional.empty();
}
/**
* Gets the name of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin name, may be empty
* @see Plugin#name()
*/
default Optional<String> getName() {
return Optional.empty();
}
/**
* Gets the description of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin description, may be empty
* @see Plugin#description()
*/
default Optional<String> getDescription() {
return Optional.empty();
}
/**
* Gets the version of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin version, may be empty
* @see Plugin#version()
*/
default Optional<String> getVersion() {
return Optional.empty();
}
/**
* Gets the url or website of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin url, may be empty
* @see Plugin#url()
*/
default Optional<String> getUrl() {
return Optional.empty();
}
/**
* Gets the description of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin description, may be empty
* @see Plugin#description()
*/
default Optional<String> getDescription() {
return Optional.empty();
}
/**
* Gets the authors of the {@link Plugin} within this container.
*
* @return the plugin authors, may be empty
* @see Plugin#authors()
*/
default List<String> getAuthors() {
return ImmutableList.of();
}
/**
* Gets the url or website of the {@link Plugin} within this container.
*
* @return an {@link Optional} with the plugin url, may be empty
* @see Plugin#url()
*/
default Optional<String> getUrl() {
return Optional.empty();
}
/**
* Gets a {@link Collection} of all dependencies of the {@link Plugin} within
* this container.
*
* @return the plugin dependencies, can be empty
* @see Plugin#dependencies()
*/
default Collection<PluginDependency> getDependencies() {
return ImmutableSet.of();
}
/**
* Gets the authors of the {@link Plugin} within this container.
*
* @return the plugin authors, may be empty
* @see Plugin#authors()
*/
default List<String> getAuthors() {
return ImmutableList.of();
}
default Optional<PluginDependency> getDependency(String id) {
return Optional.empty();
}
/**
* Gets a {@link Collection} of all dependencies of the {@link Plugin} within this container.
*
* @return the plugin dependencies, can be empty
* @see Plugin#dependencies()
*/
default Collection<PluginDependency> getDependencies() {
return ImmutableSet.of();
}
/**
* Returns the source the plugin was loaded from.
*
* @return the source the plugin was loaded from or {@link Optional#empty()}
* if unknown
*/
default Optional<Path> getSource() {
return Optional.empty();
}
default Optional<PluginDependency> getDependency(String id) {
return Optional.empty();
}
/**
* Returns the source the plugin was loaded from.
*
* @return the source the plugin was loaded from or {@link Optional#empty()} if unknown
*/
default Optional<Path> getSource() {
return Optional.empty();
}
}

Datei anzeigen

@ -5,47 +5,49 @@ import java.util.Collection;
import java.util.Optional;
/**
* Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from plugin instances
* and inject arbitrary JAR files into the plugin classpath with {@link #addToClasspath(Object, Path)}.
* Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from
* plugin instances and inject arbitrary JAR files into the plugin classpath with {@link
* #addToClasspath(Object, Path)}.
*/
public interface PluginManager {
/**
* Gets the plugin container from an instance.
*
* @param instance the instance
* @return the container
*/
Optional<PluginContainer> fromInstance(Object instance);
/**
* Retrieves a {@link PluginContainer} based on its ID.
*
* @param id the plugin ID
* @return the plugin, if available
*/
Optional<PluginContainer> getPlugin(String id);
/**
* Gets the plugin container from an instance.
*
* @param instance the instance
* @return the container
*/
Optional<PluginContainer> fromInstance(Object instance);
/**
* Gets a {@link Collection} of all {@link PluginContainer}s.
*
* @return the plugins
*/
Collection<PluginContainer> getPlugins();
/**
* Retrieves a {@link PluginContainer} based on its ID.
*
* @param id the plugin ID
* @return the plugin, if available
*/
Optional<PluginContainer> getPlugin(String id);
/**
* Checks if a plugin is loaded based on its ID.
*
* @param id the id of the plugin
* @return {@code true} if loaded
*/
boolean isLoaded(String id);
/**
* Gets a {@link Collection} of all {@link PluginContainer}s.
*
* @return the plugins
*/
Collection<PluginContainer> getPlugins();
/**
* Adds the specified {@code path} to the plugin classpath.
*
* @param plugin the plugin
* @param path the path to the JAR you want to inject into the classpath
* @throws UnsupportedOperationException if the operation is not applicable to this plugin
*/
void addToClasspath(Object plugin, Path path);
/**
* Checks if a plugin is loaded based on its ID.
*
* @param id the id of the plugin
* @return {@code true} if loaded
*/
boolean isLoaded(String id);
/**
* Adds the specified {@code path} to the plugin classpath.
*
* @param plugin the plugin
* @param path the path to the JAR you want to inject into the classpath
* @throws UnsupportedOperationException if the operation is not applicable to this plugin
*/
void addToClasspath(Object plugin, Path path);
}

Datei anzeigen

@ -1,18 +1,18 @@
package com.velocitypowered.api.plugin.annotation;
import com.google.inject.BindingAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation requests that Velocity inject a {@link java.nio.file.Path} instance with a plugin-specific data
* directory.
* This annotation requests that Velocity inject a {@link java.nio.file.Path} instance with a
* plugin-specific data directory.
*/
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@BindingAnnotation
public @interface DataDirectory {
}

Datei anzeigen

@ -1,80 +1,83 @@
package com.velocitypowered.api.plugin.meta;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.emptyToNull;
import java.util.Objects;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents a dependency on another plugin.
*/
public final class PluginDependency {
private final String id;
@Nullable
private final String version;
private final boolean optional;
private final String id;
@Nullable
private final String version;
public PluginDependency(String id, @Nullable String version, boolean optional) {
this.id = checkNotNull(id, "id");
checkArgument(!id.isEmpty(), "id cannot be empty");
this.version = emptyToNull(version);
this.optional = optional;
private final boolean optional;
public PluginDependency(String id, @Nullable String version, boolean optional) {
this.id = checkNotNull(id, "id");
checkArgument(!id.isEmpty(), "id cannot be empty");
this.version = emptyToNull(version);
this.optional = optional;
}
/**
* Returns the plugin ID of this {@link PluginDependency}
*
* @return the plugin ID
*/
public String getId() {
return id;
}
/**
* Returns the version this {@link PluginDependency} should match.
*
* @return an {@link Optional} with the plugin version, may be empty
*/
public Optional<String> getVersion() {
return Optional.ofNullable(version);
}
/**
* Returns whether the dependency is optional for the plugin to work correctly.
*
* @return true if dependency is optional
*/
public boolean isOptional() {
return optional;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
/**
* Returns the plugin ID of this {@link PluginDependency}
*
* @return the plugin ID
*/
public String getId() {
return id;
if (o == null || getClass() != o.getClass()) {
return false;
}
PluginDependency that = (PluginDependency) o;
return optional == that.optional &&
Objects.equals(id, that.id) &&
Objects.equals(version, that.version);
}
/**
* Returns the version this {@link PluginDependency} should match.
*
* @return an {@link Optional} with the plugin version, may be empty
*/
public Optional<String> getVersion() {
return Optional.ofNullable(version);
}
@Override
public int hashCode() {
return Objects.hash(id, version, optional);
}
/**
* Returns whether the dependency is optional for the plugin to work
* correctly.
*
* @return true if dependency is optional
*/
public boolean isOptional() {
return optional;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PluginDependency that = (PluginDependency) o;
return optional == that.optional &&
Objects.equals(id, that.id) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(id, version, optional);
}
@Override
public String toString() {
return "PluginDependency{" +
"id='" + id + '\'' +
", version='" + version + '\'' +
", optional=" + optional +
'}';
}
@Override
public String toString() {
return "PluginDependency{" +
"id='" + id + '\'' +
", version='" + version + '\'' +
", optional=" + optional +
'}';
}
}

Datei anzeigen

@ -1,91 +1,100 @@
package com.velocitypowered.api.proxy;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.text.Component;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import net.kyori.text.Component;
/**
* Provides a fluent interface to compose and send a connection request to another server behind the proxy. A connection
* request is created using {@link Player#createConnectionRequest(RegisteredServer)}.
* Provides a fluent interface to compose and send a connection request to another server behind the
* proxy. A connection request is created using {@link Player#createConnectionRequest(RegisteredServer)}.
*/
public interface ConnectionRequestBuilder {
/**
* Returns the server that this connection request represents.
* @return the server this request will connect to
*/
RegisteredServer getServer();
/**
* Returns the server that this connection request represents.
*
* @return the server this request will connect to
*/
RegisteredServer getServer();
/**
* Initiates the connection to the remote server and emits a result on the {@link
* CompletableFuture} after the user has logged on. No messages will be communicated to the
* client: the user is responsible for all error handling.
*
* @return a {@link CompletableFuture} representing the status of this connection
*/
CompletableFuture<Result> connect();
/**
* Initiates the connection to the remote server and emits a result on the {@link
* CompletableFuture} after the user has logged on. Velocity's own built-in handling will be used
* to provide errors to the client.
*
* @return a {@link CompletableFuture} representing the status of this connection
*/
CompletableFuture<Boolean> connectWithIndication();
/**
* Initiates the connection to the remote server without waiting for a result. Velocity will use
* generic error handling code to notify the user.
*/
void fireAndForget();
/**
* Represents the result of a connection request.
*/
interface Result {
/**
* Initiates the connection to the remote server and emits a result on the {@link CompletableFuture} after the user
* has logged on. No messages will be communicated to the client: the user is responsible for all error handling.
* @return a {@link CompletableFuture} representing the status of this connection
* Determines whether or not the connection request was successful.
*
* @return whether or not the request succeeded
*/
CompletableFuture<Result> connect();
/**
* Initiates the connection to the remote server and emits a result on the {@link CompletableFuture} after the user
* has logged on. Velocity's own built-in handling will be used to provide errors to the client.
* @return a {@link CompletableFuture} representing the status of this connection
*/
CompletableFuture<Boolean> connectWithIndication();
/**
* Initiates the connection to the remote server without waiting for a result. Velocity will use generic error
* handling code to notify the user.
*/
void fireAndForget();
/**
* Represents the result of a connection request.
*/
interface Result {
/**
* Determines whether or not the connection request was successful.
* @return whether or not the request succeeded
*/
default boolean isSuccessful() {
return getStatus() == Status.SUCCESS;
}
/**
* Returns the status associated with this result.
* @return the status for this result
*/
Status getStatus();
/**
* Returns an (optional) textual reason for the failure to connect to the server.
* @return the reason why the user could not connect to the server
*/
Optional<Component> getReason();
default boolean isSuccessful() {
return getStatus() == Status.SUCCESS;
}
/**
* Represents the status of a connection request initiated by a {@link ConnectionRequestBuilder}.
* Returns the status associated with this result.
*
* @return the status for this result
*/
enum Status {
/**
* The player was successfully connected to the server.
*/
SUCCESS,
/**
* The player is already connected to this server.
*/
ALREADY_CONNECTED,
/**
* The connection is already in progress.
*/
CONNECTION_IN_PROGRESS,
/**
* A plugin has cancelled this connection.
*/
CONNECTION_CANCELLED,
/**
* The server disconnected the user. A reason may be provided in the {@link Result} object.
*/
SERVER_DISCONNECTED
}
Status getStatus();
/**
* Returns an (optional) textual reason for the failure to connect to the server.
*
* @return the reason why the user could not connect to the server
*/
Optional<Component> getReason();
}
/**
* Represents the status of a connection request initiated by a {@link ConnectionRequestBuilder}.
*/
enum Status {
/**
* The player was successfully connected to the server.
*/
SUCCESS,
/**
* The player is already connected to this server.
*/
ALREADY_CONNECTED,
/**
* The connection is already in progress.
*/
CONNECTION_IN_PROGRESS,
/**
* A plugin has cancelled this connection.
*/
CONNECTION_CANCELLED,
/**
* The server disconnected the user. A reason may be provided in the {@link Result} object.
*/
SERVER_DISCONNECTED
}
}

Datei anzeigen

@ -7,27 +7,32 @@ import java.util.Optional;
* Represents an incoming connection to the proxy.
*/
public interface InboundConnection {
/**
* Returns the player's IP address.
* @return the player's IP
*/
InetSocketAddress getRemoteAddress();
/**
* Returns the hostname that the user entered into the client, if applicable.
* @return the hostname from the client
*/
Optional<InetSocketAddress> getVirtualHost();
/**
* Returns the player's IP address.
*
* @return the player's IP
*/
InetSocketAddress getRemoteAddress();
/**
* Determine whether or not the player remains online.
* @return whether or not the player active
*/
boolean isActive();
/**
* Returns the hostname that the user entered into the client, if applicable.
*
* @return the hostname from the client
*/
Optional<InetSocketAddress> getVirtualHost();
/**
* Returns the current protocol version this connection uses.
* @return the protocol version the connection uses
*/
int getProtocolVersion();
/**
* Determine whether or not the player remains online.
*
* @return whether or not the player active
*/
boolean isActive();
/**
* Returns the current protocol version this connection uses.
*
* @return the protocol version the connection uses
*/
int getProtocolVersion();
}

Datei anzeigen

@ -4,136 +4,150 @@ import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
import com.velocitypowered.api.proxy.player.PlayerSettings;
import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.api.proxy.player.TabList;
import com.velocitypowered.api.util.MessagePosition;
import com.velocitypowered.api.util.ModInfo;
import com.velocitypowered.api.util.title.Title;
import java.util.List;
import net.kyori.text.Component;
import java.util.Optional;
import java.util.UUID;
import net.kyori.text.Component;
/**
* Represents a player who is connected to the proxy.
*/
public interface Player extends CommandSource, InboundConnection, ChannelMessageSource, ChannelMessageSink {
/**
* Returns the player's current username.
* @return the username
*/
String getUsername();
public interface Player extends CommandSource, InboundConnection, ChannelMessageSource,
ChannelMessageSink {
/**
* Returns the player's UUID.
* @return the UUID
*/
UUID getUniqueId();
/**
* Returns the player's current username.
*
* @return the username
*/
String getUsername();
/**
* Returns the server that the player is currently connected to.
* @return an {@link Optional} the server that the player is connected to, which may be empty
*/
Optional<ServerConnection> getCurrentServer();
/**
* Returns the player's UUID.
*
* @return the UUID
*/
UUID getUniqueId();
/**
* Returns the player settings
* @return the settings
*/
PlayerSettings getPlayerSettings();
/**
* Returns the player's mod info if they have a modded client.
* @return an {@link Optional} the mod info. which may be empty
*/
Optional<ModInfo> getModInfo();
/**
* Returns the server that the player is currently connected to.
*
* @return an {@link Optional} the server that the player is connected to, which may be empty
*/
Optional<ServerConnection> getCurrentServer();
/**
* Returns the current player's ping
* @return the player's ping or -1 if ping information is currently unknown
*/
long getPing();
/**
* Sends a chat message to the player's client.
* @param component the chat message to send
*/
default void sendMessage(Component component) {
sendMessage(component, MessagePosition.CHAT);
}
/**
* Returns the player settings
*
* @return the settings
*/
PlayerSettings getPlayerSettings();
/**
* Sends a chat message to the player's client in the specified position.
* @param component the chat message to send
* @param position the position for the message
*/
void sendMessage(Component component, MessagePosition position);
/**
* Returns the player's mod info if they have a modded client.
*
* @return an {@link Optional} the mod info. which may be empty
*/
Optional<ModInfo> getModInfo();
/**
* Creates a new connection request so that the player can connect to another server.
* @param server the server to connect to
* @return a new connection request
*/
ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
/**
* Returns the current player's ping
*
* @return the player's ping or -1 if ping information is currently unknown
*/
long getPing();
/**
* Gets the player's profile properties.
*
* <p>The returned list may be unmodifiable.</p>
*
* @return the player's profile properties
*/
List<GameProfile.Property> getGameProfileProperties();
/**
* Sets the player's profile properties.
*
* @param properties the properties
*/
void setGameProfileProperties(List<GameProfile.Property> properties);
/**
* Sets the tab list header and footer for the player.
* @param header the header component
* @param footer the footer component
* @deprecated Use {@link TabList#setHeaderAndFooter(Component, Component)}.
*/
@Deprecated
void setHeaderAndFooter(Component header, Component footer);
/**
* Sends a chat message to the player's client.
*
* @param component the chat message to send
*/
default void sendMessage(Component component) {
sendMessage(component, MessagePosition.CHAT);
}
/**
* Clears the tab list header and footer for the player.
* @deprecated Use {@link TabList#clearHeaderAndFooter()}.
*/
@Deprecated
void clearHeaderAndFooter();
/**
* Returns the player's tab list.
* @return this player's tab list
*/
TabList getTabList();
/**
* Sends a chat message to the player's client in the specified position.
*
* @param component the chat message to send
* @param position the position for the message
*/
void sendMessage(Component component, MessagePosition position);
/**
* Disconnects the player with the specified reason. Once this method is called, further calls to other {@link Player}
* methods will become undefined.
* @param reason component with the reason
*/
void disconnect(Component reason);
/**
* Creates a new connection request so that the player can connect to another server.
*
* @param server the server to connect to
* @return a new connection request
*/
ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
/**
* Sends the specified title to the client.
* @param title the title to send
*/
void sendTitle(Title title);
/**
* Gets the player's profile properties.
*
* <p>The returned list may be unmodifiable.</p>
*
* @return the player's profile properties
*/
List<GameProfile.Property> getGameProfileProperties();
/**
* Sends chat input onto the players current server as if they typed it
* into the client chat box.
* @param input the chat input to send
*/
void spoofChatInput(String input);
/**
* Sets the player's profile properties.
*
* @param properties the properties
*/
void setGameProfileProperties(List<GameProfile.Property> properties);
/**
* Sets the tab list header and footer for the player.
*
* @param header the header component
* @param footer the footer component
* @deprecated Use {@link TabList#setHeaderAndFooter(Component, Component)}.
*/
@Deprecated
void setHeaderAndFooter(Component header, Component footer);
/**
* Clears the tab list header and footer for the player.
*
* @deprecated Use {@link TabList#clearHeaderAndFooter()}.
*/
@Deprecated
void clearHeaderAndFooter();
/**
* Returns the player's tab list.
*
* @return this player's tab list
*/
TabList getTabList();
/**
* Disconnects the player with the specified reason. Once this method is called, further calls to
* other {@link Player} methods will become undefined.
*
* @param reason component with the reason
*/
void disconnect(Component reason);
/**
* Sends the specified title to the client.
*
* @param title the title to send
*/
void sendTitle(Title title);
/**
* Sends chat input onto the players current server as if they typed it into the client chat box.
*
* @param input the chat input to send
*/
void spoofChatInput(String input);
}

Datei anzeigen

@ -10,132 +10,151 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import com.velocitypowered.api.scheduler.Scheduler;
import com.velocitypowered.api.util.ProxyVersion;
import net.kyori.text.Component;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
import net.kyori.text.Component;
/**
* Provides an interface to a Minecraft server proxy.
*/
public interface ProxyServer {
/**
* Retrieves the player currently connected to this proxy by their Minecraft username. The search is case-insensitive.
* @param username the username to search for
* @return an {@link Optional} with the player, which may be empty
*/
Optional<Player> getPlayer(String username);
/**
* Retrieves the player currently connected to this proxy by their Minecraft UUID.
* @param uuid the UUID
* @return an {@link Optional} with the player, which may be empty
*/
Optional<Player> getPlayer(UUID uuid);
/**
* Retrieves the player currently connected to this proxy by their Minecraft username. The search
* is case-insensitive.
*
* @param username the username to search for
* @return an {@link Optional} with the player, which may be empty
*/
Optional<Player> getPlayer(String username);
/**
* Broadcasts a message to all players currently online.
* @param component the message to send
*/
void broadcast(Component component);
/**
* Retrieves the player currently connected to this proxy by their Minecraft UUID.
*
* @param uuid the UUID
* @return an {@link Optional} with the player, which may be empty
*/
Optional<Player> getPlayer(UUID uuid);
/**
* Retrieves all players currently connected to this proxy. This call may or may not be a snapshot of all players
* online.
* @return the players online on this proxy
*/
Collection<Player> getAllPlayers();
/**
* Broadcasts a message to all players currently online.
*
* @param component the message to send
*/
void broadcast(Component component);
/**
* Returns the number of players currently connected to this proxy.
* @return the players on this proxy
*/
int getPlayerCount();
/**
* Retrieves all players currently connected to this proxy. This call may or may not be a snapshot
* of all players online.
*
* @return the players online on this proxy
*/
Collection<Player> getAllPlayers();
/**
* Retrieves a registered {@link RegisteredServer} instance by its name. The search is case-insensitive.
* @param name the name of the server
* @return the registered server, which may be empty
*/
Optional<RegisteredServer> getServer(String name);
/**
* Returns the number of players currently connected to this proxy.
*
* @return the players on this proxy
*/
int getPlayerCount();
/**
* Retrieves all {@link RegisteredServer}s registered with this proxy.
* @return the servers registered with this proxy
*/
Collection<RegisteredServer> getAllServers();
/**
* Retrieves a registered {@link RegisteredServer} instance by its name. The search is
* case-insensitive.
*
* @param name the name of the server
* @return the registered server, which may be empty
*/
Optional<RegisteredServer> getServer(String name);
/**
* Registers a server with this proxy. A server with this name should not already exist.
* @param server the server to register
* @return the newly registered server
*/
RegisteredServer registerServer(ServerInfo server);
/**
* Retrieves all {@link RegisteredServer}s registered with this proxy.
*
* @return the servers registered with this proxy
*/
Collection<RegisteredServer> getAllServers();
/**
* Unregisters this server from the proxy.
* @param server the server to unregister
*/
void unregisterServer(ServerInfo server);
/**
* Registers a server with this proxy. A server with this name should not already exist.
*
* @param server the server to register
* @return the newly registered server
*/
RegisteredServer registerServer(ServerInfo server);
/**
* Returns an instance of {@link CommandSource} that can be used to determine if the command is being invoked by
* the console or a console-like executor. Plugins that execute commands are strongly urged to implement their own
* {@link CommandSource} instead of using the console invoker.
* @return the console command invoker
*/
CommandSource getConsoleCommandSource();
/**
* Unregisters this server from the proxy.
*
* @param server the server to unregister
*/
void unregisterServer(ServerInfo server);
/**
* Gets the {@link PluginManager} instance.
*
* @return the plugin manager instance
*/
PluginManager getPluginManager();
/**
* Returns an instance of {@link CommandSource} that can be used to determine if the command is
* being invoked by the console or a console-like executor. Plugins that execute commands are
* strongly urged to implement their own {@link CommandSource} instead of using the console
* invoker.
*
* @return the console command invoker
*/
CommandSource getConsoleCommandSource();
/**
* Gets the {@link EventManager} instance.
*
* @return the event manager instance
*/
EventManager getEventManager();
/**
* Gets the {@link PluginManager} instance.
*
* @return the plugin manager instance
*/
PluginManager getPluginManager();
/**
* Gets the {@link CommandManager} instance.
* @return the command manager
*/
CommandManager getCommandManager();
/**
* Gets the {@link EventManager} instance.
*
* @return the event manager instance
*/
EventManager getEventManager();
/**
* Gets the {@link Scheduler} instance.
* @return the scheduler instance
*/
Scheduler getScheduler();
/**
* Gets the {@link CommandManager} instance.
*
* @return the command manager
*/
CommandManager getCommandManager();
/**
* Gets the {@link ChannelRegistrar} instance.
* @return the channel registrar
*/
ChannelRegistrar getChannelRegistrar();
/**
* Gets the {@link Scheduler} instance.
*
* @return the scheduler instance
*/
Scheduler getScheduler();
/**
* Gets the address that this proxy is bound to. This does not necessarily indicate the external IP address of the
* proxy.
* @return the address the proxy is bound to
*/
InetSocketAddress getBoundAddress();
/**
* Gets the {@link ChannelRegistrar} instance.
*
* @return the channel registrar
*/
ChannelRegistrar getChannelRegistrar();
/**
* Gets the {@link ProxyConfig} instance.
* @return the proxy config
* */
ProxyConfig getConfiguration();
/**
* Gets the address that this proxy is bound to. This does not necessarily indicate the external
* IP address of the proxy.
*
* @return the address the proxy is bound to
*/
InetSocketAddress getBoundAddress();
/**
* Returns the version of the proxy.
* @return the proxy version
*/
ProxyVersion getVersion();
/**
* Gets the {@link ProxyConfig} instance.
*
* @return the proxy config
*/
ProxyConfig getConfiguration();
/**
* Returns the version of the proxy.
*
* @return the proxy version
*/
ProxyVersion getVersion();
}

Datei anzeigen

@ -9,21 +9,25 @@ import com.velocitypowered.api.proxy.server.ServerInfo;
* Represents a connection to a backend server from the proxy for a client.
*/
public interface ServerConnection extends ChannelMessageSource, ChannelMessageSink {
/**
* Returns the server that this connection is connected to.
* @return the server this connection is connected to
*/
RegisteredServer getServer();
/**
* Returns the server info for this connection.
* @return the server info for this connection
*/
ServerInfo getServerInfo();
/**
* Returns the server that this connection is connected to.
*
* @return the server this connection is connected to
*/
RegisteredServer getServer();
/**
* Returns the player that this connection is associated with.
* @return the player for this connection
*/
Player getPlayer();
/**
* Returns the server info for this connection.
*
* @return the server info for this connection
*/
ServerInfo getServerInfo();
/**
* Returns the player that this connection is associated with.
*
* @return the player for this connection
*/
Player getPlayer();
}

Datei anzeigen

@ -1,116 +1,133 @@
package com.velocitypowered.api.proxy.config;
import com.velocitypowered.api.util.Favicon;
import net.kyori.text.Component;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import net.kyori.text.Component;
/**
* Provides an interface to a proxy configuration
*/
public interface ProxyConfig {
/**
* Whether GameSpy 4 queries are accepted by the proxy
* @return queries enabled
*/
boolean isQueryEnabled();
/**
* Get the port GameSpy 4 queries are accepted on
* @return the query port
*/
int getQueryPort();
/**
* Whether GameSpy 4 queries are accepted by the proxy
*
* @return queries enabled
*/
boolean isQueryEnabled();
/**
* Get the map name reported to GameSpy 4 query services
* @return the map name
*/
String getQueryMap();
/**
* Get the port GameSpy 4 queries are accepted on
*
* @return the query port
*/
int getQueryPort();
/**
* Whether GameSpy 4 queries should show plugins installed on
* Velocity by default
* @return show plugins in query
*/
boolean shouldQueryShowPlugins();
/**
* Get the map name reported to GameSpy 4 query services
*
* @return the map name
*/
String getQueryMap();
/**
* Get the MOTD component shown in the tab list
* @return the motd component
*/
Component getMotdComponent();
/**
* Whether GameSpy 4 queries should show plugins installed on Velocity by default
*
* @return show plugins in query
*/
boolean shouldQueryShowPlugins();
/**
* Get the maximum players shown in the tab list
* @return max players
*/
int getShowMaxPlayers();
/**
* Get the MOTD component shown in the tab list
*
* @return the motd component
*/
Component getMotdComponent();
/**
* Get whether the proxy is online mode. This determines if players are authenticated with Mojang servers.
* @return online mode enabled
*/
boolean isOnlineMode();
/**
* Get the maximum players shown in the tab list
*
* @return max players
*/
int getShowMaxPlayers();
/**
* Get a Map of all servers registered on this proxy
* @return registered servers map
*/
Map<String, String> getServers();
/**
* Get whether the proxy is online mode. This determines if players are authenticated with Mojang
* servers.
*
* @return online mode enabled
*/
boolean isOnlineMode();
/**
* Get the order of servers that players will be connected to
* @return connection order list
*/
List<String> getAttemptConnectionOrder();
/**
* Get a Map of all servers registered on this proxy
*
* @return registered servers map
*/
Map<String, String> getServers();
/**
* Get forced servers mapped to given virtual host
* @return list of server names
*/
Map<String, List<String>> getForcedHosts();
/**
* Get the order of servers that players will be connected to
*
* @return connection order list
*/
List<String> getAttemptConnectionOrder();
/**
* Get the minimum compression threshold for packets
* @return the compression threshold
*/
int getCompressionThreshold();
/**
* Get forced servers mapped to given virtual host
*
* @return list of server names
*/
Map<String, List<String>> getForcedHosts();
/**
* Get the level of compression that packets will be compressed to
* @return the compression level
*/
int getCompressionLevel();
/**
* Get the minimum compression threshold for packets
*
* @return the compression threshold
*/
int getCompressionThreshold();
/**
* Get the limit for how long a player must wait to log back in
* @return the login rate limit (in milliseconds)
*/
int getLoginRatelimit();
/**
* Get the level of compression that packets will be compressed to
*
* @return the compression level
*/
int getCompressionLevel();
/**
* Get the proxy favicon shown in the tablist
* @return optional favicon
*/
Optional<Favicon> getFavicon();
/**
* Get the limit for how long a player must wait to log back in
*
* @return the login rate limit (in milliseconds)
*/
int getLoginRatelimit();
/**
* Get whether this proxy displays that it supports Forge/FML
* @return forge announce enabled
*/
boolean isAnnounceForge();
/**
* Get the proxy favicon shown in the tablist
*
* @return optional favicon
*/
Optional<Favicon> getFavicon();
/**
* Get how long this proxy will wait until performing a read timeout
* @return connection timeout (in milliseconds)
*/
int getConnectTimeout();
/**
* Get whether this proxy displays that it supports Forge/FML
*
* @return forge announce enabled
*/
boolean isAnnounceForge();
/**
* Get how long this proxy will wait until performing a read timeout
* @return read timeout (in milliseconds)
*/
int getReadTimeout();
/**
* Get how long this proxy will wait until performing a read timeout
*
* @return connection timeout (in milliseconds)
*/
int getConnectTimeout();
/**
* Get how long this proxy will wait until performing a read timeout
*
* @return read timeout (in milliseconds)
*/
int getReadTimeout();
}

Datei anzeigen

@ -4,9 +4,11 @@ package com.velocitypowered.api.proxy.messages;
* Represents a kind of channel identifier.
*/
public interface ChannelIdentifier {
/**
* Returns the textual representation of this identifier.
* @return the textual representation of the identifier
*/
String getId();
/**
* Returns the textual representation of this identifier.
*
* @return the textual representation of the identifier
*/
String getId();
}

Datei anzeigen

@ -4,11 +4,13 @@ package com.velocitypowered.api.proxy.messages;
* Represents something that can send plugin messages.
*/
public interface ChannelMessageSink {
/**
* Sends a plugin message to this target.
* @param identifier the channel identifier to send the message on
* @param data the data to send
* @return whether or not the message could be sent
*/
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data);
/**
* Sends a plugin message to this target.
*
* @param identifier the channel identifier to send the message on
* @param data the data to send
* @return whether or not the message could be sent
*/
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data);
}

Datei anzeigen

@ -4,4 +4,5 @@ package com.velocitypowered.api.proxy.messages;
* A marker interface that indicates a source of plugin messages.
*/
public interface ChannelMessageSource {
}

Datei anzeigen

@ -1,18 +1,22 @@
package com.velocitypowered.api.proxy.messages;
/**
* Represents an interface to register and unregister {@link ChannelIdentifier}s for the proxy to listen on.
* Represents an interface to register and unregister {@link ChannelIdentifier}s for the proxy to
* listen on.
*/
public interface ChannelRegistrar {
/**
* Registers the specified message identifiers to listen on for the
* @param identifiers the channel identifiers to register
*/
void register(ChannelIdentifier... identifiers);
/**
* Unregisters the handler for the specified channel.
* @param identifiers the identifiers to unregister
*/
void unregister(ChannelIdentifier... identifiers);
/**
* Registers the specified message identifiers to listen on for the
*
* @param identifiers the channel identifiers to register
*/
void register(ChannelIdentifier... identifiers);
/**
* Unregisters the handler for the specified channel.
*
* @param identifiers the identifiers to unregister
*/
void unregister(ChannelIdentifier... identifiers);
}

Datei anzeigen

@ -2,46 +2,51 @@ 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;
import java.util.Objects;
/**
* 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.
* 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;
public LegacyChannelIdentifier(String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "provided name is empty");
this.name = name;
}
private final String name;
public String getName() {
return name;
}
public LegacyChannelIdentifier(String name) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "provided name is empty");
this.name = name;
}
@Override
public String toString() {
return name + " (legacy)";
}
public String getName() {
return name;
}
@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 String toString() {
return name + " (legacy)";
}
@Override
public int hashCode() {
return Objects.hash(name);
@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 String getId() {
return this.getName();
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public String getId() {
return this.getName();
}
}

Datei anzeigen

@ -2,78 +2,87 @@ package com.velocitypowered.api.proxy.messages;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.checkerframework.checker.nullness.qual.Nullable;
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.
* 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 static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9\\-_]+");
private MinecraftChannelIdentifier(String namespace, String name) {
this.namespace = namespace;
this.name = name;
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(!Strings.isNullOrEmpty(name), "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);
}
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;
}
/**
* 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);
if (o == null || getClass() != o.getClass()) {
return false;
}
MinecraftChannelIdentifier that = (MinecraftChannelIdentifier) o;
return Objects.equals(namespace, that.namespace) &&
Objects.equals(name, that.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(!Strings.isNullOrEmpty(name), "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);
}
@Override
public int hashCode() {
return Objects.hash(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 getId() {
return namespace + ":" + name;
}
@Override
public String getId() {
return namespace + ":" + name;
}
}

Datei anzeigen

@ -1,4 +1,5 @@
/**
* Provides an interface to receive, handle, and send plugin messages on the proxy from clients and servers.
* Provides an interface to receive, handle, and send plugin messages on the proxy from clients and
* servers.
*/
package com.velocitypowered.api.proxy.messages;

Datei anzeigen

@ -6,51 +6,58 @@ import java.util.Locale;
* Represents the client settings for the player.
*/
public interface PlayerSettings {
/**
* Returns the locale of the Minecraft client.
* @return the client locale
*/
Locale getLocale();
/**
* Returns the client's view distance. This does not guarantee the client will see this many chunks, since your
* servers are responsible for sending the chunks.
* @return the client view distance
*/
byte getViewDistance();
/**
* Returns the locale of the Minecraft client.
*
* @return the client locale
*/
Locale getLocale();
/**
* Returns the chat setting for the client.
* @return the chat setting
*/
ChatMode getChatMode();
/**
* Returns the client's view distance. This does not guarantee the client will see this many
* chunks, since your servers are responsible for sending the chunks.
*
* @return the client view distance
*/
byte getViewDistance();
/**
* Returns whether or not the client has chat colors disabled.
* @return whether or not the client has chat colors disabled
*/
boolean hasChatColors();
/**
* Returns the chat setting for the client.
*
* @return the chat setting
*/
ChatMode getChatMode();
/**
* Returns the parts of player skins the client will show.
* @return the skin parts for the client
*/
SkinParts getSkinParts();
/**
* Returns whether or not the client has chat colors disabled.
*
* @return whether or not the client has chat colors disabled
*/
boolean hasChatColors();
/**
* Returns the primary hand of the client.
* @return the primary hand of the client
*/
MainHand getMainHand();
/**
* Returns the parts of player skins the client will show.
*
* @return the skin parts for the client
*/
SkinParts getSkinParts();
enum ChatMode {
SHOWN,
COMMANDS_ONLY,
HIDDEN
}
/**
* Returns the primary hand of the client.
*
* @return the primary hand of the client
*/
MainHand getMainHand();
enum MainHand {
LEFT,
RIGHT
}
enum ChatMode {
SHOWN,
COMMANDS_ONLY,
HIDDEN
}
enum MainHand {
LEFT,
RIGHT
}
}

Datei anzeigen

@ -1,37 +1,38 @@
package com.velocitypowered.api.proxy.player;
public final class SkinParts {
private final byte bitmask;
public SkinParts(byte skinBitmask) {
this.bitmask = skinBitmask;
}
private final byte bitmask;
public boolean hasCape() {
return (bitmask & 1) == 1;
}
public SkinParts(byte skinBitmask) {
this.bitmask = skinBitmask;
}
public boolean hasJacket() {
return ((bitmask >> 1) & 1) == 1;
}
public boolean hasCape() {
return (bitmask & 1) == 1;
}
public boolean hasLeftSleeve() {
return ((bitmask >> 2) & 1) == 1;
}
public boolean hasJacket() {
return ((bitmask >> 1) & 1) == 1;
}
public boolean hasRightSleeve() {
return ((bitmask >> 3) & 1) == 1;
}
public boolean hasLeftSleeve() {
return ((bitmask >> 2) & 1) == 1;
}
public boolean hasLeftPants() {
return ((bitmask >> 4) & 1) == 1;
}
public boolean hasRightSleeve() {
return ((bitmask >> 3) & 1) == 1;
}
public boolean hasRightPants() {
return ((bitmask >> 5) & 1) == 1;
}
public boolean hasLeftPants() {
return ((bitmask >> 4) & 1) == 1;
}
public boolean hasHat() {
return ((bitmask >> 6) & 1) == 1;
}
public boolean hasRightPants() {
return ((bitmask >> 5) & 1) == 1;
}
public boolean hasHat() {
return ((bitmask >> 6) & 1) == 1;
}
}

Datei anzeigen

@ -2,51 +2,56 @@ package com.velocitypowered.api.proxy.player;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.util.GameProfile;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the tab list of a {@link Player}.
*/
public interface TabList {
/**
* Sets the tab list header and footer for the player.
* @param header the header component
* @param footer the footer component
*/
void setHeaderAndFooter(Component header, Component footer);
/**
* Clears the tab list header and footer for the player.
*/
void clearHeaderAndFooter();
/**
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
* @param entry to add to the tab list
*/
void addEntry(TabListEntry entry);
/**
* Removes the {@link TabListEntry} from the tab list with the {@link GameProfile}
* identified with the specified {@link UUID}.
* @param uuid of the
* @return {@link Optional} containing the removed {@link TabListEntry} if present,
* otherwise {@link Optional#empty()}
*/
Optional<TabListEntry> removeEntry(UUID uuid);
/**
* Returns an immutable {@link Collection} of the {@link TabListEntry}s in the tab list.
* @return immutable {@link Collection} of tab list entries
*/
Collection<TabListEntry> getEntries();
/**
* Sets the tab list header and footer for the player.
*
* @param header the header component
* @param footer the footer component
*/
void setHeaderAndFooter(Component header, Component footer);
// Necessary because the TabListEntry implementation isn't in the api
@Deprecated
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, int gameMode);
/**
* Clears the tab list header and footer for the player.
*/
void clearHeaderAndFooter();
/**
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
*
* @param entry to add to the tab list
*/
void addEntry(TabListEntry entry);
/**
* Removes the {@link TabListEntry} from the tab list with the {@link GameProfile} identified with
* the specified {@link UUID}.
*
* @param uuid of the
* @return {@link Optional} containing the removed {@link TabListEntry} if present, otherwise
* {@link Optional#empty()}
*/
Optional<TabListEntry> removeEntry(UUID uuid);
/**
* Returns an immutable {@link Collection} of the {@link TabListEntry}s in the tab list.
*
* @return immutable {@link Collection} of tab list entries
*/
Collection<TabListEntry> getEntries();
// Necessary because the TabListEntry implementation isn't in the api
@Deprecated
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
int gameMode);
}

Datei anzeigen

@ -1,177 +1,195 @@
package com.velocitypowered.api.proxy.player;
import com.velocitypowered.api.util.GameProfile;
import java.util.Optional;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Optional;
/**
* Represents a single entry in a {@link TabList}.
*/
public interface TabListEntry {
/**
* Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}.
* @return parent {@link TabList}
*/
TabList getTabList();
/**
* Returns the {@link GameProfile} of the entry, which uniquely identifies the entry
* with the containing {@link java.util.UUID}, as well as deciding what is shown
* as the player head in the tab list.
* @return {@link GameProfile} of the entry
*/
GameProfile getProfile();
/**
* Returns {@link Optional} text {@link Component}, which if present is the text displayed for
* {@code this} entry in the {@link TabList}, otherwise {@link GameProfile#getName()} is shown.
* @return {@link Optional} text {@link Component} of name displayed in the tab list
*/
Optional<Component> getDisplayName();
/**
* Sets the text {@link Component} to be displayed for {@code this} {@link TabListEntry}.
* If {@code null}, {@link GameProfile#getName()} will be shown.
* @param displayName to show in the {@link TabList} for {@code this} entry
* @return {@code this}, for chaining
*/
TabListEntry setDisplayName(@Nullable Component displayName);
/**
* Returns the latency for {@code this} entry.
* <p>The icon shown in the tab list is calculated by the latency in the following way:<p>
* <ul>
* <li>A negative latency will display the no connection icon</li>
* <li>0-150 will display 5 bars</li>
* <li>150-300 will display 4 bars</li>
* <li>300-600 will display 3 bars</li>
* <li>600-1000 will display 2 bars</li>
* <li>A latency move than 1 second will display 1 bar</li>
* <li></li>
* </ul>
* @return latency set for {@code this} entry
*/
int getLatency();
/**
* Sets the latency for {@code this} entry to the specified value
* @see #getLatency()
* @param latency to changed to
* @return {@code this}, for chaining
*/
TabListEntry setLatency(int latency);
/**
* Gets the game mode {@code this} entry has been set to.
* <p>The number corresponds to the game mode in the following way:</p>
* <ol start="0">
* <li>Survival</li>
* <li>Creative</li>
* <li>Adventure</li>
* <li>Spectator</li>
* </ol>
* @return the game mode
*/
int getGameMode();
/**
* Sets the game mode for {@code this} entry to the specified value
* @see #getGameMode()
* @param gameMode to change to
* @return {@code this}, for chaining
*/
TabListEntry setGameMode(int gameMode);
/**
* Returns a {@link Builder} to create a {@link TabListEntry}.
* @return {@link TabListEntry} builder
*/
static Builder builder() {
return new Builder();
}
/**
* Represents a builder which creates {@link TabListEntry}s.
* @see TabListEntry
*/
class Builder {
private @Nullable TabList tabList;
private @Nullable GameProfile profile;
private @Nullable Component displayName;
private int latency = 0;
private int gameMode = 0;
private Builder() {}
/**
* Sets the parent {@link TabList} for this entry,
* the entry will only be able to be added to that specific {@link TabList}.
* @param tabList to set
* @return {@code this}, for chaining
*/
public Builder tabList(TabList tabList) {
this.tabList = tabList;
return this;
}
/**
* Sets the {@link GameProfile} of the {@link TabListEntry}.
* @see TabListEntry#getProfile()
* @param profile to set
* @return {@code this}, for chaining
*/
public Builder profile(GameProfile profile) {
this.profile = profile;
return this;
}
/**
* Sets the displayed name of the {@link TabListEntry}
* @see TabListEntry#getDisplayName()
* @param displayName to set
* @return {@code this}, for chaining
*/
public Builder displayName(@Nullable Component displayName) {
this.displayName = displayName;
return this;
}
/**
* Sets the latency of the {@link TabListEntry}
* @see TabListEntry#getLatency()
* @param latency to set
* @return {@code this}, for chaining
*/
public Builder latency(int latency) {
this.latency = latency;
return this;
}
/**
* Sets the game mode of the {@link TabListEntry}
* @see TabListEntry#getGameMode()
* @param gameMode to set
* @return {@code this}, for chaining
*/
public Builder gameMode(int gameMode) {
this.gameMode = gameMode;
return this;
}
/**
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
* @return the constructed {@link TabListEntry}
*/
public TabListEntry build() {
if (tabList == null) {
throw new IllegalStateException("The Tablist must be set when building a TabListEntry");
}
if (profile == null) {
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
}
return tabList.buildEntry(profile, displayName, latency, gameMode);
}
/**
* Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}.
*
* @return parent {@link TabList}
*/
TabList getTabList();
/**
* Returns the {@link GameProfile} of the entry, which uniquely identifies the entry with the
* containing {@link java.util.UUID}, as well as deciding what is shown as the player head in the
* tab list.
*
* @return {@link GameProfile} of the entry
*/
GameProfile getProfile();
/**
* Returns {@link Optional} text {@link Component}, which if present is the text displayed for
* {@code this} entry in the {@link TabList}, otherwise {@link GameProfile#getName()} is shown.
*
* @return {@link Optional} text {@link Component} of name displayed in the tab list
*/
Optional<Component> getDisplayName();
/**
* Sets the text {@link Component} to be displayed for {@code this} {@link TabListEntry}. If
* {@code null}, {@link GameProfile#getName()} will be shown.
*
* @param displayName to show in the {@link TabList} for {@code this} entry
* @return {@code this}, for chaining
*/
TabListEntry setDisplayName(@Nullable Component displayName);
/**
* Returns the latency for {@code this} entry.
* <p>The icon shown in the tab list is calculated by the latency in the following way:<p>
* <ul>
* <li>A negative latency will display the no connection icon</li>
* <li>0-150 will display 5 bars</li>
* <li>150-300 will display 4 bars</li>
* <li>300-600 will display 3 bars</li>
* <li>600-1000 will display 2 bars</li>
* <li>A latency move than 1 second will display 1 bar</li>
* <li></li>
* </ul>
*
* @return latency set for {@code this} entry
*/
int getLatency();
/**
* Sets the latency for {@code this} entry to the specified value
*
* @param latency to changed to
* @return {@code this}, for chaining
* @see #getLatency()
*/
TabListEntry setLatency(int latency);
/**
* Gets the game mode {@code this} entry has been set to.
* <p>The number corresponds to the game mode in the following way:</p>
* <ol start="0">
* <li>Survival</li>
* <li>Creative</li>
* <li>Adventure</li>
* <li>Spectator</li>
* </ol>
*
* @return the game mode
*/
int getGameMode();
/**
* Sets the game mode for {@code this} entry to the specified value
*
* @param gameMode to change to
* @return {@code this}, for chaining
* @see #getGameMode()
*/
TabListEntry setGameMode(int gameMode);
/**
* Returns a {@link Builder} to create a {@link TabListEntry}.
*
* @return {@link TabListEntry} builder
*/
static Builder builder() {
return new Builder();
}
/**
* Represents a builder which creates {@link TabListEntry}s.
*
* @see TabListEntry
*/
class Builder {
private @Nullable TabList tabList;
private @Nullable GameProfile profile;
private @Nullable Component displayName;
private int latency = 0;
private int gameMode = 0;
private Builder() {
}
/**
* Sets the parent {@link TabList} for this entry, the entry will only be able to be added to
* that specific {@link TabList}.
*
* @param tabList to set
* @return {@code this}, for chaining
*/
public Builder tabList(TabList tabList) {
this.tabList = tabList;
return this;
}
/**
* Sets the {@link GameProfile} of the {@link TabListEntry}.
*
* @param profile to set
* @return {@code this}, for chaining
* @see TabListEntry#getProfile()
*/
public Builder profile(GameProfile profile) {
this.profile = profile;
return this;
}
/**
* Sets the displayed name of the {@link TabListEntry}
*
* @param displayName to set
* @return {@code this}, for chaining
* @see TabListEntry#getDisplayName()
*/
public Builder displayName(@Nullable Component displayName) {
this.displayName = displayName;
return this;
}
/**
* Sets the latency of the {@link TabListEntry}
*
* @param latency to set
* @return {@code this}, for chaining
* @see TabListEntry#getLatency()
*/
public Builder latency(int latency) {
this.latency = latency;
return this;
}
/**
* Sets the game mode of the {@link TabListEntry}
*
* @param gameMode to set
* @return {@code this}, for chaining
* @see TabListEntry#getGameMode()
*/
public Builder gameMode(int gameMode) {
this.gameMode = gameMode;
return this;
}
/**
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
*
* @return the constructed {@link TabListEntry}
*/
public TabListEntry build() {
if (tabList == null) {
throw new IllegalStateException("The Tablist must be set when building a TabListEntry");
}
if (profile == null) {
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
}
return tabList.buildEntry(profile, displayName, latency, gameMode);
}
}
}

Datei anzeigen

@ -3,301 +3,323 @@ package com.velocitypowered.api.proxy.server;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.proxy.config.ProxyConfig;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* GS4 query response. This class is immutable.
*/
public final class QueryResponse {
private final String hostname;
private final String gameVersion;
private final String map;
private final int currentPlayers;
private final int maxPlayers;
private final String proxyHost;
private final int proxyPort;
private final Collection<String> players;
private final String proxyVersion;
private final Collection<PluginInformation> plugins;
private QueryResponse(String hostname, String gameVersion, String map, int currentPlayers, int maxPlayers, String proxyHost, int proxyPort, Collection<String> players, String proxyVersion, Collection<PluginInformation> plugins) {
this.hostname = hostname;
this.gameVersion = gameVersion;
this.map = map;
this.currentPlayers = currentPlayers;
this.maxPlayers = maxPlayers;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
this.players = players;
this.proxyVersion = proxyVersion;
this.plugins = plugins;
private final String hostname;
private final String gameVersion;
private final String map;
private final int currentPlayers;
private final int maxPlayers;
private final String proxyHost;
private final int proxyPort;
private final Collection<String> players;
private final String proxyVersion;
private final Collection<PluginInformation> plugins;
private QueryResponse(String hostname, String gameVersion, String map, int currentPlayers,
int maxPlayers, String proxyHost, int proxyPort, Collection<String> players,
String proxyVersion, Collection<PluginInformation> plugins) {
this.hostname = hostname;
this.gameVersion = gameVersion;
this.map = map;
this.currentPlayers = currentPlayers;
this.maxPlayers = maxPlayers;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
this.players = players;
this.proxyVersion = proxyVersion;
this.plugins = plugins;
}
/**
* Get hostname which will be used to reply to the query. By default it is {@link
* ProxyConfig#getMotdComponent()} in plain text without colour codes.
*
* @return hostname
*/
public String getHostname() {
return hostname;
}
/**
* Get game version which will be used to reply to the query. By default supported Minecraft
* versions range is sent.
*
* @return game version
*/
public String getGameVersion() {
return gameVersion;
}
/**
* Get map name which will be used to reply to the query. By default {@link
* ProxyConfig#getQueryMap()} is sent.
*
* @return map name
*/
public String getMap() {
return map;
}
/**
* Get current online player count which will be used to reply to the query.
*
* @return online player count
*/
public int getCurrentPlayers() {
return currentPlayers;
}
/**
* Get max player count which will be used to reply to the query.
*
* @return max player count
*/
public int getMaxPlayers() {
return maxPlayers;
}
/**
* Get proxy (public facing) hostname
*
* @return proxy hostname
*/
public String getProxyHost() {
return proxyHost;
}
/**
* Get proxy (public facing) port
*
* @return proxy port
*/
public int getProxyPort() {
return proxyPort;
}
/**
* Get collection of players which will be used to reply to the query.
*
* @return collection of players
*/
public Collection<String> getPlayers() {
return players;
}
/**
* Get server software (name and version) which will be used to reply to the query.
*
* @return server software
*/
public String getProxyVersion() {
return proxyVersion;
}
/**
* Get list of plugins which will be used to reply to the query.
*
* @return collection of plugins
*/
public Collection<PluginInformation> getPlugins() {
return plugins;
}
/**
* Creates a new {@link Builder} instance from data represented by this response
*
* @return {@link QueryResponse} builder
*/
public Builder toBuilder() {
return QueryResponse.builder()
.hostname(getHostname())
.gameVersion(getGameVersion())
.map(getMap())
.currentPlayers(getCurrentPlayers())
.maxPlayers(getMaxPlayers())
.proxyHost(getProxyHost())
.proxyPort(getProxyPort())
.players(getPlayers())
.proxyVersion(getProxyVersion())
.plugins(getPlugins());
}
/**
* Creates a new {@link Builder} instance
*
* @return {@link QueryResponse} builder
*/
public static Builder builder() {
return new Builder();
}
/**
* A builder for {@link QueryResponse} objects.
*/
public static final class Builder {
@MonotonicNonNull
private String hostname;
@MonotonicNonNull
private String gameVersion;
@MonotonicNonNull
private String map;
@MonotonicNonNull
private String proxyHost;
@MonotonicNonNull
private String proxyVersion;
private int currentPlayers;
private int maxPlayers;
private int proxyPort;
private List<String> players = new ArrayList<>();
private List<PluginInformation> plugins = new ArrayList<>();
private Builder() {
}
public Builder hostname(String hostname) {
this.hostname = Preconditions.checkNotNull(hostname, "hostname");
return this;
}
public Builder gameVersion(String gameVersion) {
this.gameVersion = Preconditions.checkNotNull(gameVersion, "gameVersion");
return this;
}
public Builder map(String map) {
this.map = Preconditions.checkNotNull(map, "map");
return this;
}
public Builder currentPlayers(int currentPlayers) {
Preconditions.checkArgument(currentPlayers >= 0, "currentPlayers cannot be negative");
this.currentPlayers = currentPlayers;
return this;
}
public Builder maxPlayers(int maxPlayers) {
Preconditions.checkArgument(maxPlayers >= 0, "maxPlayers cannot be negative");
this.maxPlayers = maxPlayers;
return this;
}
public Builder proxyHost(String proxyHost) {
this.proxyHost = Preconditions.checkNotNull(proxyHost, "proxyHost");
return this;
}
public Builder proxyPort(int proxyPort) {
Preconditions
.checkArgument(proxyPort >= 1 && proxyPort <= 65535, "proxyPort must be between 1-65535");
this.proxyPort = proxyPort;
return this;
}
public Builder players(Collection<String> players) {
this.players.addAll(Preconditions.checkNotNull(players, "players"));
return this;
}
public Builder players(String... players) {
this.players.addAll(Arrays.asList(Preconditions.checkNotNull(players, "players")));
return this;
}
public Builder clearPlayers() {
this.players.clear();
return this;
}
public Builder proxyVersion(String proxyVersion) {
this.proxyVersion = Preconditions.checkNotNull(proxyVersion, "proxyVersion");
return this;
}
public Builder plugins(Collection<PluginInformation> plugins) {
this.plugins.addAll(Preconditions.checkNotNull(plugins, "plugins"));
return this;
}
public Builder plugins(PluginInformation... plugins) {
this.plugins.addAll(Arrays.asList(Preconditions.checkNotNull(plugins, "plugins")));
return this;
}
public Builder clearPlugins() {
this.plugins.clear();
return this;
}
/**
* Get hostname which will be used to reply to the query. By default it is {@link ProxyConfig#getMotdComponent()} in plain text without colour codes.
* @return hostname
* Builds new {@link QueryResponse} with supplied data
*
* @return response
*/
public String getHostname() {
return hostname;
public QueryResponse build() {
return new QueryResponse(
Preconditions.checkNotNull(hostname, "hostname"),
Preconditions.checkNotNull(gameVersion, "gameVersion"),
Preconditions.checkNotNull(map, "map"),
currentPlayers,
maxPlayers,
Preconditions.checkNotNull(proxyHost, "proxyHost"),
proxyPort,
ImmutableList.copyOf(players),
Preconditions.checkNotNull(proxyVersion, "proxyVersion"),
ImmutableList.copyOf(plugins)
);
}
}
/**
* Plugin information
*/
public static class PluginInformation {
private String name;
private String version;
public PluginInformation(String name, String version) {
this.name = Preconditions.checkNotNull(name, "name");
this.version = Preconditions.checkNotNull(version, "version");
}
/**
* Get game version which will be used to reply to the query. By default supported Minecraft versions range is sent.
* @return game version
*/
public String getGameVersion() {
return gameVersion;
public String getName() {
return name;
}
/**
* Get map name which will be used to reply to the query. By default {@link ProxyConfig#getQueryMap()} is sent.
* @return map name
*/
public String getMap() {
return map;
public void setName(String name) {
this.name = name;
}
/**
* Get current online player count which will be used to reply to the query.
* @return online player count
*/
public int getCurrentPlayers() {
return currentPlayers;
public void setVersion(@Nullable String version) {
this.version = version;
}
/**
* Get max player count which will be used to reply to the query.
* @return max player count
*/
public int getMaxPlayers() {
return maxPlayers;
@Nullable
public String getVersion() {
return version;
}
/**
* Get proxy (public facing) hostname
* @return proxy hostname
*/
public String getProxyHost() {
return proxyHost;
}
/**
* Get proxy (public facing) port
* @return proxy port
*/
public int getProxyPort() {
return proxyPort;
}
/**
* Get collection of players which will be used to reply to the query.
* @return collection of players
*/
public Collection<String> getPlayers() {
return players;
}
/**
* Get server software (name and version) which will be used to reply to the query.
* @return server software
*/
public String getProxyVersion() {
return proxyVersion;
}
/**
* Get list of plugins which will be used to reply to the query.
* @return collection of plugins
*/
public Collection<PluginInformation> getPlugins() {
return plugins;
}
/**
* Creates a new {@link Builder} instance from data represented by this response
* @return {@link QueryResponse} builder
*/
public Builder toBuilder() {
return QueryResponse.builder()
.hostname(getHostname())
.gameVersion(getGameVersion())
.map(getMap())
.currentPlayers(getCurrentPlayers())
.maxPlayers(getMaxPlayers())
.proxyHost(getProxyHost())
.proxyPort(getProxyPort())
.players(getPlayers())
.proxyVersion(getProxyVersion())
.plugins(getPlugins());
}
/**
* Creates a new {@link Builder} instance
* @return {@link QueryResponse} builder
*/
public static Builder builder() {
return new Builder();
}
/**
* A builder for {@link QueryResponse} objects.
*/
public static final class Builder {
@MonotonicNonNull
private String hostname;
@MonotonicNonNull
private String gameVersion;
@MonotonicNonNull
private String map;
@MonotonicNonNull
private String proxyHost;
@MonotonicNonNull
private String proxyVersion;
private int currentPlayers;
private int maxPlayers;
private int proxyPort;
private List<String> players = new ArrayList<>();
private List<PluginInformation> plugins = new ArrayList<>();
private Builder() {}
public Builder hostname(String hostname) {
this.hostname = Preconditions.checkNotNull(hostname, "hostname");
return this;
}
public Builder gameVersion(String gameVersion) {
this.gameVersion = Preconditions.checkNotNull(gameVersion, "gameVersion");
return this;
}
public Builder map(String map) {
this.map = Preconditions.checkNotNull(map, "map");
return this;
}
public Builder currentPlayers(int currentPlayers) {
Preconditions.checkArgument(currentPlayers >= 0, "currentPlayers cannot be negative");
this.currentPlayers = currentPlayers;
return this;
}
public Builder maxPlayers(int maxPlayers) {
Preconditions.checkArgument(maxPlayers >= 0, "maxPlayers cannot be negative");
this.maxPlayers = maxPlayers;
return this;
}
public Builder proxyHost(String proxyHost) {
this.proxyHost = Preconditions.checkNotNull(proxyHost, "proxyHost");
return this;
}
public Builder proxyPort(int proxyPort) {
Preconditions.checkArgument(proxyPort >= 1 && proxyPort <= 65535, "proxyPort must be between 1-65535");
this.proxyPort = proxyPort;
return this;
}
public Builder players(Collection<String> players) {
this.players.addAll(Preconditions.checkNotNull(players, "players"));
return this;
}
public Builder players(String... players) {
this.players.addAll(Arrays.asList(Preconditions.checkNotNull(players, "players")));
return this;
}
public Builder clearPlayers() {
this.players.clear();
return this;
}
public Builder proxyVersion(String proxyVersion) {
this.proxyVersion = Preconditions.checkNotNull(proxyVersion, "proxyVersion");
return this;
}
public Builder plugins(Collection<PluginInformation> plugins) {
this.plugins.addAll(Preconditions.checkNotNull(plugins, "plugins"));
return this;
}
public Builder plugins(PluginInformation... plugins) {
this.plugins.addAll(Arrays.asList(Preconditions.checkNotNull(plugins, "plugins")));
return this;
}
public Builder clearPlugins() {
this.plugins.clear();
return this;
}
/**
* Builds new {@link QueryResponse} with supplied data
* @return response
*/
public QueryResponse build() {
return new QueryResponse(
Preconditions.checkNotNull(hostname, "hostname"),
Preconditions.checkNotNull(gameVersion, "gameVersion"),
Preconditions.checkNotNull(map, "map"),
currentPlayers,
maxPlayers,
Preconditions.checkNotNull(proxyHost, "proxyHost"),
proxyPort,
ImmutableList.copyOf(players),
Preconditions.checkNotNull(proxyVersion, "proxyVersion"),
ImmutableList.copyOf(plugins)
);
}
}
/**
* Plugin information
*/
public static class PluginInformation {
private String name;
private String version;
public PluginInformation(String name, String version) {
this.name = Preconditions.checkNotNull(name, "name");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setVersion(@Nullable String version) {
this.version = version;
}
@Nullable
public String getVersion() {
return version;
}
public static PluginInformation of(String name, @Nullable String version) {
return new PluginInformation(name, version);
}
public static PluginInformation of(String name, @Nullable String version) {
return new PluginInformation(name, version);
}
}
}

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.api.proxy.server;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
@ -10,21 +9,25 @@ import java.util.concurrent.CompletableFuture;
* Represents a server that has been registered with the proxy.
*/
public interface RegisteredServer extends ChannelMessageSink {
/**
* Returns the {@link ServerInfo} for this server.
* @return the server info
*/
ServerInfo getServerInfo();
/**
* Returns a list of all the players currently connected to this server on this proxy.
* @return the players on this proxy
*/
Collection<Player> getPlayersConnected();
/**
* Returns the {@link ServerInfo} for this server.
*
* @return the server info
*/
ServerInfo getServerInfo();
/**
* Attempts to ping the remote server and return the server list ping result.
* @return the server ping result from the server
*/
CompletableFuture<ServerPing> ping();
/**
* Returns a list of all the players currently connected to this server on this proxy.
*
* @return the players on this proxy
*/
Collection<Player> getPlayersConnected();
/**
* Attempts to ping the remote server and return the server list ping result.
*
* @return the server ping result from the server
*/
CompletableFuture<ServerPing> ping();
}

Datei anzeigen

@ -1,55 +1,61 @@
package com.velocitypowered.api.proxy.server;
import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.net.InetSocketAddress;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* ServerInfo represents a server that a player can connect to. This object is immutable and safe for concurrent access.
* ServerInfo represents a server that a player can connect to. This object is immutable and safe
* for concurrent access.
*/
public final class ServerInfo {
private final String name;
private final InetSocketAddress address;
/**
* Creates a new ServerInfo object.
* @param name the name for the server
* @param address the address of the server to connect to
*/
public ServerInfo(String name, InetSocketAddress address) {
this.name = Preconditions.checkNotNull(name, "name");
this.address = Preconditions.checkNotNull(address, "address");
}
private final String name;
private final InetSocketAddress address;
public final String getName() {
return name;
}
/**
* Creates a new ServerInfo object.
*
* @param name the name for the server
* @param address the address of the server to connect to
*/
public ServerInfo(String name, InetSocketAddress address) {
this.name = Preconditions.checkNotNull(name, "name");
this.address = Preconditions.checkNotNull(address, "address");
}
public final InetSocketAddress getAddress() {
return address;
}
public final String getName() {
return name;
}
@Override
public String toString() {
return "ServerInfo{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
public final InetSocketAddress getAddress() {
return address;
}
@Override
public final boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServerInfo that = (ServerInfo) o;
return Objects.equals(name, that.name) &&
Objects.equals(address, that.address);
}
@Override
public String toString() {
return "ServerInfo{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
@Override
public final int hashCode() {
return Objects.hash(name, address);
@Override
public final boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ServerInfo that = (ServerInfo) o;
return Objects.equals(name, that.name) &&
Objects.equals(address, that.address);
}
@Override
public final int hashCode() {
return Objects.hash(name, address);
}
}

Datei anzeigen

@ -4,308 +4,319 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.velocitypowered.api.util.Favicon;
import com.velocitypowered.api.util.ModInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.*;
/**
* Represents a 1.7 and above server list ping response. This class is immutable.
*/
public final class ServerPing {
private final Version version;
private final @Nullable Players players;
private final Component description;
private final @Nullable Favicon favicon;
private final @Nullable ModInfo modinfo;
public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon) {
this(version, players, description, favicon, ModInfo.DEFAULT);
private final Version version;
private final @Nullable Players players;
private final Component description;
private final @Nullable Favicon favicon;
private final @Nullable ModInfo modinfo;
public ServerPing(Version version, @Nullable Players players, Component description,
@Nullable Favicon favicon) {
this(version, players, description, favicon, ModInfo.DEFAULT);
}
public ServerPing(Version version, @Nullable Players players, Component description,
@Nullable Favicon favicon, @Nullable ModInfo modinfo) {
this.version = Preconditions.checkNotNull(version, "version");
this.players = players;
this.description = Preconditions.checkNotNull(description, "description");
this.favicon = favicon;
this.modinfo = modinfo;
}
public Version getVersion() {
return version;
}
public Optional<Players> getPlayers() {
return Optional.ofNullable(players);
}
public Component getDescription() {
return description;
}
public Optional<Favicon> getFavicon() {
return Optional.ofNullable(favicon);
}
public Optional<ModInfo> getModinfo() {
return Optional.ofNullable(modinfo);
}
@Override
public String toString() {
return "ServerPing{" +
"version=" + version +
", players=" + players +
", description=" + description +
", favicon='" + favicon + '\'' +
'}';
}
public Builder asBuilder() {
Builder builder = new Builder();
builder.version = version;
if (players != null) {
builder.onlinePlayers = players.online;
builder.maximumPlayers = players.max;
builder.samplePlayers.addAll(players.sample);
} else {
builder.nullOutPlayers = true;
}
builder.description = description;
builder.favicon = favicon;
builder.nullOutModinfo = modinfo == null;
if (modinfo != null) {
builder.modType = modinfo.getType();
builder.mods.addAll(modinfo.getMods());
}
return builder;
}
public static Builder builder() {
return new Builder();
}
/**
* A builder for {@link ServerPing} objects.
*/
public static final class Builder {
private Version version = new Version(0, "Unknown");
private int onlinePlayers;
private int maximumPlayers;
private final List<SamplePlayer> samplePlayers = new ArrayList<>();
private String modType = "FML";
private final List<ModInfo.Mod> mods = new ArrayList<>();
private @Nullable Component description;
private @Nullable Favicon favicon;
private boolean nullOutPlayers;
private boolean nullOutModinfo;
private Builder() {
}
public ServerPing(Version version, @Nullable Players players, Component description, @Nullable Favicon favicon, @Nullable ModInfo modinfo) {
this.version = Preconditions.checkNotNull(version, "version");
this.players = players;
this.description = Preconditions.checkNotNull(description, "description");
this.favicon = favicon;
this.modinfo = modinfo;
public Builder version(Version version) {
this.version = Preconditions.checkNotNull(version, "version");
return this;
}
public Builder onlinePlayers(int onlinePlayers) {
this.onlinePlayers = onlinePlayers;
return this;
}
public Builder maximumPlayers(int maximumPlayers) {
this.maximumPlayers = maximumPlayers;
return this;
}
public Builder samplePlayers(SamplePlayer... players) {
this.samplePlayers.addAll(Arrays.asList(players));
return this;
}
public Builder modType(String modType) {
this.modType = Preconditions.checkNotNull(modType, "modType");
return this;
}
public Builder mods(ModInfo.Mod... mods) {
this.mods.addAll(Arrays.asList(mods));
return this;
}
public Builder clearMods() {
this.mods.clear();
return this;
}
public Builder clearSamplePlayers() {
this.samplePlayers.clear();
return this;
}
public Builder notModCompatible() {
this.nullOutModinfo = true;
return this;
}
public Builder nullPlayers() {
this.nullOutPlayers = true;
return this;
}
public Builder description(Component description) {
this.description = Preconditions.checkNotNull(description, "description");
return this;
}
public Builder favicon(Favicon favicon) {
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
return this;
}
public ServerPing build() {
if (this.version == null) {
throw new IllegalStateException("version not specified");
}
if (this.description == null) {
throw new IllegalStateException("no server description supplied");
}
return new ServerPing(version,
nullOutPlayers ? null : new Players(onlinePlayers, maximumPlayers, samplePlayers),
description, favicon, nullOutModinfo ? null : new ModInfo(modType, mods));
}
public Version getVersion() {
return version;
return version;
}
public Optional<Players> getPlayers() {
return Optional.ofNullable(players);
public int getOnlinePlayers() {
return onlinePlayers;
}
public Component getDescription() {
return description;
public int getMaximumPlayers() {
return maximumPlayers;
}
public List<SamplePlayer> getSamplePlayers() {
return samplePlayers;
}
public Optional<Component> getDescription() {
return Optional.ofNullable(description);
}
public Optional<Favicon> getFavicon() {
return Optional.ofNullable(favicon);
return Optional.ofNullable(favicon);
}
public Optional<ModInfo> getModinfo() {
return Optional.ofNullable(modinfo);
public String getModType() {
return modType;
}
public List<ModInfo.Mod> getMods() {
return mods;
}
@Override
public String toString() {
return "ServerPing{" +
"version=" + version +
", players=" + players +
", description=" + description +
", favicon='" + favicon + '\'' +
'}';
return "Builder{" +
"version=" + version +
", onlinePlayers=" + onlinePlayers +
", maximumPlayers=" + maximumPlayers +
", samplePlayers=" + samplePlayers +
", modType=" + modType +
", mods=" + mods +
", description=" + description +
", favicon=" + favicon +
", nullOutPlayers=" + nullOutPlayers +
", nullOutModinfo=" + nullOutModinfo +
'}';
}
}
public static final class Version {
private final int protocol;
private final String name;
public Version(int protocol, String name) {
this.protocol = protocol;
this.name = name;
}
public Builder asBuilder() {
Builder builder = new Builder();
builder.version = version;
if (players != null) {
builder.onlinePlayers = players.online;
builder.maximumPlayers = players.max;
builder.samplePlayers.addAll(players.sample);
} else {
builder.nullOutPlayers = true;
}
builder.description = description;
builder.favicon = favicon;
builder.nullOutModinfo = modinfo == null;
if (modinfo != null) {
builder.modType = modinfo.getType();
builder.mods.addAll(modinfo.getMods());
}
return builder;
public int getProtocol() {
return protocol;
}
public static Builder builder() {
return new Builder();
public String getName() {
return name;
}
/**
* A builder for {@link ServerPing} objects.
*/
public static final class Builder {
private Version version = new Version(0, "Unknown");
private int onlinePlayers;
private int maximumPlayers;
private final List<SamplePlayer> samplePlayers = new ArrayList<>();
private String modType = "FML";
private final List<ModInfo.Mod> mods = new ArrayList<>();
private @Nullable Component description;
private @Nullable Favicon favicon;
private boolean nullOutPlayers;
private boolean nullOutModinfo;
@Override
public String toString() {
return "Version{" +
"protocol=" + protocol +
", name='" + name + '\'' +
'}';
}
}
private Builder() {
public static final class Players {
}
private final int online;
private final int max;
private final List<SamplePlayer> sample;
public Builder version(Version version) {
this.version = Preconditions.checkNotNull(version, "version");
return this;
}
public Builder onlinePlayers(int onlinePlayers) {
this.onlinePlayers = onlinePlayers;
return this;
}
public Builder maximumPlayers(int maximumPlayers) {
this.maximumPlayers = maximumPlayers;
return this;
}
public Builder samplePlayers(SamplePlayer... players) {
this.samplePlayers.addAll(Arrays.asList(players));
return this;
}
public Builder modType(String modType) {
this.modType = Preconditions.checkNotNull(modType, "modType");
return this;
}
public Builder mods(ModInfo.Mod... mods) {
this.mods.addAll(Arrays.asList(mods));
return this;
}
public Builder clearMods() {
this.mods.clear();
return this;
}
public Builder clearSamplePlayers() {
this.samplePlayers.clear();
return this;
}
public Builder notModCompatible() {
this.nullOutModinfo = true;
return this;
}
public Builder nullPlayers() {
this.nullOutPlayers = true;
return this;
}
public Builder description(Component description) {
this.description = Preconditions.checkNotNull(description, "description");
return this;
}
public Builder favicon(Favicon favicon) {
this.favicon = Preconditions.checkNotNull(favicon, "favicon");
return this;
}
public ServerPing build() {
if (this.version == null) {
throw new IllegalStateException("version not specified");
}
if (this.description == null) {
throw new IllegalStateException("no server description supplied");
}
return new ServerPing(version, nullOutPlayers ? null : new Players(onlinePlayers, maximumPlayers, samplePlayers),
description, favicon, nullOutModinfo ? null : new ModInfo(modType, mods));
}
public Version getVersion() {
return version;
}
public int getOnlinePlayers() {
return onlinePlayers;
}
public int getMaximumPlayers() {
return maximumPlayers;
}
public List<SamplePlayer> getSamplePlayers() {
return samplePlayers;
}
public Optional<Component> getDescription() {
return Optional.ofNullable(description);
}
public Optional<Favicon> getFavicon() {
return Optional.ofNullable(favicon);
}
public String getModType() {
return modType;
}
public List<ModInfo.Mod> getMods() {
return mods;
}
@Override
public String toString() {
return "Builder{" +
"version=" + version +
", onlinePlayers=" + onlinePlayers +
", maximumPlayers=" + maximumPlayers +
", samplePlayers=" + samplePlayers +
", modType=" + modType +
", mods=" + mods +
", description=" + description +
", favicon=" + favicon +
", nullOutPlayers=" + nullOutPlayers +
", nullOutModinfo=" + nullOutModinfo +
'}';
}
public Players(int online, int max, List<SamplePlayer> sample) {
this.online = online;
this.max = max;
this.sample = ImmutableList.copyOf(sample);
}
public static final class Version {
private final int protocol;
private final String name;
public Version(int protocol, String name) {
this.protocol = protocol;
this.name = name;
}
public int getProtocol() {
return protocol;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Version{" +
"protocol=" + protocol +
", name='" + name + '\'' +
'}';
}
public int getOnline() {
return online;
}
public static final class Players {
private final int online;
private final int max;
private final List<SamplePlayer> sample;
public Players(int online, int max, List<SamplePlayer> sample) {
this.online = online;
this.max = max;
this.sample = ImmutableList.copyOf(sample);
}
public int getOnline() {
return online;
}
public int getMax() {
return max;
}
public List<SamplePlayer> getSample() {
return sample;
}
@Override
public String toString() {
return "Players{" +
"online=" + online +
", max=" + max +
", sample=" + sample +
'}';
}
public int getMax() {
return max;
}
public static final class SamplePlayer {
private final String name;
private final UUID id;
public SamplePlayer(String name, UUID id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public UUID getId() {
return id;
}
@Override
public String toString() {
return "SamplePlayer{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
public List<SamplePlayer> getSample() {
return sample;
}
@Override
public String toString() {
return "Players{" +
"online=" + online +
", max=" + max +
", sample=" + sample +
'}';
}
}
public static final class SamplePlayer {
private final String name;
private final UUID id;
public SamplePlayer(String name, UUID id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public UUID getId() {
return id;
}
@Override
public String toString() {
return "SamplePlayer{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
}

Datei anzeigen

@ -4,21 +4,24 @@ package com.velocitypowered.api.scheduler;
* Represents a task that is scheduled to run on the proxy.
*/
public interface ScheduledTask {
/**
* Returns the plugin that scheduled this task.
* @return the plugin that scheduled this task
*/
Object plugin();
/**
* Returns the current status of this task.
* @return the current status of this task
*/
TaskStatus status();
/**
* Returns the plugin that scheduled this task.
*
* @return the plugin that scheduled this task
*/
Object plugin();
/**
* Cancels this task. If the task is already running, the thread in which it is running will be interrupted.
* If the task is not currently running, Velocity will terminate it safely.
*/
void cancel();
/**
* Returns the current status of this task.
*
* @return the current status of this task
*/
TaskStatus status();
/**
* Cancels this task. If the task is already running, the thread in which it is running will be
* interrupted. If the task is not currently running, Velocity will terminate it safely.
*/
void cancel();
}

Datei anzeigen

@ -1,57 +1,65 @@
package com.velocitypowered.api.scheduler;
import org.checkerframework.common.value.qual.IntRange;
import java.util.concurrent.TimeUnit;
import org.checkerframework.common.value.qual.IntRange;
/**
* Represents a scheduler to execute tasks on the proxy.
*/
public interface Scheduler {
/**
* Initializes a new {@link TaskBuilder} for creating a task on the proxy.
* @param plugin the plugin to request the task for
* @param runnable the task to run when scheduled
* @return the task builder
*/
TaskBuilder buildTask(Object plugin, Runnable runnable);
/**
* Initializes a new {@link TaskBuilder} for creating a task on the proxy.
*
* @param plugin the plugin to request the task for
* @param runnable the task to run when scheduled
* @return the task builder
*/
TaskBuilder buildTask(Object plugin, Runnable runnable);
/**
* Represents a fluent interface to schedule tasks on the proxy.
*/
interface TaskBuilder {
/**
* Represents a fluent interface to schedule tasks on the proxy.
* Specifies that the task should delay its execution by the specified amount of time.
*
* @param time the time to delay by
* @param unit the unit of time for {@code time}
* @return this builder, for chaining
*/
interface TaskBuilder {
/**
* Specifies that the task should delay its execution by the specified amount of time.
* @param time the time to delay by
* @param unit the unit of time for {@code time}
* @return this builder, for chaining
*/
TaskBuilder delay(@IntRange(from = 0) long time, TimeUnit unit);
TaskBuilder delay(@IntRange(from = 0) long time, TimeUnit unit);
/**
* Specifies that the task should continue running after waiting for the specified amount, until it is cancelled.
* @param time the time to delay by
* @param unit the unit of time for {@code time}
* @return this builder, for chaining
*/
TaskBuilder repeat(@IntRange(from = 0) long time, TimeUnit unit);
/**
* Specifies that the task should continue running after waiting for the specified amount, until
* it is cancelled.
*
* @param time the time to delay by
* @param unit the unit of time for {@code time}
* @return this builder, for chaining
*/
TaskBuilder repeat(@IntRange(from = 0) long time, TimeUnit unit);
/**
* Clears the delay on this task.
* @return this builder, for chaining
*/
TaskBuilder clearDelay();
/**
* Clears the delay on this task.
*
* @return this builder, for chaining
*/
TaskBuilder clearDelay();
/**
* Clears the repeat interval on this task.
* @return this builder, for chaining
*/
TaskBuilder clearRepeat();
/**
* Clears the repeat interval on this task.
*
* @return this builder, for chaining
*/
TaskBuilder clearRepeat();
/**
* Schedules this task for execution.
* @return the scheduled task
*/
ScheduledTask schedule();
}
/**
* Schedules this task for execution.
*
* @return the scheduled task
*/
ScheduledTask schedule();
}
}

Datei anzeigen

@ -1,16 +1,16 @@
package com.velocitypowered.api.scheduler;
public enum TaskStatus {
/**
* The task is scheduled and is currently running.
*/
SCHEDULED,
/**
* The task was cancelled with {@link ScheduledTask#cancel()}.
*/
CANCELLED,
/**
* The task has run to completion. This is applicable only for tasks without a repeat.
*/
FINISHED
/**
* The task is scheduled and is currently running.
*/
SCHEDULED,
/**
* The task was cancelled with {@link ScheduledTask#cancel()}.
*/
CANCELLED,
/**
* The task has run to completion. This is applicable only for tasks without a repeat.
*/
FINISHED
}

Datei anzeigen

@ -1,9 +1,6 @@
package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -12,78 +9,92 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.Objects;
import javax.imageio.ImageIO;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents a Minecraft server favicon. A Minecraft server favicon is a 64x64 image that can be displayed to a remote
* client that sends a Server List Ping packet, and is automatically displayed in the Minecraft client.
* Represents a Minecraft server favicon. A Minecraft server favicon is a 64x64 image that can be
* displayed to a remote client that sends a Server List Ping packet, and is automatically displayed
* in the Minecraft client.
*/
public final class Favicon {
private final String base64Url;
/**
* Directly create a favicon using its Base64 URL directly. You are generally better served by the create() series
* of functions.
* @param base64Url the url for use with this favicon
*/
public Favicon(String base64Url) {
this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url");
}
private final String base64Url;
/**
* Returns the Base64-encoded URI for this image.
* @return a URL representing this favicon
*/
public String getBase64Url() {
return base64Url;
}
/**
* Directly create a favicon using its Base64 URL directly. You are generally better served by the
* create() series of functions.
*
* @param base64Url the url for use with this favicon
*/
public Favicon(String base64Url) {
this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url");
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Favicon favicon = (Favicon) o;
return Objects.equals(base64Url, favicon.base64Url);
}
/**
* Returns the Base64-encoded URI for this image.
*
* @return a URL representing this favicon
*/
public String getBase64Url() {
return base64Url;
}
@Override
public int hashCode() {
return Objects.hash(base64Url);
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Favicon favicon = (Favicon) o;
return Objects.equals(base64Url, favicon.base64Url);
}
@Override
public String toString() {
return "Favicon{" +
"base64Url='" + base64Url + '\'' +
'}';
}
@Override
public int hashCode() {
return Objects.hash(base64Url);
}
/**
* Creates a new {@code Favicon} from the specified {@code image}.
* @param image the image to use for the favicon
* @return the created {@link Favicon} instance
*/
public static Favicon create(BufferedImage image) {
Preconditions.checkNotNull(image, "image");
Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, "Image does not have" +
" 64x64 dimensions (found %sx%s)", image.getWidth(), image.getHeight());
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(image, "PNG", os);
} catch (IOException e) {
throw new AssertionError(e);
}
return new Favicon("data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray()));
}
@Override
public String toString() {
return "Favicon{" +
"base64Url='" + base64Url + '\'' +
'}';
}
/**
* Creates a new {@code Favicon} by reading the image from the specified {@code path}.
* @param path the path to the image to create a favicon for
* @return the created {@link Favicon} instance
* @throws IOException if the file could not be read from the path
*/
public static Favicon create(Path path) throws IOException {
try (InputStream stream = Files.newInputStream(path)) {
return create(ImageIO.read(stream));
}
/**
* Creates a new {@code Favicon} from the specified {@code image}.
*
* @param image the image to use for the favicon
* @return the created {@link Favicon} instance
*/
public static Favicon create(BufferedImage image) {
Preconditions.checkNotNull(image, "image");
Preconditions
.checkArgument(image.getWidth() == 64 && image.getHeight() == 64, "Image does not have" +
" 64x64 dimensions (found %sx%s)", image.getWidth(), image.getHeight());
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ImageIO.write(image, "PNG", os);
} catch (IOException e) {
throw new AssertionError(e);
}
return new Favicon(
"data:image/png;base64," + Base64.getEncoder().encodeToString(os.toByteArray()));
}
/**
* Creates a new {@code Favicon} by reading the image from the specified {@code path}.
*
* @param path the path to the image to create a favicon for
* @return the created {@link Favicon} instance
* @throws IOException if the file could not be read from the path
*/
public static Favicon create(Path path) throws IOException {
try (InputStream stream = Files.newInputStream(path)) {
return create(ImageIO.read(stream));
}
}
}

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.UUID;
@ -10,82 +9,85 @@ import java.util.UUID;
* Represents a Mojang game profile. This class is immutable.
*/
public final class GameProfile {
private final String id;
private final String id;
private final String name;
private final List<Property> properties;
public GameProfile(String id, String name, List<Property> properties) {
this.id = Preconditions.checkNotNull(id, "id");
this.name = Preconditions.checkNotNull(name, "name");
this.properties = ImmutableList.copyOf(properties);
}
public String getId() {
return id;
}
public UUID idAsUuid() {
return UuidUtils.fromUndashed(id);
}
public String getName() {
return name;
}
public List<Property> getProperties() {
return properties;
}
/**
* Creates a game profile suitable for use in offline-mode.
*
* @param username the username to use
* @return the new offline-mode game profile
*/
public static GameProfile forOfflinePlayer(String username) {
Preconditions.checkNotNull(username, "username");
String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username));
return new GameProfile(id, username, ImmutableList.of());
}
@Override
public String toString() {
return "GameProfile{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", properties=" + properties +
'}';
}
public static final class Property {
private final String name;
private final List<Property> properties;
private final String value;
private final String signature;
public GameProfile(String id, String name, List<Property> properties) {
this.id = Preconditions.checkNotNull(id, "id");
this.name = Preconditions.checkNotNull(name, "name");
this.properties = ImmutableList.copyOf(properties);
}
public String getId() {
return id;
}
public UUID idAsUuid() {
return UuidUtils.fromUndashed(id);
public Property(String name, String value, String signature) {
this.name = Preconditions.checkNotNull(name, "name");
this.value = Preconditions.checkNotNull(value, "value");
this.signature = Preconditions.checkNotNull(signature, "signature");
}
public String getName() {
return name;
return name;
}
public List<Property> getProperties() {
return properties;
public String getValue() {
return value;
}
/**
* Creates a game profile suitable for use in offline-mode.
* @param username the username to use
* @return the new offline-mode game profile
*/
public static GameProfile forOfflinePlayer(String username) {
Preconditions.checkNotNull(username, "username");
String id = UuidUtils.toUndashed(UuidUtils.generateOfflinePlayerUuid(username));
return new GameProfile(id, username, ImmutableList.of());
public String getSignature() {
return signature;
}
@Override
public String toString() {
return "GameProfile{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", properties=" + properties +
'}';
}
public static final class Property {
private final String name;
private final String value;
private final String signature;
public Property(String name, String value, String signature) {
this.name = Preconditions.checkNotNull(name, "name");
this.value = Preconditions.checkNotNull(value, "value");
this.signature = Preconditions.checkNotNull(signature, "signature");
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public String getSignature() {
return signature;
}
@Override
public String toString() {
return "Property{" +
"name='" + name + '\'' +
", value='" + value + '\'' +
", signature='" + signature + '\'' +
'}';
}
return "Property{" +
"name='" + name + '\'' +
", value='" + value + '\'' +
", signature='" + signature + '\'' +
'}';
}
}
}

Datei anzeigen

@ -4,17 +4,18 @@ package com.velocitypowered.api.util;
* Represents where a chat message is going to be sent.
*/
public enum MessagePosition {
/**
* The chat message will appear in the client's HUD. These messages can be filtered out by the client.
*/
CHAT,
/**
* The chat message will appear in the client's HUD and can't be dismissed.
*/
SYSTEM,
/**
* The chat message will appear above the player's main HUD. This text format doesn't support many component features,
* such as hover events.
*/
ACTION_BAR
/**
* The chat message will appear in the client's HUD. These messages can be filtered out by the
* client.
*/
CHAT,
/**
* The chat message will appear in the client's HUD and can't be dismissed.
*/
SYSTEM,
/**
* The chat message will appear above the player's main HUD. This text format doesn't support many
* component features, such as hover events.
*/
ACTION_BAR
}

Datei anzeigen

@ -2,59 +2,60 @@ package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
public final class ModInfo {
public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of());
private final String type;
private final List<Mod> modList;
public ModInfo(String type, List<Mod> modList) {
this.type = Preconditions.checkNotNull(type, "type");
this.modList = ImmutableList.copyOf(modList);
public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of());
private final String type;
private final List<Mod> modList;
public ModInfo(String type, List<Mod> modList) {
this.type = Preconditions.checkNotNull(type, "type");
this.modList = ImmutableList.copyOf(modList);
}
public String getType() {
return type;
}
public List<Mod> getMods() {
return modList;
}
@Override
public String toString() {
return "ModInfo{" +
"type='" + type + '\'' +
", modList=" + modList +
'}';
}
public static final class Mod {
private final String id;
private final String version;
public Mod(String id, String version) {
this.id = Preconditions.checkNotNull(id, "id");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getType() {
return type;
public String getId() {
return id;
}
public List<Mod> getMods() {
return modList;
public String getVersion() {
return version;
}
@Override
public String toString() {
return "ModInfo{" +
"type='" + type + '\'' +
", modList=" + modList +
'}';
}
public static final class Mod {
private final String id;
private final String version;
public Mod(String id, String version) {
this.id = Preconditions.checkNotNull(id, "id");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getId() {
return id;
}
public String getVersion() {
return version;
}
@Override
public String toString() {
return "Mod{" +
"id='" + id + '\'' +
", version='" + version + '\'' +
'}';
}
return "Mod{" +
"id='" + id + '\'' +
", version='" + version + '\'' +
'}';
}
}
}

Datei anzeigen

@ -1,57 +1,61 @@
package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Provides a version object for the proxy.
*/
public final class ProxyVersion {
private final String name;
private final String vendor;
private final String version;
public ProxyVersion(String name, String vendor, String version) {
this.name = Preconditions.checkNotNull(name, "name");
this.vendor = Preconditions.checkNotNull(vendor, "vendor");
this.version = Preconditions.checkNotNull(version, "version");
}
private final String name;
private final String vendor;
private final String version;
public String getName() {
return name;
}
public ProxyVersion(String name, String vendor, String version) {
this.name = Preconditions.checkNotNull(name, "name");
this.vendor = Preconditions.checkNotNull(vendor, "vendor");
this.version = Preconditions.checkNotNull(version, "version");
}
public String getVendor() {
return vendor;
}
public String getName() {
return name;
}
public String getVersion() {
return version;
}
public String getVendor() {
return vendor;
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ProxyVersion that = (ProxyVersion) o;
return Objects.equals(name, that.name) &&
Objects.equals(vendor, that.vendor) &&
Objects.equals(version, that.version);
}
public String getVersion() {
return version;
}
@Override
public int hashCode() {
return Objects.hash(name, vendor, version);
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ProxyVersion that = (ProxyVersion) o;
return Objects.equals(name, that.name) &&
Objects.equals(vendor, that.vendor) &&
Objects.equals(version, that.version);
}
@Override
public String toString() {
return "ProxyVersion{" +
"name='" + name + '\'' +
", vendor='" + vendor + '\'' +
", version='" + version + '\'' +
'}';
}
@Override
public int hashCode() {
return Objects.hash(name, vendor, version);
}
@Override
public String toString() {
return "ProxyVersion{" +
"name='" + name + '\'' +
", vendor='" + vendor + '\'' +
", version='" + version + '\'' +
'}';
}
}

Datei anzeigen

@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.UUID;
@ -11,41 +10,45 @@ import java.util.UUID;
* Provides a small, useful selection of utilities for working with Minecraft UUIDs.
*/
public final class UuidUtils {
private UuidUtils() {
throw new AssertionError();
}
/**
* Converts from an undashed Mojang-style UUID into a Java {@link UUID} object.
* @param string the string to convert
* @return the UUID object
*/
public static UUID fromUndashed(final String string) {
Objects.requireNonNull(string, "string");
Preconditions.checkArgument(string.length() == 32, "Length is incorrect");
return new UUID(
Long.parseUnsignedLong(string.substring(0, 16), 16),
Long.parseUnsignedLong(string.substring(16), 16)
);
}
private UuidUtils() {
throw new AssertionError();
}
/**
* Converts from a Java {@link UUID} object into an undashed Mojang-style UUID.
* @param uuid the UUID to convert
* @return the undashed UUID
*/
public static String toUndashed(final UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid");
return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') +
Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0');
}
/**
* Converts from an undashed Mojang-style UUID into a Java {@link UUID} object.
*
* @param string the string to convert
* @return the UUID object
*/
public static UUID fromUndashed(final String string) {
Objects.requireNonNull(string, "string");
Preconditions.checkArgument(string.length() == 32, "Length is incorrect");
return new UUID(
Long.parseUnsignedLong(string.substring(0, 16), 16),
Long.parseUnsignedLong(string.substring(16), 16)
);
}
/**
* Generates a UUID for use for offline mode.
* @param username the username to use
* @return the offline mode UUID
*/
public static UUID generateOfflinePlayerUuid(String username) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
}
/**
* Converts from a Java {@link UUID} object into an undashed Mojang-style UUID.
*
* @param uuid the UUID to convert
* @return the undashed UUID
*/
public static String toUndashed(final UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid");
return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') +
Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0');
}
/**
* Generates a UUID for use for offline mode.
*
* @param username the username to use
* @return the offline mode UUID
*/
public static UUID generateOfflinePlayerUuid(String username) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
}
}

Datei anzeigen

@ -1,236 +1,251 @@
package com.velocitypowered.api.util.title;
import com.google.common.base.Preconditions;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Objects;
import java.util.Optional;
import net.kyori.text.Component;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents a "full" title, including all components. This class is immutable.
*/
public final class TextTitle implements Title {
private final @Nullable Component title;
private final @Nullable Component subtitle;
private final int stay;
private final int fadeIn;
private final int fadeOut;
private final boolean resetBeforeSend;
private TextTitle(Builder builder) {
this.title = builder.title;
this.subtitle = builder.subtitle;
this.stay = builder.stay;
this.fadeIn = builder.fadeIn;
this.fadeOut = builder.fadeOut;
this.resetBeforeSend = builder.resetBeforeSend;
private final @Nullable Component title;
private final @Nullable Component subtitle;
private final int stay;
private final int fadeIn;
private final int fadeOut;
private final boolean resetBeforeSend;
private TextTitle(Builder builder) {
this.title = builder.title;
this.subtitle = builder.subtitle;
this.stay = builder.stay;
this.fadeIn = builder.fadeIn;
this.fadeOut = builder.fadeOut;
this.resetBeforeSend = builder.resetBeforeSend;
}
/**
* Returns the main title this title has, if any.
*
* @return the main title of this title
*/
public Optional<Component> getTitle() {
return Optional.ofNullable(title);
}
/**
* Returns the subtitle this title has, if any.
*
* @return the subtitle
*/
public Optional<Component> getSubtitle() {
return Optional.ofNullable(subtitle);
}
/**
* Returns the number of ticks this title will stay up.
*
* @return how long the title will stay, in ticks
*/
public int getStay() {
return stay;
}
/**
* Returns the number of ticks over which this title will fade in.
*
* @return how long the title will fade in, in ticks
*/
public int getFadeIn() {
return fadeIn;
}
/**
* Returns the number of ticks over which this title will fade out.
*
* @return how long the title will fade out, in ticks
*/
public int getFadeOut() {
return fadeOut;
}
/**
* Returns whether or not a reset packet will be sent before this title is sent. By default,
* unless explicitly disabled, this is enabled by default.
*
* @return whether or not a reset packet will be sent before this title is sent
*/
public boolean isResetBeforeSend() {
return resetBeforeSend;
}
/**
* Determines whether or not this title has times set on it. If none are set, it will update the
* previous title set on the client.
*
* @return whether or not this title has times set on it
*/
public boolean areTimesSet() {
return stay != 0 || fadeIn != 0 || fadeOut != 0;
}
/**
* Creates a new builder from the contents of this title so that it may be changed.
*
* @return a builder instance with the contents of this title
*/
public Builder toBuilder() {
return new Builder(this);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TextTitle textTitle = (TextTitle) o;
return stay == textTitle.stay &&
fadeIn == textTitle.fadeIn &&
fadeOut == textTitle.fadeOut &&
resetBeforeSend == textTitle.resetBeforeSend &&
Objects.equals(title, textTitle.title) &&
Objects.equals(subtitle, textTitle.subtitle);
}
@Override
public String toString() {
return "TextTitle{" +
"title=" + title +
", subtitle=" + subtitle +
", stay=" + stay +
", fadeIn=" + fadeIn +
", fadeOut=" + fadeOut +
", resetBeforeSend=" + resetBeforeSend +
'}';
}
@Override
public int hashCode() {
return Objects.hash(title, subtitle, stay, fadeIn, fadeOut, resetBeforeSend);
}
/**
* Creates a new builder for constructing titles.
*
* @return a builder for constructing titles
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private @Nullable Component title;
private @Nullable Component subtitle;
private int stay;
private int fadeIn;
private int fadeOut;
private boolean resetBeforeSend = true;
private Builder() {
}
private Builder(TextTitle copy) {
this.title = copy.title;
this.subtitle = copy.subtitle;
this.stay = copy.stay;
this.fadeIn = copy.fadeIn;
this.fadeOut = copy.fadeOut;
this.resetBeforeSend = copy.resetBeforeSend;
}
public Builder title(Component title) {
this.title = Preconditions.checkNotNull(title, "title");
return this;
}
public Builder clearTitle() {
this.title = null;
return this;
}
public Builder subtitle(Component subtitle) {
this.subtitle = Preconditions.checkNotNull(subtitle, "subtitle");
return this;
}
public Builder clearSubtitle() {
this.subtitle = null;
return this;
}
public Builder stay(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.stay = ticks;
return this;
}
public Builder fadeIn(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.fadeIn = ticks;
return this;
}
public Builder fadeOut(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.fadeOut = ticks;
return this;
}
public Builder resetBeforeSend(boolean b) {
this.resetBeforeSend = b;
return this;
}
/**
* Returns the main title this title has, if any.
* @return the main title of this title
*/
public Optional<Component> getTitle() {
return Optional.ofNullable(title);
return Optional.ofNullable(title);
}
/**
* Returns the subtitle this title has, if any.
* @return the subtitle
*/
public Optional<Component> getSubtitle() {
return Optional.ofNullable(subtitle);
return Optional.ofNullable(subtitle);
}
/**
* Returns the number of ticks this title will stay up.
* @return how long the title will stay, in ticks
*/
public int getStay() {
return stay;
return stay;
}
/**
* Returns the number of ticks over which this title will fade in.
* @return how long the title will fade in, in ticks
*/
public int getFadeIn() {
return fadeIn;
return fadeIn;
}
/**
* Returns the number of ticks over which this title will fade out.
* @return how long the title will fade out, in ticks
*/
public int getFadeOut() {
return fadeOut;
return fadeOut;
}
/**
* Returns whether or not a reset packet will be sent before this title is sent. By default, unless explicitly
* disabled, this is enabled by default.
* @return whether or not a reset packet will be sent before this title is sent
*/
public boolean isResetBeforeSend() {
return resetBeforeSend;
return resetBeforeSend;
}
/**
* Determines whether or not this title has times set on it. If none are set, it will update the previous title
* set on the client.
* @return whether or not this title has times set on it
*/
public boolean areTimesSet() {
return stay != 0 || fadeIn != 0 || fadeOut != 0;
}
/**
* Creates a new builder from the contents of this title so that it may be changed.
* @return a builder instance with the contents of this title
*/
public Builder toBuilder() {
return new Builder(this);
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TextTitle textTitle = (TextTitle) o;
return stay == textTitle.stay &&
fadeIn == textTitle.fadeIn &&
fadeOut == textTitle.fadeOut &&
resetBeforeSend == textTitle.resetBeforeSend &&
Objects.equals(title, textTitle.title) &&
Objects.equals(subtitle, textTitle.subtitle);
public TextTitle build() {
return new TextTitle(this);
}
@Override
public String toString() {
return "TextTitle{" +
"title=" + title +
", subtitle=" + subtitle +
", stay=" + stay +
", fadeIn=" + fadeIn +
", fadeOut=" + fadeOut +
", resetBeforeSend=" + resetBeforeSend +
'}';
}
@Override
public int hashCode() {
return Objects.hash(title, subtitle, stay, fadeIn, fadeOut, resetBeforeSend);
}
/**
* Creates a new builder for constructing titles.
* @return a builder for constructing titles
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private @Nullable Component title;
private @Nullable Component subtitle;
private int stay;
private int fadeIn;
private int fadeOut;
private boolean resetBeforeSend = true;
private Builder() {}
private Builder(TextTitle copy) {
this.title = copy.title;
this.subtitle = copy.subtitle;
this.stay = copy.stay;
this.fadeIn = copy.fadeIn;
this.fadeOut = copy.fadeOut;
this.resetBeforeSend = copy.resetBeforeSend;
}
public Builder title(Component title) {
this.title = Preconditions.checkNotNull(title, "title");
return this;
}
public Builder clearTitle() {
this.title = null;
return this;
}
public Builder subtitle(Component subtitle) {
this.subtitle = Preconditions.checkNotNull(subtitle, "subtitle");
return this;
}
public Builder clearSubtitle() {
this.subtitle = null;
return this;
}
public Builder stay(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.stay = ticks;
return this;
}
public Builder fadeIn(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.fadeIn = ticks;
return this;
}
public Builder fadeOut(int ticks) {
Preconditions.checkArgument(ticks >= 0, "ticks value %s is negative", ticks);
this.fadeOut = ticks;
return this;
}
public Builder resetBeforeSend(boolean b) {
this.resetBeforeSend = b;
return this;
}
public Optional<Component> getTitle() {
return Optional.ofNullable(title);
}
public Optional<Component> getSubtitle() {
return Optional.ofNullable(subtitle);
}
public int getStay() {
return stay;
}
public int getFadeIn() {
return fadeIn;
}
public int getFadeOut() {
return fadeOut;
}
public boolean isResetBeforeSend() {
return resetBeforeSend;
}
public TextTitle build() {
return new TextTitle(this);
}
@Override
public String toString() {
return "Builder{" +
"title=" + title +
", subtitle=" + subtitle +
", stay=" + stay +
", fadeIn=" + fadeIn +
", fadeOut=" + fadeOut +
", resetBeforeSend=" + resetBeforeSend +
'}';
}
return "Builder{" +
"title=" + title +
", subtitle=" + subtitle +
", stay=" + stay +
", fadeIn=" + fadeIn +
", fadeOut=" + fadeOut +
", resetBeforeSend=" + resetBeforeSend +
'}';
}
}
}

Datei anzeigen

@ -4,4 +4,5 @@ package com.velocitypowered.api.util.title;
* Represents a title that can be sent to a Minecraft client.
*/
public interface Title {
}

Datei anzeigen

@ -4,47 +4,51 @@ package com.velocitypowered.api.util.title;
* Provides special-purpose titles.
*/
public final class Titles {
private Titles() {
throw new AssertionError();
private Titles() {
throw new AssertionError();
}
private static final Title RESET = new Title() {
@Override
public String toString() {
return "reset title";
}
};
private static final Title RESET = new Title() {
@Override
public String toString() {
return "reset title";
}
};
private static final Title HIDE = new Title() {
@Override
public String toString() {
return "hide title";
}
};
/**
* Returns a title that, when sent to the client, will cause all title data to be reset and any existing title to be
* hidden.
* @return the reset title
*/
public static Title reset() {
return RESET;
private static final Title HIDE = new Title() {
@Override
public String toString() {
return "hide title";
}
};
/**
* Returns a title that, when sent to the client, will cause any existing title to be hidden. The title may be
* restored by a {@link TextTitle} with no title or subtitle (only a time).
* @return the hide title
*/
public static Title hide() {
return HIDE;
}
/**
* Returns a title that, when sent to the client, will cause all title data to be reset and any
* existing title to be hidden.
*
* @return the reset title
*/
public static Title reset() {
return RESET;
}
/**
* Returns a builder for {@link TextTitle}s.
* @return a builder for text titles
*/
public static TextTitle.Builder text() {
return TextTitle.builder();
}
/**
* Returns a title that, when sent to the client, will cause any existing title to be hidden. The
* title may be restored by a {@link TextTitle} with no title or subtitle (only a time).
*
* @return the hide title
*/
public static Title hide() {
return HIDE;
}
/**
* Returns a builder for {@link TextTitle}s.
*
* @return a builder for text titles
*/
public static TextTitle.Builder text() {
return TextTitle.builder();
}
}

Datei anzeigen

@ -1,67 +1,81 @@
package com.velocitypowered.api.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class UuidUtilsTest {
private static final UUID EXPECTED_DASHED_UUID = UUID.fromString("6b501978-d3be-4f33-bcf6-6e7808f37a0d");
private static final String ACTUAL_UNDASHED_UUID = EXPECTED_DASHED_UUID.toString().replace("-", "");
private static final UUID ISSUE_109_ZERO_UUID = new UUID(0, 0);
private static final String ISSUE_109_ZERO_UUID_UNDASHED = "00000000000000000000000000000000";
private static final UUID EXPECTED_DASHED_UUID = UUID
.fromString("6b501978-d3be-4f33-bcf6-6e7808f37a0d");
private static final String ACTUAL_UNDASHED_UUID = EXPECTED_DASHED_UUID.toString()
.replace("-", "");
private static final UUID ISSUE_109_ONE_LSB_UUID = new UUID(0, 1);
private static final String ISSUE_109_ONE_LSB_UUID_UNDASHED = "00000000000000000000000000000001";
private static final UUID ISSUE_109_ZERO_UUID = new UUID(0, 0);
private static final String ISSUE_109_ZERO_UUID_UNDASHED = "00000000000000000000000000000000";
private static final UUID ISSUE_109_ONE_MLSB_UUID = new UUID(1, 1);
private static final String ISSUE_109_ONE_MLSB_UUID_UNDASHED = "00000000000000010000000000000001";
private static final UUID ISSUE_109_ONE_LSB_UUID = new UUID(0, 1);
private static final String ISSUE_109_ONE_LSB_UUID_UNDASHED = "00000000000000000000000000000001";
private static final UUID ISSUE_109_LEADING_ZERO_UUID = UUID.fromString("0d470a25-0416-48a1-b7a6-2a27aa5eb251");
private static final String ISSUE_109_LEADING_ZERO_UNDASHED = "0d470a25041648a1b7a62a27aa5eb251";
private static final UUID ISSUE_109_ONE_MLSB_UUID = new UUID(1, 1);
private static final String ISSUE_109_ONE_MLSB_UUID_UNDASHED = "00000000000000010000000000000001";
private static final UUID TEST_OFFLINE_PLAYER_UUID = UUID.fromString("708f6260-183d-3912-bbde-5e279a5e739a");
private static final String TEST_OFFLINE_PLAYER = "tuxed";
private static final UUID ISSUE_109_LEADING_ZERO_UUID = UUID
.fromString("0d470a25-0416-48a1-b7a6-2a27aa5eb251");
private static final String ISSUE_109_LEADING_ZERO_UNDASHED = "0d470a25041648a1b7a62a27aa5eb251";
@Test
void generateOfflinePlayerUuid() {
assertEquals(TEST_OFFLINE_PLAYER_UUID, UuidUtils.generateOfflinePlayerUuid(TEST_OFFLINE_PLAYER), "UUIDs do not match");
}
private static final UUID TEST_OFFLINE_PLAYER_UUID = UUID
.fromString("708f6260-183d-3912-bbde-5e279a5e739a");
private static final String TEST_OFFLINE_PLAYER = "tuxed";
@Test
void fromUndashed() {
assertEquals(EXPECTED_DASHED_UUID, UuidUtils.fromUndashed(ACTUAL_UNDASHED_UUID), "UUIDs do not match");
}
@Test
void generateOfflinePlayerUuid() {
assertEquals(TEST_OFFLINE_PLAYER_UUID, UuidUtils.generateOfflinePlayerUuid(TEST_OFFLINE_PLAYER),
"UUIDs do not match");
}
@Test
void toUndashed() {
assertEquals(ACTUAL_UNDASHED_UUID, UuidUtils.toUndashed(EXPECTED_DASHED_UUID), "UUIDs do not match");
}
@Test
void fromUndashed() {
assertEquals(EXPECTED_DASHED_UUID, UuidUtils.fromUndashed(ACTUAL_UNDASHED_UUID),
"UUIDs do not match");
}
@Test
void zeroUuidIssue109() {
assertEquals(ISSUE_109_ZERO_UUID, UuidUtils.fromUndashed(ISSUE_109_ZERO_UUID_UNDASHED), "UUIDs do not match");
assertEquals(ISSUE_109_ZERO_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ZERO_UUID), "UUIDs do not match");
}
@Test
void toUndashed() {
assertEquals(ACTUAL_UNDASHED_UUID, UuidUtils.toUndashed(EXPECTED_DASHED_UUID),
"UUIDs do not match");
}
@Test
void leadingZeroUuidIssue109() {
assertEquals(ISSUE_109_LEADING_ZERO_UUID, UuidUtils.fromUndashed(ISSUE_109_LEADING_ZERO_UNDASHED), "UUIDs do not match");
assertEquals(ISSUE_109_LEADING_ZERO_UNDASHED, UuidUtils.toUndashed(ISSUE_109_LEADING_ZERO_UUID), "UUIDs do not match");
}
@Test
void zeroUuidIssue109() {
assertEquals(ISSUE_109_ZERO_UUID, UuidUtils.fromUndashed(ISSUE_109_ZERO_UUID_UNDASHED),
"UUIDs do not match");
assertEquals(ISSUE_109_ZERO_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ZERO_UUID),
"UUIDs do not match");
}
@Test
void oneUuidLsbIssue109() {
assertEquals(ISSUE_109_ONE_LSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_LSB_UUID_UNDASHED), "UUIDs do not match");
assertEquals(ISSUE_109_ONE_LSB_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ONE_LSB_UUID), "UUIDs do not match");
}
@Test
void leadingZeroUuidIssue109() {
assertEquals(ISSUE_109_LEADING_ZERO_UUID,
UuidUtils.fromUndashed(ISSUE_109_LEADING_ZERO_UNDASHED), "UUIDs do not match");
assertEquals(ISSUE_109_LEADING_ZERO_UNDASHED, UuidUtils.toUndashed(ISSUE_109_LEADING_ZERO_UUID),
"UUIDs do not match");
}
@Test
void oneUuidMsbAndLsbIssue109() {
assertEquals(ISSUE_109_ONE_MLSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_MLSB_UUID_UNDASHED), "UUIDs do not match");
assertEquals(ISSUE_109_ONE_MLSB_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ONE_MLSB_UUID), "UUIDs do not match");
}
@Test
void oneUuidLsbIssue109() {
assertEquals(ISSUE_109_ONE_LSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_LSB_UUID_UNDASHED),
"UUIDs do not match");
assertEquals(ISSUE_109_ONE_LSB_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ONE_LSB_UUID),
"UUIDs do not match");
}
@Test
void oneUuidMsbAndLsbIssue109() {
assertEquals(ISSUE_109_ONE_MLSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_MLSB_UUID_UNDASHED),
"UUIDs do not match");
assertEquals(ISSUE_109_ONE_MLSB_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ONE_MLSB_UUID),
"UUIDs do not match");
}
}

Datei anzeigen

@ -0,0 +1,256 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html.
Checkstyle is very configurable. Be sure to read the documentation at
http://checkstyle.sf.net (or in your downloaded distribution).
To completely disable a check, just comment it out or delete it from the file.
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
-->
<module name="Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format"
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message"
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="LineLength">
<property name="max" value="100"/>
<property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap"/>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces"/>
<module name="LeftCurly"/>
<module name="RightCurly">
<property name="id" value="RightCurlySame"/>
<property name="tokens"
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
LITERAL_DO"/>
</module>
<module name="RightCurly">
<property name="id" value="RightCurlyAlone"/>
<property name="option" value="alone"/>
<property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
INSTANCE_INIT"/>
</module>
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LambdaParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="Indentation">
<property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="2"/>
<property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4"/>
<property name="arrayInitIndent" value="2"/>
</module>
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1"/>
</module>
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
</module>
<module name="MethodParamPad"/>
<module name="NoWhitespaceBefore">
<property name="tokens"
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
<module name="JavadocParagraph"/>
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="JavadocMethod">
<property name="scope" value="public"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation"/>
</module>
</module>

Datei anzeigen

@ -25,7 +25,7 @@ dependencies {
implementation "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
} else if (System.getenv("CHECKERFRAMEWORK") == null) {
throw new GradleException("Environment variable CHECKERFRAMEWORK is not set")
} else if (! file(System.getenv("CHECKERFRAMEWORK")).exists()) {
} else if (!file(System.getenv("CHECKERFRAMEWORK")).exists()) {
throw new GradleException("Environment variable CHECKERFRAMEWORK is set to non-existent directory " + System.getenv("CHECKERFRAMEWORK"));
} else {
ext.checkerframeworkdist = "$System.env.CHECKERFRAMEWORK/checker/dist"

4
gradle/checkstyle.gradle Normale Datei
Datei anzeigen

@ -0,0 +1,4 @@
checkstyle {
toolVersion '8.14'
configFile new File(project.rootDir, ['config', 'checkstyle', 'checkstyle.xml'].join(File.separator))
}

Datei anzeigen

@ -1,8 +1,10 @@
plugins {
id 'java'
id 'checkstyle'
}
apply from: '../gradle/checkerframework.gradle'
apply from: '../gradle/checkstyle.gradle'
dependencies {
compile "com.google.guava:guava:${guavaVersion}"

Datei anzeigen

@ -1,13 +1,16 @@
package com.velocitypowered.natives;
/**
* This marker interface indicates that this object should be explicitly disposed before the object can no longer be used.
* Not disposing these objects will likely leak native resources and eventually lead to resource exhaustion.
* This marker interface indicates that this object should be explicitly disposed before the object
* can no longer be used. Not disposing these objects will likely leak native resources and
* eventually lead to resource exhaustion.
*/
public interface Disposable {
/**
* Disposes this object. After this call returns, any use of this object becomes invalid. Multiple calls to
* this function should be safe: there should be no side-effects once an object is disposed.
*/
void dispose();
/**
* Disposes this object. After this call returns, any use of this object becomes invalid. Multiple
* calls to this function should be safe: there should be no side-effects once an object is
* disposed.
*/
void dispose();
}

Datei anzeigen

@ -2,62 +2,62 @@ package com.velocitypowered.natives.compression;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class JavaVelocityCompressor implements VelocityCompressor {
public static final VelocityCompressorFactory FACTORY = JavaVelocityCompressor::new;
private final Deflater deflater;
private final Inflater inflater;
private final byte[] buf;
private boolean disposed = false;
public static final VelocityCompressorFactory FACTORY = JavaVelocityCompressor::new;
private JavaVelocityCompressor(int level) {
this.deflater = new Deflater(level);
this.inflater = new Inflater();
this.buf = new byte[ZLIB_BUFFER_SIZE];
private final Deflater deflater;
private final Inflater inflater;
private final byte[] buf;
private boolean disposed = false;
private JavaVelocityCompressor(int level) {
this.deflater = new Deflater(level);
this.inflater = new Inflater();
this.buf = new byte[ZLIB_BUFFER_SIZE];
}
@Override
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
byte[] inData = new byte[source.readableBytes()];
source.readBytes(inData);
inflater.setInput(inData);
while (!inflater.finished()) {
int read = inflater.inflate(buf);
destination.writeBytes(buf, 0, read);
}
inflater.reset();
}
@Override
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
@Override
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
byte[] inData = new byte[source.readableBytes()];
source.readBytes(inData);
inflater.setInput(inData);
while (!inflater.finished()) {
int read = inflater.inflate(buf);
destination.writeBytes(buf, 0, read);
}
inflater.reset();
byte[] inData = new byte[source.readableBytes()];
source.readBytes(inData);
deflater.setInput(inData);
deflater.finish();
while (!deflater.finished()) {
int bytes = deflater.deflate(buf);
destination.writeBytes(buf, 0, bytes);
}
deflater.reset();
}
@Override
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
@Override
public void dispose() {
disposed = true;
deflater.end();
inflater.end();
}
byte[] inData = new byte[source.readableBytes()];
source.readBytes(inData);
deflater.setInput(inData);
deflater.finish();
while (!deflater.finished()) {
int bytes = deflater.deflate(buf);
destination.writeBytes(buf, 0, bytes);
}
deflater.reset();
}
@Override
public void dispose() {
disposed = true;
deflater.end();
inflater.end();
}
private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed");
}
private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed");
}
}

Datei anzeigen

@ -2,75 +2,78 @@ package com.velocitypowered.natives.compression;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
public class NativeVelocityCompressor implements VelocityCompressor {
public static final VelocityCompressorFactory FACTORY = NativeVelocityCompressor::new;
private final NativeZlibInflate inflate = new NativeZlibInflate();
private final long inflateCtx;
private final NativeZlibDeflate deflate = new NativeZlibDeflate();
private final long deflateCtx;
private boolean disposed = false;
public static final VelocityCompressorFactory FACTORY = NativeVelocityCompressor::new;
private NativeVelocityCompressor(int level) {
this.inflateCtx = inflate.init();
this.deflateCtx = deflate.init(level);
private final NativeZlibInflate inflate = new NativeZlibInflate();
private final long inflateCtx;
private final NativeZlibDeflate deflate = new NativeZlibDeflate();
private final long deflateCtx;
private boolean disposed = false;
private NativeVelocityCompressor(int level) {
this.inflateCtx = inflate.init();
this.deflateCtx = deflate.init(level);
}
@Override
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
source.memoryAddress();
destination.memoryAddress();
while (!inflate.finished && source.isReadable()) {
if (!destination.isWritable()) {
destination.ensureWritable(ZLIB_BUFFER_SIZE);
}
int produced = inflate.process(inflateCtx, source.memoryAddress() + source.readerIndex(),
source.readableBytes(),
destination.memoryAddress() + destination.writerIndex(), destination.writableBytes());
source.readerIndex(source.readerIndex() + inflate.consumed);
destination.writerIndex(destination.writerIndex() + produced);
}
@Override
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
source.memoryAddress();
destination.memoryAddress();
inflate.reset(inflateCtx);
inflate.consumed = 0;
inflate.finished = false;
}
while (!inflate.finished && source.isReadable()) {
if (!destination.isWritable()) {
destination.ensureWritable(ZLIB_BUFFER_SIZE);
}
int produced = inflate.process(inflateCtx, source.memoryAddress() + source.readerIndex(), source.readableBytes(),
destination.memoryAddress() + destination.writerIndex(), destination.writableBytes());
source.readerIndex(source.readerIndex() + inflate.consumed);
destination.writerIndex(destination.writerIndex() + produced);
}
@Override
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
source.memoryAddress();
destination.memoryAddress();
inflate.reset(inflateCtx);
inflate.consumed = 0;
inflate.finished = false;
while (!deflate.finished) {
if (!destination.isWritable()) {
destination.ensureWritable(ZLIB_BUFFER_SIZE);
}
int produced = deflate.process(deflateCtx, source.memoryAddress() + source.readerIndex(),
source.readableBytes(),
destination.memoryAddress() + destination.writerIndex(), destination.writableBytes(),
!source.isReadable());
source.readerIndex(source.readerIndex() + deflate.consumed);
destination.writerIndex(destination.writerIndex() + produced);
}
@Override
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
ensureNotDisposed();
source.memoryAddress();
destination.memoryAddress();
deflate.reset(deflateCtx);
deflate.consumed = 0;
deflate.finished = false;
}
while (!deflate.finished) {
if (!destination.isWritable()) {
destination.ensureWritable(ZLIB_BUFFER_SIZE);
}
int produced = deflate.process(deflateCtx, source.memoryAddress() + source.readerIndex(), source.readableBytes(),
destination.memoryAddress() + destination.writerIndex(), destination.writableBytes(), !source.isReadable());
source.readerIndex(source.readerIndex() + deflate.consumed);
destination.writerIndex(destination.writerIndex() + produced);
}
private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed");
}
deflate.reset(deflateCtx);
deflate.consumed = 0;
deflate.finished = false;
}
private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed");
}
@Override
public void dispose() {
if (!disposed) {
inflate.free(inflateCtx);
deflate.free(deflateCtx);
}
disposed = true;
@Override
public void dispose() {
if (!disposed) {
inflate.free(inflateCtx);
deflate.free(deflateCtx);
}
disposed = true;
}
}

Datei anzeigen

@ -4,21 +4,23 @@ package com.velocitypowered.natives.compression;
* Represents a native interface for zlib's deflate functions.
*/
class NativeZlibDeflate {
boolean finished;
int consumed;
native long init(int level);
boolean finished;
int consumed;
native long free(long ctx);
native long init(int level);
native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress, int destinationLength,
boolean flush);
native long free(long ctx);
native void reset(long ctx);
native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
int destinationLength,
boolean flush);
static {
initIDs();
}
native void reset(long ctx);
private static native void initIDs();
static {
initIDs();
}
private static native void initIDs();
}

Datei anzeigen

@ -4,20 +4,22 @@ package com.velocitypowered.natives.compression;
* Represents a native interface for zlib's inflate functions.
*/
class NativeZlibInflate {
boolean finished;
int consumed;
native long init();
boolean finished;
int consumed;
native long free(long ctx);
native long init();
native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress, int destinationLength);
native long free(long ctx);
native void reset(long ctx);
native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
int destinationLength);
static {
initIDs();
}
native void reset(long ctx);
private static native void initIDs();
static {
initIDs();
}
private static native void initIDs();
}

Datei anzeigen

@ -2,19 +2,19 @@ package com.velocitypowered.natives.compression;
import com.velocitypowered.natives.Disposable;
import io.netty.buffer.ByteBuf;
import java.util.zip.DataFormatException;
/**
* Provides an interface to inflate and deflate {@link ByteBuf}s using zlib.
*/
public interface VelocityCompressor extends Disposable {
/**
* The default preferred output buffer size for zlib.
*/
int ZLIB_BUFFER_SIZE = 8192;
void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
/**
* The default preferred output buffer size for zlib.
*/
int ZLIB_BUFFER_SIZE = 8192;
void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException;
}

Datei anzeigen

@ -1,5 +1,6 @@
package com.velocitypowered.natives.compression;
public interface VelocityCompressorFactory {
VelocityCompressor create(int level);
VelocityCompressor create(int level);
}

Datei anzeigen

@ -2,53 +2,54 @@ package com.velocitypowered.natives.encryption;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import java.security.GeneralSecurityException;
public class JavaVelocityCipher implements VelocityCipher {
public static final VelocityCipherFactory FACTORY = new VelocityCipherFactory() {
@Override
public VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException {
return new JavaVelocityCipher(true, key);
}
@Override
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
return new JavaVelocityCipher(false, key);
}
};
private final Cipher cipher;
private boolean disposed = false;
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key, new IvParameterSpec(key.getEncoded()));
public static final VelocityCipherFactory FACTORY = new VelocityCipherFactory() {
@Override
public VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException {
return new JavaVelocityCipher(true, key);
}
@Override
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
ensureNotDisposed();
byte[] sourceAsBytes = new byte[source.readableBytes()];
source.readBytes(sourceAsBytes);
int outputSize = cipher.getOutputSize(sourceAsBytes.length);
byte[] destinationBytes = new byte[outputSize];
cipher.update(sourceAsBytes, 0, sourceAsBytes.length, destinationBytes);
destination.writeBytes(destinationBytes);
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
return new JavaVelocityCipher(false, key);
}
};
@Override
public void dispose() {
disposed = true;
}
private final Cipher cipher;
private boolean disposed = false;
private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed");
}
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
this.cipher = Cipher.getInstance("AES/CFB8/NoPadding");
this.cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key,
new IvParameterSpec(key.getEncoded()));
}
@Override
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
ensureNotDisposed();
byte[] sourceAsBytes = new byte[source.readableBytes()];
source.readBytes(sourceAsBytes);
int outputSize = cipher.getOutputSize(sourceAsBytes.length);
byte[] destinationBytes = new byte[outputSize];
cipher.update(sourceAsBytes, 0, sourceAsBytes.length, destinationBytes);
destination.writeBytes(destinationBytes);
}
@Override
public void dispose() {
disposed = true;
}
private void ensureNotDisposed() {
Preconditions.checkState(!disposed, "Object already disposed");
}
}

Datei anzeigen

@ -1,9 +1,11 @@
package com.velocitypowered.natives.encryption;
public class MbedtlsAesImpl {
native long init(byte[] key);
native void process(long ctx, long sourceAddress, int sourceLength, long destinationAddress, boolean encrypt);
native long init(byte[] key);
native void free(long ptr);
native void process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
boolean encrypt);
native void free(long ptr);
}

Datei anzeigen

@ -1,55 +1,55 @@
package com.velocitypowered.natives.encryption;
import io.netty.buffer.ByteBuf;
import java.security.GeneralSecurityException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import java.security.GeneralSecurityException;
public class NativeVelocityCipher implements VelocityCipher {
public static final VelocityCipherFactory FACTORY = new VelocityCipherFactory() {
@Override
public VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException {
return new NativeVelocityCipher(true, key);
}
@Override
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
return new NativeVelocityCipher(false, key);
}
};
private static final MbedtlsAesImpl impl = new MbedtlsAesImpl();
private final long ctx;
private final boolean encrypt;
private boolean disposed = false;
private NativeVelocityCipher(boolean encrypt, SecretKey key) {
this.encrypt = encrypt;
this.ctx = impl.init(key.getEncoded());
public static final VelocityCipherFactory FACTORY = new VelocityCipherFactory() {
@Override
public VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException {
return new NativeVelocityCipher(true, key);
}
@Override
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
source.memoryAddress();
destination.memoryAddress();
// The exact amount we read in is also the amount we write out.
int len = source.readableBytes();
destination.ensureWritable(len);
impl.process(ctx, source.memoryAddress() + source.readerIndex(), len,
destination.memoryAddress() + destination.writerIndex(), encrypt);
source.skipBytes(len);
destination.writerIndex(destination.writerIndex() + len);
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
return new NativeVelocityCipher(false, key);
}
};
private static final MbedtlsAesImpl impl = new MbedtlsAesImpl();
@Override
public void dispose() {
if (!disposed) {
impl.free(ctx);
}
disposed = true;
private final long ctx;
private final boolean encrypt;
private boolean disposed = false;
private NativeVelocityCipher(boolean encrypt, SecretKey key) {
this.encrypt = encrypt;
this.ctx = impl.init(key.getEncoded());
}
@Override
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
source.memoryAddress();
destination.memoryAddress();
// The exact amount we read in is also the amount we write out.
int len = source.readableBytes();
destination.ensureWritable(len);
impl.process(ctx, source.memoryAddress() + source.readerIndex(), len,
destination.memoryAddress() + destination.writerIndex(), encrypt);
source.skipBytes(len);
destination.writerIndex(destination.writerIndex() + len);
}
@Override
public void dispose() {
if (!disposed) {
impl.free(ctx);
}
disposed = true;
}
}

Datei anzeigen

@ -2,9 +2,9 @@ package com.velocitypowered.natives.encryption;
import com.velocitypowered.natives.Disposable;
import io.netty.buffer.ByteBuf;
import javax.crypto.ShortBufferException;
public interface VelocityCipher extends Disposable {
void process(ByteBuf source, ByteBuf destination) throws ShortBufferException;
void process(ByteBuf source, ByteBuf destination) throws ShortBufferException;
}

Datei anzeigen

@ -1,10 +1,11 @@
package com.velocitypowered.natives.encryption;
import javax.crypto.SecretKey;
import java.security.GeneralSecurityException;
import javax.crypto.SecretKey;
public interface VelocityCipherFactory {
VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException;
VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException;
VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException;
VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException;
}

Datei anzeigen

@ -1,81 +1,85 @@
package com.velocitypowered.natives.util;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class NativeCodeLoader<T> implements Supplier<T> {
private final Variant<T> selected;
NativeCodeLoader(List<Variant<T>> variants) {
this.selected = getVariant(variants);
private final Variant<T> selected;
NativeCodeLoader(List<Variant<T>> variants) {
this.selected = getVariant(variants);
}
@Override
public T get() {
return selected.object;
}
private static <T> Variant<T> getVariant(List<Variant<T>> variants) {
for (Variant<T> variant : variants) {
T got = variant.get();
if (got == null) {
continue;
}
return variant;
}
throw new IllegalArgumentException("Can't find any suitable variants");
}
public String getLoadedVariant() {
return selected.name;
}
static class Variant<T> {
private Status status;
private final Runnable setup;
private final String name;
private final T object;
Variant(BooleanSupplier possiblyAvailable, Runnable setup, String name, T object) {
this.status =
possiblyAvailable.getAsBoolean() ? Status.POSSIBLY_AVAILABLE : Status.NOT_AVAILABLE;
this.setup = setup;
this.name = name;
this.object = object;
}
@Override
public T get() {
return selected.object;
}
public @Nullable T get() {
if (status == Status.NOT_AVAILABLE || status == Status.SETUP_FAILURE) {
return null;
}
private static <T> Variant<T> getVariant(List<Variant<T>> variants) {
for (Variant<T> variant : variants) {
T got = variant.get();
if (got == null) {
continue;
}
return variant;
// Make sure setup happens only once
if (status == Status.POSSIBLY_AVAILABLE) {
try {
setup.run();
status = Status.SETUP;
} catch (Exception e) {
status = Status.SETUP_FAILURE;
return null;
}
throw new IllegalArgumentException("Can't find any suitable variants");
}
return object;
}
}
public String getLoadedVariant() {
return selected.name;
}
private enum Status {
NOT_AVAILABLE,
POSSIBLY_AVAILABLE,
SETUP,
SETUP_FAILURE
}
static class Variant<T> {
private Status status;
private final Runnable setup;
private final String name;
private final T object;
Variant(BooleanSupplier possiblyAvailable, Runnable setup, String name, T object) {
this.status = possiblyAvailable.getAsBoolean() ? Status.POSSIBLY_AVAILABLE : Status.NOT_AVAILABLE;
this.setup = setup;
this.name = name;
this.object = object;
}
public @Nullable T get() {
if (status == Status.NOT_AVAILABLE || status == Status.SETUP_FAILURE) {
return null;
}
// Make sure setup happens only once
if (status == Status.POSSIBLY_AVAILABLE) {
try {
setup.run();
status = Status.SETUP;
} catch (Exception e) {
status = Status.SETUP_FAILURE;
return null;
}
}
return object;
}
}
private enum Status {
NOT_AVAILABLE,
POSSIBLY_AVAILABLE,
SETUP,
SETUP_FAILURE
}
static final BooleanSupplier MACOS = () -> System.getProperty("os.name", "").equalsIgnoreCase("Mac OS X") &&
System.getProperty("os.arch").equals("x86_64");
static final BooleanSupplier LINUX = () -> System.getProperties().getProperty("os.name", "").equalsIgnoreCase("Linux") &&
System.getProperty("os.arch").equals("amd64");
static final BooleanSupplier ALWAYS = () -> true;
static final BooleanSupplier MACOS = () ->
System.getProperty("os.name", "").equalsIgnoreCase("Mac OS X") &&
System.getProperty("os.arch").equals("x86_64");
static final BooleanSupplier LINUX = () ->
System.getProperties().getProperty("os.name", "").equalsIgnoreCase("Linux") &&
System.getProperty("os.arch").equals("amd64");
static final BooleanSupplier ALWAYS = () -> true;
}

Datei anzeigen

@ -6,7 +6,6 @@ import com.velocitypowered.natives.compression.NativeVelocityCompressor;
import com.velocitypowered.natives.compression.VelocityCompressorFactory;
import com.velocitypowered.natives.encryption.JavaVelocityCipher;
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@ -14,55 +13,58 @@ import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class Natives {
private Natives() {
throw new AssertionError();
}
private static Runnable copyAndLoadNative(String path) {
return () -> {
try {
Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.')));
InputStream nativeLib = Natives.class.getResourceAsStream(path);
if (nativeLib == null) {
throw new IllegalStateException("Native library " + path + " not found.");
}
private Natives() {
throw new AssertionError();
}
Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
Files.deleteIfExists(tempFile);
} catch (IOException ignored) {
// Well, it doesn't matter...
}
}));
System.load(tempFile.toAbsolutePath().toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
};
}
private static Runnable copyAndLoadNative(String path) {
return () -> {
try {
Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.')));
InputStream nativeLib = Natives.class.getResourceAsStream(path);
if (nativeLib == null) {
throw new IllegalStateException("Native library " + path + " not found.");
}
public static final NativeCodeLoader<VelocityCompressorFactory> compressor = new NativeCodeLoader<>(
ImmutableList.of(
new NativeCodeLoader.Variant<>(NativeCodeLoader.MACOS,
copyAndLoadNative("/macosx/velocity-compress.dylib"), "native (macOS)",
NativeVelocityCompressor.FACTORY),
new NativeCodeLoader.Variant<>(NativeCodeLoader.LINUX,
copyAndLoadNative("/linux_x64/velocity-compress.so"), "native (Linux amd64)",
NativeVelocityCompressor.FACTORY),
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {}, "Java", JavaVelocityCompressor.FACTORY)
)
);
Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
Files.deleteIfExists(tempFile);
} catch (IOException ignored) {
// Well, it doesn't matter...
}
}));
System.load(tempFile.toAbsolutePath().toString());
} catch (IOException e) {
throw new RuntimeException(e);
}
};
}
public static final NativeCodeLoader<VelocityCipherFactory> cipher = new NativeCodeLoader<>(
ImmutableList.of(
public static final NativeCodeLoader<VelocityCompressorFactory> compressor = new NativeCodeLoader<>(
ImmutableList.of(
new NativeCodeLoader.Variant<>(NativeCodeLoader.MACOS,
copyAndLoadNative("/macosx/velocity-compress.dylib"), "native (macOS)",
NativeVelocityCompressor.FACTORY),
new NativeCodeLoader.Variant<>(NativeCodeLoader.LINUX,
copyAndLoadNative("/linux_x64/velocity-compress.so"), "native (Linux amd64)",
NativeVelocityCompressor.FACTORY),
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {
}, "Java", JavaVelocityCompressor.FACTORY)
)
);
public static final NativeCodeLoader<VelocityCipherFactory> cipher = new NativeCodeLoader<>(
ImmutableList.of(
/*new NativeCodeLoader.Variant<>(NativeCodeLoader.MACOS,
copyAndLoadNative("/macosx/velocity-cipher.dylib"), "mbed TLS (macOS)",
NativeVelocityCipher.FACTORY),
new NativeCodeLoader.Variant<>(NativeCodeLoader.LINUX,
copyAndLoadNative("/linux_x64/velocity-cipher.so"), "mbed TLS (Linux amd64)",
NativeVelocityCipher.FACTORY),*/
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {}, "Java", JavaVelocityCipher.FACTORY)
)
);
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {
}, "Java", JavaVelocityCipher.FACTORY)
)
);
}

Datei anzeigen

@ -1,65 +1,66 @@
package com.velocitypowered.natives.compression;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import java.util.Random;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.condition.OS.LINUX;
import static org.junit.jupiter.api.condition.OS.MAC;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.util.Random;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
class VelocityCompressorTest {
@BeforeAll
static void checkNatives() {
Natives.compressor.getLoadedVariant();
@BeforeAll
static void checkNatives() {
Natives.compressor.getLoadedVariant();
}
@Test
@EnabledOnOs({MAC, LINUX})
void nativeIntegrityCheck() throws DataFormatException {
VelocityCompressor compressor = Natives.compressor.get().create(Deflater.DEFAULT_COMPRESSION);
if (compressor instanceof JavaVelocityCompressor) {
compressor.dispose();
fail("Loaded regular compressor");
}
check(compressor);
}
@Test
@EnabledOnOs({ MAC, LINUX })
void nativeIntegrityCheck() throws DataFormatException {
VelocityCompressor compressor = Natives.compressor.get().create(Deflater.DEFAULT_COMPRESSION);
if (compressor instanceof JavaVelocityCompressor) {
compressor.dispose();
fail("Loaded regular compressor");
}
check(compressor);
}
@Test
void javaIntegrityCheck() throws DataFormatException {
VelocityCompressor compressor = JavaVelocityCompressor.FACTORY.create(Deflater.DEFAULT_COMPRESSION);
check(compressor);
}
private void check(VelocityCompressor compressor) throws DataFormatException {
ByteBuf source = Unpooled.directBuffer();
ByteBuf dest = Unpooled.directBuffer();
ByteBuf decompressed = Unpooled.directBuffer();
Random random = new Random(1);
byte[] randomBytes = new byte[1 << 16];
random.nextBytes(randomBytes);
source.writeBytes(randomBytes);
try {
compressor.deflate(source, dest);
compressor.inflate(dest, decompressed);
source.readerIndex(0);
assertTrue(ByteBufUtil.equals(source, decompressed));
} finally {
source.release();
dest.release();
decompressed.release();
compressor.dispose();
}
@Test
void javaIntegrityCheck() throws DataFormatException {
VelocityCompressor compressor = JavaVelocityCompressor.FACTORY
.create(Deflater.DEFAULT_COMPRESSION);
check(compressor);
}
private void check(VelocityCompressor compressor) throws DataFormatException {
ByteBuf source = Unpooled.directBuffer();
ByteBuf dest = Unpooled.directBuffer();
ByteBuf decompressed = Unpooled.directBuffer();
Random random = new Random(1);
byte[] randomBytes = new byte[1 << 16];
random.nextBytes(randomBytes);
source.writeBytes(randomBytes);
try {
compressor.deflate(source, dest);
compressor.inflate(dest, decompressed);
source.readerIndex(0);
assertTrue(ByteBufUtil.equals(source, decompressed));
} finally {
source.release();
dest.release();
decompressed.release();
compressor.dispose();
}
}
}

Datei anzeigen

@ -1,71 +1,71 @@
package com.velocitypowered.natives.encryption;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import com.velocitypowered.natives.util.Natives;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.security.GeneralSecurityException;
import java.util.Random;
import javax.crypto.spec.SecretKeySpec;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.util.Random;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
class VelocityCipherTest {
private static final int ENCRYPT_DATA_SIZE = 1 << 16;
@BeforeAll
static void checkNatives() {
Natives.cipher.getLoadedVariant();
private static final int ENCRYPT_DATA_SIZE = 1 << 16;
@BeforeAll
static void checkNatives() {
Natives.cipher.getLoadedVariant();
}
@Test
@Disabled
void nativeIntegrityCheck() throws GeneralSecurityException {
VelocityCipherFactory factory = Natives.cipher.get();
if (factory == JavaVelocityCipher.FACTORY) {
fail("Loaded regular compressor");
}
check(factory);
}
@Test
@Disabled
void nativeIntegrityCheck() throws GeneralSecurityException {
VelocityCipherFactory factory = Natives.cipher.get();
if (factory == JavaVelocityCipher.FACTORY) {
fail("Loaded regular compressor");
}
check(factory);
}
@Test
void javaIntegrityCheck() throws GeneralSecurityException {
check(JavaVelocityCipher.FACTORY);
}
private void check(VelocityCipherFactory factory) throws GeneralSecurityException {
// Generate a random 16-byte key.
Random random = new Random(1);
byte[] key = new byte[16];
random.nextBytes(key);
VelocityCipher decrypt = factory.forDecryption(new SecretKeySpec(key, "AES"));
VelocityCipher encrypt = factory.forEncryption(new SecretKeySpec(key, "AES"));
ByteBuf source = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
ByteBuf dest = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
ByteBuf decryptionBuf = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
byte[] randomBytes = new byte[ENCRYPT_DATA_SIZE];
random.nextBytes(randomBytes);
source.writeBytes(randomBytes);
try {
encrypt.process(source, dest);
decrypt.process(dest, decryptionBuf);
source.readerIndex(0);
assertTrue(ByteBufUtil.equals(source, decryptionBuf));
} finally {
source.release();
dest.release();
decryptionBuf.release();
decrypt.dispose();
encrypt.dispose();
}
@Test
void javaIntegrityCheck() throws GeneralSecurityException {
check(JavaVelocityCipher.FACTORY);
}
private void check(VelocityCipherFactory factory) throws GeneralSecurityException {
// Generate a random 16-byte key.
Random random = new Random(1);
byte[] key = new byte[16];
random.nextBytes(key);
VelocityCipher decrypt = factory.forDecryption(new SecretKeySpec(key, "AES"));
VelocityCipher encrypt = factory.forEncryption(new SecretKeySpec(key, "AES"));
ByteBuf source = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
ByteBuf dest = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
ByteBuf decryptionBuf = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
byte[] randomBytes = new byte[ENCRYPT_DATA_SIZE];
random.nextBytes(randomBytes);
source.writeBytes(randomBytes);
try {
encrypt.process(source, dest);
decrypt.process(dest, decryptionBuf);
source.readerIndex(0);
assertTrue(ByteBufUtil.equals(source, decryptionBuf));
} finally {
source.release();
dest.release();
decryptionBuf.release();
decrypt.dispose();
encrypt.dispose();
}
}
}

Datei anzeigen

@ -2,17 +2,11 @@ plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '2.0.4'
id 'de.sebastianboegl.shadow.transformer.log4j' version '2.1.1'
id 'checkstyle'
}
apply from: '../gradle/checkerframework.gradle'
compileJava {
options.compilerArgs += ['-proc:none']
}
compileTestJava {
options.compilerArgs += ['-proc:none']
}
apply from: '../gradle/checkstyle.gradle'
jar {
manifest {

Datei anzeigen

@ -1,29 +1,29 @@
package com.velocitypowered.proxy;
import java.text.DecimalFormat;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.text.DecimalFormat;
public class Velocity {
private static final Logger logger = LogManager.getLogger(Velocity.class);
static {
// We use BufferedImage for favicons, and on macOS this puts the Java application in the dock. How inconvenient.
// Force AWT to work with its head chopped off.
System.setProperty("java.awt.headless", "true");
}
private static final Logger logger = LogManager.getLogger(Velocity.class);
public static void main(String... args) {
long startTime = System.currentTimeMillis();
static {
// We use BufferedImage for favicons, and on macOS this puts the Java application in the dock. How inconvenient.
// Force AWT to work with its head chopped off.
System.setProperty("java.awt.headless", "true");
}
VelocityServer server = new VelocityServer();
server.start();
public static void main(String... args) {
long startTime = System.currentTimeMillis();
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown, "Shutdown thread"));
VelocityServer server = new VelocityServer();
server.start();
double bootTime = (System.currentTimeMillis() - startTime) / 1000d;
logger.info("Done ({}s)!", new DecimalFormat("#.##").format(bootTime));
server.getConsoleCommandSource().start();
}
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown, "Shutdown thread"));
double bootTime = (System.currentTimeMillis() - startTime) / 1000d;
logger.info("Done ({}s)!", new DecimalFormat("#.##").format(bootTime));
server.getConsoleCommandSource().start();
}
}

Datei anzeigen

@ -24,7 +24,6 @@ import com.velocitypowered.proxy.config.AnnotatedConfig;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.network.http.NettyHttpClient;
import com.velocitypowered.proxy.plugin.VelocityEventManager;
@ -36,356 +35,367 @@ import com.velocitypowered.proxy.server.ServerMap;
import com.velocitypowered.proxy.util.AddressUtil;
import com.velocitypowered.proxy.util.EncryptionUtils;
import com.velocitypowered.proxy.util.Ratelimiter;
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
import io.netty.bootstrap.Bootstrap;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
import net.kyori.text.serializer.GsonComponentSerializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.*;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.util.*;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import net.kyori.text.Component;
import net.kyori.text.TextComponent;
import net.kyori.text.serializer.GsonComponentSerializer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
public class VelocityServer implements ProxyServer {
private static final Logger logger = LogManager.getLogger(VelocityServer.class);
public static final Gson GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
.create();
private static final Logger logger = LogManager.getLogger(VelocityServer.class);
public static final Gson GSON = new GsonBuilder()
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
.create();
private @MonotonicNonNull ConnectionManager cm;
private @MonotonicNonNull VelocityConfiguration configuration;
private @MonotonicNonNull NettyHttpClient httpClient;
private @MonotonicNonNull KeyPair serverKeyPair;
private @MonotonicNonNull ServerMap servers;
private final VelocityCommandManager commandManager = new VelocityCommandManager();
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
private boolean shutdown = false;
private @MonotonicNonNull VelocityPluginManager pluginManager;
private @MonotonicNonNull ConnectionManager cm;
private @MonotonicNonNull VelocityConfiguration configuration;
private @MonotonicNonNull NettyHttpClient httpClient;
private @MonotonicNonNull KeyPair serverKeyPair;
private @MonotonicNonNull ServerMap servers;
private final VelocityCommandManager commandManager = new VelocityCommandManager();
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
private boolean shutdown = false;
private @MonotonicNonNull VelocityPluginManager pluginManager;
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
private @MonotonicNonNull VelocityConsole console;
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
private @MonotonicNonNull VelocityEventManager eventManager;
private @MonotonicNonNull VelocityScheduler scheduler;
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
private @MonotonicNonNull VelocityConsole console;
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
private @MonotonicNonNull VelocityEventManager eventManager;
private @MonotonicNonNull VelocityScheduler scheduler;
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
public KeyPair getServerKeyPair() {
if (serverKeyPair == null) {
throw new AssertionError();
}
return serverKeyPair;
public KeyPair getServerKeyPair() {
if (serverKeyPair == null) {
throw new AssertionError();
}
return serverKeyPair;
}
public VelocityConfiguration getConfiguration() {
VelocityConfiguration cfg = this.configuration;
if (cfg == null) {
throw new IllegalStateException("Configuration not initialized!");
}
return cfg;
}
@Override
public ProxyVersion getVersion() {
Package pkg = VelocityServer.class.getPackage();
String implName;
String implVersion;
String implVendor;
if (pkg != null) {
implName = MoreObjects.firstNonNull(pkg.getImplementationTitle(), "Velocity");
implVersion = MoreObjects.firstNonNull(pkg.getImplementationVersion(), "<unknown>");
implVendor = MoreObjects.firstNonNull(pkg.getImplementationVendor(), "Velocity Contributors");
} else {
implName = "Velocity";
implVersion = "<unknown>";
implVendor = "Velocity Contributors";
}
public VelocityConfiguration getConfiguration() {
VelocityConfiguration cfg = this.configuration;
if (cfg == null) {
throw new IllegalStateException("Configuration not initialized!");
}
return cfg;
return new ProxyVersion(implName, implVendor, implVersion);
}
@Override
public VelocityCommandManager getCommandManager() {
return commandManager;
}
@EnsuresNonNull({"serverKeyPair", "servers", "pluginManager", "eventManager", "scheduler",
"console", "cm", "configuration"})
public void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
pluginManager = new VelocityPluginManager(this);
eventManager = new VelocityEventManager(pluginManager);
scheduler = new VelocityScheduler(pluginManager);
console = new VelocityConsole(this);
cm = new ConnectionManager(this);
servers = new ServerMap(this);
cm.logChannelInformation();
// Initialize commands first
commandManager.register(new VelocityCommand(this), "velocity");
commandManager.register(new ServerCommand(this), "server");
commandManager.register(new ShutdownCommand(this), "shutdown", "end");
try {
Path configPath = Paths.get("velocity.toml");
configuration = VelocityConfiguration.read(configPath);
if (!configuration.validate()) {
logger.error(
"Your configuration is invalid. Velocity will refuse to start up until the errors are resolved.");
LogManager.shutdown();
System.exit(1);
}
AnnotatedConfig
.saveConfig(configuration.dumpConfig(), configPath); //Resave config to add new values
} catch (Exception e) {
logger.error("Unable to read/load/save your velocity.toml. The server will shut down.", e);
LogManager.shutdown();
System.exit(1);
}
@Override
public ProxyVersion getVersion() {
Package pkg = VelocityServer.class.getPackage();
String implName;
String implVersion;
String implVendor;
if (pkg != null) {
implName = MoreObjects.firstNonNull(pkg.getImplementationTitle(), "Velocity");
implVersion = MoreObjects.firstNonNull(pkg.getImplementationVersion(), "<unknown>");
implVendor = MoreObjects.firstNonNull(pkg.getImplementationVendor(), "Velocity Contributors");
} else {
implName = "Velocity";
implVersion = "<unknown>";
implVendor = "Velocity Contributors";
for (Map.Entry<String, String> entry : configuration.getServers().entrySet()) {
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
}
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
httpClient = new NettyHttpClient(this);
loadPlugins();
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
// to fully initialize before we accept any connections to the server.
eventManager.fire(new ProxyInitializeEvent()).join();
// init console permissions after plugins are loaded
console.setupPermissions();
this.cm.bind(configuration.getBind());
if (configuration.isQueryEnabled()) {
this.cm.queryBind(configuration.getBind().getHostString(), configuration.getQueryPort());
}
}
@RequiresNonNull({"pluginManager", "eventManager"})
private void loadPlugins() {
logger.info("Loading plugins...");
try {
Path pluginPath = Paths.get("plugins");
if (!pluginPath.toFile().exists()) {
Files.createDirectory(pluginPath);
} else {
if (!pluginPath.toFile().isDirectory()) {
logger.warn("Plugin location {} is not a directory, continuing without loading plugins",
pluginPath);
return;
}
return new ProxyVersion(implName, implVendor, implVersion);
pluginManager.loadPlugins(pluginPath);
}
} catch (Exception e) {
logger.error("Couldn't load plugins", e);
}
@Override
public VelocityCommandManager getCommandManager() {
return commandManager;
// Register the plugin main classes so that we may proceed with firing the proxy initialize event
for (PluginContainer plugin : pluginManager.getPlugins()) {
Optional<?> instance = plugin.getInstance();
if (instance.isPresent()) {
eventManager.register(instance.get(), instance.get());
}
}
@EnsuresNonNull({"serverKeyPair", "servers", "pluginManager", "eventManager", "scheduler", "console", "cm", "configuration"})
public void start() {
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
}
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
pluginManager = new VelocityPluginManager(this);
eventManager = new VelocityEventManager(pluginManager);
scheduler = new VelocityScheduler(pluginManager);
console = new VelocityConsole(this);
cm = new ConnectionManager(this);
servers = new ServerMap(this);
public Bootstrap initializeGenericBootstrap() {
if (cm == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return this.cm.createWorker();
}
cm.logChannelInformation();
public boolean isShutdown() {
return shutdown;
}
// Initialize commands first
commandManager.register(new VelocityCommand(this), "velocity");
commandManager.register(new ServerCommand(this), "server");
commandManager.register(new ShutdownCommand(this), "shutdown", "end");
try {
Path configPath = Paths.get("velocity.toml");
configuration = VelocityConfiguration.read(configPath);
if (!configuration.validate()) {
logger.error("Your configuration is invalid. Velocity will refuse to start up until the errors are resolved.");
LogManager.shutdown();
System.exit(1);
}
AnnotatedConfig.saveConfig(configuration.dumpConfig(), configPath); //Resave config to add new values
} catch (Exception e) {
logger.error("Unable to read/load/save your velocity.toml. The server will shut down.", e);
LogManager.shutdown();
System.exit(1);
}
for (Map.Entry<String, String> entry : configuration.getServers().entrySet()) {
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
}
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
httpClient = new NettyHttpClient(this);
loadPlugins();
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
// to fully initialize before we accept any connections to the server.
eventManager.fire(new ProxyInitializeEvent()).join();
// init console permissions after plugins are loaded
console.setupPermissions();
this.cm.bind(configuration.getBind());
if (configuration.isQueryEnabled()) {
this.cm.queryBind(configuration.getBind().getHostString(), configuration.getQueryPort());
}
public void shutdown() {
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
throw new AssertionError();
}
@RequiresNonNull({"pluginManager", "eventManager"})
private void loadPlugins() {
logger.info("Loading plugins...");
if (!shutdownInProgress.compareAndSet(false, true)) {
return;
}
logger.info("Shutting down the proxy...");
try {
Path pluginPath = Paths.get("plugins");
if (!pluginPath.toFile().exists()) {
Files.createDirectory(pluginPath);
} else {
if (!pluginPath.toFile().isDirectory()) {
logger.warn("Plugin location {} is not a directory, continuing without loading plugins", pluginPath);
return;
}
pluginManager.loadPlugins(pluginPath);
}
} catch (Exception e) {
logger.error("Couldn't load plugins", e);
}
// Register the plugin main classes so that we may proceed with firing the proxy initialize event
for (PluginContainer plugin : pluginManager.getPlugins()) {
Optional<?> instance = plugin.getInstance();
if (instance.isPresent()) {
eventManager.register(instance.get(), instance.get());
}
}
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
for (ConnectedPlayer player : ImmutableList.copyOf(connectionsByUuid.values())) {
player.close(TextComponent.of("Proxy shutting down."));
}
public Bootstrap initializeGenericBootstrap() {
if (cm == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return this.cm.createWorker();
this.cm.shutdown();
eventManager.fire(new ProxyShutdownEvent());
try {
if (!eventManager.shutdown() || !scheduler.shutdown()) {
logger.error("Your plugins took over 10 seconds to shut down.");
}
} catch (InterruptedException e) {
// Not much we can do about this...
Thread.currentThread().interrupt();
}
public boolean isShutdown() {
return shutdown;
shutdown = true;
}
public NettyHttpClient getHttpClient() {
if (httpClient == null) {
throw new IllegalStateException("HTTP client not initialized");
}
return httpClient;
}
public void shutdown() {
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
throw new AssertionError();
}
if (!shutdownInProgress.compareAndSet(false, true)) {
return;
}
logger.info("Shutting down the proxy...");
for (ConnectedPlayer player : ImmutableList.copyOf(connectionsByUuid.values())) {
player.close(TextComponent.of("Proxy shutting down."));
}
this.cm.shutdown();
eventManager.fire(new ProxyShutdownEvent());
try {
if (!eventManager.shutdown() || !scheduler.shutdown()) {
logger.error("Your plugins took over 10 seconds to shut down.");
}
} catch (InterruptedException e) {
// Not much we can do about this...
Thread.currentThread().interrupt();
}
shutdown = true;
public Ratelimiter getIpAttemptLimiter() {
if (ipAttemptLimiter == null) {
throw new IllegalStateException("Ratelimiter not initialized");
}
return ipAttemptLimiter;
}
public NettyHttpClient getHttpClient() {
if (httpClient == null) {
throw new IllegalStateException("HTTP client not initialized");
}
return httpClient;
public boolean registerConnection(ConnectedPlayer connection) {
String lowerName = connection.getUsername().toLowerCase(Locale.US);
if (connectionsByName.putIfAbsent(lowerName, connection) != null) {
return false;
}
if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) {
connectionsByName.remove(lowerName, connection);
return false;
}
return true;
}
public Ratelimiter getIpAttemptLimiter() {
if (ipAttemptLimiter == null) {
throw new IllegalStateException("Ratelimiter not initialized");
}
return ipAttemptLimiter;
}
public void unregisterConnection(ConnectedPlayer connection) {
connectionsByName.remove(connection.getUsername().toLowerCase(Locale.US), connection);
connectionsByUuid.remove(connection.getUniqueId(), connection);
}
public boolean registerConnection(ConnectedPlayer connection) {
String lowerName = connection.getUsername().toLowerCase(Locale.US);
if (connectionsByName.putIfAbsent(lowerName, connection) != null) {
return false;
}
if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) {
connectionsByName.remove(lowerName, connection);
return false;
}
return true;
}
@Override
public Optional<Player> getPlayer(String username) {
Preconditions.checkNotNull(username, "username");
return Optional.ofNullable(connectionsByName.get(username.toLowerCase(Locale.US)));
}
public void unregisterConnection(ConnectedPlayer connection) {
connectionsByName.remove(connection.getUsername().toLowerCase(Locale.US), connection);
connectionsByUuid.remove(connection.getUniqueId(), connection);
}
@Override
public Optional<Player> getPlayer(UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid");
return Optional.ofNullable(connectionsByUuid.get(uuid));
}
@Override
public Optional<Player> getPlayer(String username) {
Preconditions.checkNotNull(username, "username");
return Optional.ofNullable(connectionsByName.get(username.toLowerCase(Locale.US)));
@Override
public void broadcast(Component component) {
Preconditions.checkNotNull(component, "component");
Chat chat = Chat.createClientbound(component);
for (ConnectedPlayer player : connectionsByUuid.values()) {
player.getConnection().write(chat);
}
}
@Override
public Optional<Player> getPlayer(UUID uuid) {
Preconditions.checkNotNull(uuid, "uuid");
return Optional.ofNullable(connectionsByUuid.get(uuid));
}
@Override
public Collection<Player> getAllPlayers() {
return ImmutableList.copyOf(connectionsByUuid.values());
}
@Override
public void broadcast(Component component) {
Preconditions.checkNotNull(component, "component");
Chat chat = Chat.createClientbound(component);
for (ConnectedPlayer player : connectionsByUuid.values()) {
player.getConnection().write(chat);
}
}
@Override
public int getPlayerCount() {
return connectionsByUuid.size();
}
@Override
public Collection<Player> getAllPlayers() {
return ImmutableList.copyOf(connectionsByUuid.values());
@Override
public Optional<RegisteredServer> getServer(String name) {
Preconditions.checkNotNull(name, "name");
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.getServer(name);
}
@Override
public int getPlayerCount() {
return connectionsByUuid.size();
@Override
public Collection<RegisteredServer> getAllServers() {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.getAllServers();
}
@Override
public Optional<RegisteredServer> getServer(String name) {
Preconditions.checkNotNull(name, "name");
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.getServer(name);
@Override
public RegisteredServer registerServer(ServerInfo server) {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.register(server);
}
@Override
public Collection<RegisteredServer> getAllServers() {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.getAllServers();
@Override
public void unregisterServer(ServerInfo server) {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
servers.unregister(server);
}
@Override
public RegisteredServer registerServer(ServerInfo server) {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return servers.register(server);
@Override
public VelocityConsole getConsoleCommandSource() {
if (console == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return console;
}
@Override
public void unregisterServer(ServerInfo server) {
if (servers == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
servers.unregister(server);
@Override
public PluginManager getPluginManager() {
if (pluginManager == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return pluginManager;
}
@Override
public VelocityConsole getConsoleCommandSource() {
if (console == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return console;
@Override
public EventManager getEventManager() {
if (eventManager == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return eventManager;
}
@Override
public PluginManager getPluginManager() {
if (pluginManager == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return pluginManager;
@Override
public VelocityScheduler getScheduler() {
if (scheduler == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return scheduler;
}
@Override
public EventManager getEventManager() {
if (eventManager == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return eventManager;
}
@Override
public VelocityChannelRegistrar getChannelRegistrar() {
return channelRegistrar;
}
@Override
public VelocityScheduler getScheduler() {
if (scheduler == null) {
throw new IllegalStateException("Server did not initialize properly.");
}
return scheduler;
}
@Override
public VelocityChannelRegistrar getChannelRegistrar() {
return channelRegistrar;
}
@Override
public InetSocketAddress getBoundAddress() {
if (configuration == null) {
throw new IllegalStateException("No configuration"); // even though you'll never get the chance... heh, heh
}
return configuration.getBind();
@Override
public InetSocketAddress getBoundAddress() {
if (configuration == null) {
throw new IllegalStateException(
"No configuration"); // even though you'll never get the chance... heh, heh
}
return configuration.getBind();
}
}

Datei anzeigen

@ -9,90 +9,96 @@ import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import net.kyori.text.TextComponent;
import net.kyori.text.event.ClickEvent;
import net.kyori.text.event.HoverEvent;
import net.kyori.text.format.TextColor;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class ServerCommand implements Command {
private final ProxyServer server;
public ServerCommand(ProxyServer server) {
this.server = server;
private final ProxyServer server;
public ServerCommand(ProxyServer server) {
this.server = server;
}
@Override
public void execute(CommandSource source, String @NonNull [] args) {
if (!(source instanceof Player)) {
source.sendMessage(TextComponent.of("Only players may run this command.", TextColor.RED));
return;
}
@Override
public void execute(CommandSource source, String @NonNull [] args) {
if (!(source instanceof Player)) {
source.sendMessage(TextComponent.of("Only players may run this command.", TextColor.RED));
return;
}
Player player = (Player) source;
if (args.length == 1) {
// Trying to connect to a server.
String serverName = args[0];
Optional<RegisteredServer> toConnect = server.getServer(serverName);
if (!toConnect.isPresent()) {
player.sendMessage(
TextComponent.of("Server " + serverName + " doesn't exist.", TextColor.RED));
return;
}
Player player = (Player) source;
if (args.length == 1) {
// Trying to connect to a server.
String serverName = args[0];
Optional<RegisteredServer> toConnect = server.getServer(serverName);
if (!toConnect.isPresent()) {
player.sendMessage(TextComponent.of("Server " + serverName + " doesn't exist.", TextColor.RED));
return;
}
player.createConnectionRequest(toConnect.get()).fireAndForget();
} else {
String currentServer = player.getCurrentServer().map(ServerConnection::getServerInfo)
.map(ServerInfo::getName)
.orElse("<unknown>");
player.sendMessage(TextComponent
.of("You are currently connected to " + currentServer + ".", TextColor.YELLOW));
player.createConnectionRequest(toConnect.get()).fireAndForget();
// Assemble the list of servers as components
TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ")
.color(TextColor.YELLOW);
List<RegisteredServer> infos = ImmutableList.copyOf(server.getAllServers());
for (int i = 0; i < infos.size(); i++) {
RegisteredServer rs = infos.get(i);
TextComponent infoComponent = TextComponent.of(rs.getServerInfo().getName());
String playersText = rs.getPlayersConnected().size() + " player(s) online";
if (rs.getServerInfo().getName().equals(currentServer)) {
infoComponent = infoComponent.color(TextColor.GREEN)
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("Currently connected to this server\n" + playersText)));
} else {
String currentServer = player.getCurrentServer().map(ServerConnection::getServerInfo).map(ServerInfo::getName)
.orElse("<unknown>");
player.sendMessage(TextComponent.of("You are currently connected to " + currentServer + ".", TextColor.YELLOW));
// Assemble the list of servers as components
TextComponent.Builder serverListBuilder = TextComponent.builder("Available servers: ").color(TextColor.YELLOW);
List<RegisteredServer> infos = ImmutableList.copyOf(server.getAllServers());
for (int i = 0; i < infos.size(); i++) {
RegisteredServer rs = infos.get(i);
TextComponent infoComponent = TextComponent.of(rs.getServerInfo().getName());
String playersText = rs.getPlayersConnected().size() + " player(s) online";
if (rs.getServerInfo().getName().equals(currentServer)) {
infoComponent = infoComponent.color(TextColor.GREEN)
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("Currently connected to this server\n" + playersText)));
} else {
infoComponent = infoComponent.color(TextColor.GRAY)
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/server " + rs.getServerInfo().getName()))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.of("Click to connect to this server\n" + playersText)));
}
serverListBuilder.append(infoComponent);
if (i != infos.size() - 1) {
serverListBuilder.append(TextComponent.of(", ", TextColor.GRAY));
}
}
player.sendMessage(serverListBuilder.build());
infoComponent = infoComponent.color(TextColor.GRAY)
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND,
"/server " + rs.getServerInfo().getName()))
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
TextComponent.of("Click to connect to this server\n" + playersText)));
}
}
@Override
public List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
if (currentArgs.length == 0) {
return server.getAllServers().stream()
.map(rs -> rs.getServerInfo().getName())
.collect(Collectors.toList());
} else if (currentArgs.length == 1) {
return server.getAllServers().stream()
.map(rs -> rs.getServerInfo().getName())
.filter(name -> name.regionMatches(true, 0, currentArgs[0], 0, currentArgs[0].length()))
.collect(Collectors.toList());
} else {
return ImmutableList.of();
serverListBuilder.append(infoComponent);
if (i != infos.size() - 1) {
serverListBuilder.append(TextComponent.of(", ", TextColor.GRAY));
}
}
}
@Override
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source.getPermissionValue("velocity.command.server") != Tristate.FALSE;
player.sendMessage(serverListBuilder.build());
}
}
@Override
public List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
if (currentArgs.length == 0) {
return server.getAllServers().stream()
.map(rs -> rs.getServerInfo().getName())
.collect(Collectors.toList());
} else if (currentArgs.length == 1) {
return server.getAllServers().stream()
.map(rs -> rs.getServerInfo().getName())
.filter(name -> name.regionMatches(true, 0, currentArgs[0], 0, currentArgs[0].length()))
.collect(Collectors.toList());
} else {
return ImmutableList.of();
}
}
@Override
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source.getPermissionValue("velocity.command.server") != Tristate.FALSE;
}
}

Datei anzeigen

@ -8,23 +8,25 @@ import net.kyori.text.format.TextColor;
import org.checkerframework.checker.nullness.qual.NonNull;
public class ShutdownCommand implements Command {
private final VelocityServer server;
public ShutdownCommand(VelocityServer server) {
this.server = server;
}
private final VelocityServer server;
@Override
public void execute(CommandSource source, String @NonNull [] args) {
if (source != server.getConsoleCommandSource()) {
source.sendMessage(TextComponent.of("You are not allowed to use this command.", TextColor.RED));
return;
}
server.shutdown();
}
public ShutdownCommand(VelocityServer server) {
this.server = server;
}
@Override
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source == server.getConsoleCommandSource();
@Override
public void execute(CommandSource source, String @NonNull [] args) {
if (source != server.getConsoleCommandSource()) {
source
.sendMessage(TextComponent.of("You are not allowed to use this command.", TextColor.RED));
return;
}
server.shutdown();
}
@Override
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
return source == server.getConsoleCommandSource();
}
}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen