Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-06 00:00:47 +01:00
Ursprung
53aa92db92
Commit
25b5e00125
@ -2,9 +2,11 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
|
id 'checkstyle'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: '../gradle/checkerframework.gradle'
|
apply from: '../gradle/checkerframework.gradle'
|
||||||
|
apply from: '../gradle/checkstyle.gradle'
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
ap {
|
ap {
|
||||||
|
@ -3,7 +3,11 @@ package com.velocitypowered.api.plugin.ap;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.velocitypowered.api.plugin.Plugin;
|
import com.velocitypowered.api.plugin.Plugin;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
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.AbstractProcessor;
|
||||||
import javax.annotation.processing.ProcessingEnvironment;
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
import javax.annotation.processing.RoundEnvironment;
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
@ -16,73 +20,76 @@ import javax.lang.model.element.TypeElement;
|
|||||||
import javax.tools.Diagnostic;
|
import javax.tools.Diagnostic;
|
||||||
import javax.tools.FileObject;
|
import javax.tools.FileObject;
|
||||||
import javax.tools.StandardLocation;
|
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"})
|
@SupportedAnnotationTypes({"com.velocitypowered.api.plugin.Plugin"})
|
||||||
public class PluginAnnotationProcessor extends AbstractProcessor {
|
public class PluginAnnotationProcessor extends AbstractProcessor {
|
||||||
private ProcessingEnvironment environment;
|
|
||||||
private String pluginClassFound;
|
|
||||||
private boolean warnedAboutMultiplePlugins;
|
|
||||||
|
|
||||||
@Override
|
private ProcessingEnvironment environment;
|
||||||
public synchronized void init(ProcessingEnvironment processingEnv) {
|
private String pluginClassFound;
|
||||||
this.environment = processingEnv;
|
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
|
for (Element element : roundEnv.getElementsAnnotatedWith(Plugin.class)) {
|
||||||
public SourceVersion getSupportedSourceVersion() {
|
if (element.getKind() != ElementKind.CLASS) {
|
||||||
return SourceVersion.latestSupported();
|
environment.getMessager()
|
||||||
}
|
.printMessage(Diagnostic.Kind.ERROR, "Only classes can be annotated with "
|
||||||
|
+ Plugin.class.getCanonicalName());
|
||||||
@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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,149 +4,162 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.plugin.Plugin;
|
import com.velocitypowered.api.plugin.Plugin;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public class SerializedPluginDescription {
|
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,
|
// @Nullable is used here to make GSON skip these in the serialized file
|
||||||
List<String> authors, List<Dependency> dependencies, String main) {
|
private final String id;
|
||||||
this.id = Preconditions.checkNotNull(id, "id");
|
private final @Nullable String name;
|
||||||
this.name = Strings.emptyToNull(name);
|
private final @Nullable String version;
|
||||||
this.version = Strings.emptyToNull(version);
|
private final @Nullable String description;
|
||||||
this.description = Strings.emptyToNull(description);
|
private final @Nullable String url;
|
||||||
this.url = Strings.emptyToNull(url);
|
private final @Nullable List<String> authors;
|
||||||
this.authors = authors == null || authors.isEmpty() ? ImmutableList.of() : authors;
|
private final @Nullable List<Dependency> dependencies;
|
||||||
this.dependencies = dependencies == null || dependencies.isEmpty() ? ImmutableList.of() : dependencies;
|
private final String main;
|
||||||
this.main = Preconditions.checkNotNull(main, "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) {
|
public String getId() {
|
||||||
List<Dependency> dependencies = new ArrayList<>();
|
return id;
|
||||||
for (com.velocitypowered.api.plugin.Dependency dependency : plugin.dependencies()) {
|
}
|
||||||
dependencies.add(new Dependency(dependency.id(), dependency.optional()));
|
|
||||||
}
|
public @Nullable String getName() {
|
||||||
return new SerializedPluginDescription(plugin.id(), plugin.name(), plugin.version(), plugin.description(), plugin.url(),
|
return name;
|
||||||
Arrays.stream(plugin.authors()).filter(author -> !author.isEmpty()).collect(Collectors.toList()), dependencies, qualifiedName);
|
}
|
||||||
|
|
||||||
|
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() {
|
public String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getName() {
|
public boolean isOptional() {
|
||||||
return name;
|
return optional;
|
||||||
}
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
SerializedPluginDescription that = (SerializedPluginDescription) o;
|
}
|
||||||
return Objects.equals(id, that.id) &&
|
if (o == null || getClass() != o.getClass()) {
|
||||||
Objects.equals(name, that.name) &&
|
return false;
|
||||||
Objects.equals(version, that.version) &&
|
}
|
||||||
Objects.equals(description, that.description) &&
|
Dependency that = (Dependency) o;
|
||||||
Objects.equals(url, that.url) &&
|
return optional == that.optional &&
|
||||||
Objects.equals(authors, that.authors) &&
|
Objects.equals(id, that.id);
|
||||||
Objects.equals(dependencies, that.dependencies) &&
|
|
||||||
Objects.equals(main, that.main);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, name, version, description, url, authors, dependencies);
|
return Objects.hash(id, optional);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "SerializedPluginDescription{" +
|
return "Dependency{" +
|
||||||
"id='" + id + '\'' +
|
"id='" + id + '\'' +
|
||||||
", name='" + name + '\'' +
|
", optional=" + optional +
|
||||||
", 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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,46 @@
|
|||||||
package com.velocitypowered.api.command;
|
package com.velocitypowered.api.command;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
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}
|
* Represents a command that can be executed by a {@link CommandSource}, such as a {@link
|
||||||
* or the console.
|
* com.velocitypowered.api.proxy.Player} or the console.
|
||||||
*/
|
*/
|
||||||
public interface Command {
|
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}.
|
* Executes the command for the specified {@link CommandSource}.
|
||||||
* @param source the source to run the command for
|
*
|
||||||
* @param currentArgs the current, partial arguments for this command
|
* @param source the source of this command
|
||||||
* @return tab complete suggestions
|
* @param args the arguments for this command
|
||||||
*/
|
*/
|
||||||
default List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
|
void execute(CommandSource source, String @NonNull [] args);
|
||||||
return ImmutableList.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests to check if the {@code source} has permission to use this command
|
* Provides tab complete suggestions for a command for a specified {@link CommandSource}.
|
||||||
* with the provided {@code args}.
|
*
|
||||||
*
|
* @param source the source to run the command for
|
||||||
* <p>If this method returns false, the handling will be forwarded onto
|
* @param currentArgs the current, partial arguments for this command
|
||||||
* the players current server.</p>
|
* @return tab complete suggestions
|
||||||
*
|
*/
|
||||||
* @param source the source of the command
|
default List<String> suggest(CommandSource source, String @NonNull [] currentArgs) {
|
||||||
* @param args the arguments for this command
|
return ImmutableList.of();
|
||||||
* @return whether the source has permission
|
}
|
||||||
*/
|
|
||||||
default boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
/**
|
||||||
return true;
|
* 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,28 @@ package com.velocitypowered.api.command;
|
|||||||
* Represents an interface to register a command executor with the proxy.
|
* Represents an interface to register a command executor with the proxy.
|
||||||
*/
|
*/
|
||||||
public interface CommandManager {
|
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.
|
* Registers the specified command with the manager with the specified aliases.
|
||||||
* @param alias the command alias to unregister
|
*
|
||||||
*/
|
* @param command the command to register
|
||||||
void unregister(String alias);
|
* @param aliases the alias to use
|
||||||
|
*/
|
||||||
|
void register(Command command, String... aliases);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to execute a command from the specified {@code cmdLine}.
|
* Unregisters a command.
|
||||||
* @param source the command's source
|
*
|
||||||
* @param cmdLine the command to run
|
* @param alias the command alias to unregister
|
||||||
* @return true if the command was found and executed, false if it was not
|
*/
|
||||||
*/
|
void unregister(String alias);
|
||||||
boolean execute(CommandSource source, String cmdLine);
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,11 @@ import net.kyori.text.Component;
|
|||||||
* Represents something that can be used to run a {@link Command}.
|
* Represents something that can be used to run a {@link Command}.
|
||||||
*/
|
*/
|
||||||
public interface CommandSource extends PermissionSubject {
|
public interface CommandSource extends PermissionSubject {
|
||||||
/**
|
|
||||||
* Sends the specified {@code component} to the invoker.
|
/**
|
||||||
* @param component the text component to send
|
* Sends the specified {@code component} to the invoker.
|
||||||
*/
|
*
|
||||||
void sendMessage(Component component);
|
* @param component the text component to send
|
||||||
|
*/
|
||||||
|
void sendMessage(Component component);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package com.velocitypowered.api.event;
|
package com.velocitypowered.api.event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an interface to perform direct dispatch of an event. This makes integration easier to achieve with platforms
|
* Represents an interface to perform direct dispatch of an event. This makes integration easier to
|
||||||
* such as RxJava.
|
* achieve with platforms such as RxJava.
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface EventHandler<E> {
|
public interface EventHandler<E> {
|
||||||
void execute(E event);
|
|
||||||
|
void execute(E event);
|
||||||
}
|
}
|
||||||
|
@ -6,68 +6,82 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
* Allows plugins to register and deregister listeners for event handlers.
|
* Allows plugins to register and deregister listeners for event handlers.
|
||||||
*/
|
*/
|
||||||
public interface EventManager {
|
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}.
|
* Requests that the specified {@code listener} listen for events and associate it with the {@code
|
||||||
* @param plugin the plugin to associate with the handler
|
* plugin}.
|
||||||
* @param eventClass the class for the event handler to register
|
*
|
||||||
* @param handler the handler to register
|
* @param plugin the plugin to associate with the listener
|
||||||
* @param <E> the event type to handle
|
* @param listener the listener to register
|
||||||
*/
|
*/
|
||||||
default <E> void register(Object plugin, Class<E> eventClass, EventHandler<E> handler) {
|
void register(Object plugin, Object listener);
|
||||||
register(plugin, eventClass, PostOrder.NORMAL, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that the specified {@code handler} listen for events and associate it with the {@code plugin}.
|
* Requests that the specified {@code handler} listen for events and associate it with the {@code
|
||||||
* @param plugin the plugin to associate with the handler
|
* plugin}.
|
||||||
* @param eventClass the class for the event handler to register
|
*
|
||||||
* @param postOrder the order in which events should be posted to the handler
|
* @param plugin the plugin to associate with the handler
|
||||||
* @param handler the handler to register
|
* @param eventClass the class for the event handler to register
|
||||||
* @param <E> the event type to handle
|
* @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);
|
*/
|
||||||
|
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
|
* Requests that the specified {@code handler} listen for events and associate it with the {@code
|
||||||
* while a plugin handles a potentially long-running operation such as a database query.
|
* plugin}.
|
||||||
* @param event the event to fire
|
*
|
||||||
* @return a {@link CompletableFuture} representing the posted event
|
* @param plugin the plugin to associate with the handler
|
||||||
*/
|
* @param eventClass the class for the event handler to register
|
||||||
<E> CompletableFuture<E> fire(E event);
|
* @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.
|
* Fires the specified event to the event bus asynchronously. This allows Velocity to continue
|
||||||
* @param event the event to fire
|
* servicing connections while a plugin handles a potentially long-running operation such as a
|
||||||
*/
|
* database query.
|
||||||
default void fireAndForget(Object event) {
|
*
|
||||||
fire(event);
|
* @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}.
|
* Posts the specified event to the event bus and discards the result.
|
||||||
* @param plugin the plugin to deregister listeners for
|
*
|
||||||
*/
|
* @param event the event to fire
|
||||||
void unregisterListeners(Object plugin);
|
*/
|
||||||
|
default void fireAndForget(Object event) {
|
||||||
|
fire(event);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters a specific listener for a specific plugin.
|
* Unregisters all listeners for the specified {@code plugin}.
|
||||||
* @param plugin the plugin associated with the listener
|
*
|
||||||
* @param listener the listener to deregister
|
* @param plugin the plugin to deregister listeners for
|
||||||
*/
|
*/
|
||||||
void unregisterListener(Object plugin, Object listener);
|
void unregisterListeners(Object plugin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters a specific event handler for a specific plugin.
|
* Unregisters a specific listener for a specific plugin.
|
||||||
* @param plugin the plugin to associate with the handler
|
*
|
||||||
* @param handler the handler to register
|
* @param plugin the plugin associated with the listener
|
||||||
* @param <E> the event type to handle
|
* @param listener the listener to deregister
|
||||||
*/
|
*/
|
||||||
<E> void unregister(Object plugin, EventHandler<E> handler);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package com.velocitypowered.api.event;
|
package com.velocitypowered.api.event;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the order an event will be posted to a listener method, relative
|
* Represents the order an event will be posted to a listener method, relative to other listeners.
|
||||||
* to other listeners.
|
|
||||||
*/
|
*/
|
||||||
public enum PostOrder {
|
public enum PostOrder {
|
||||||
|
|
||||||
FIRST, EARLY, NORMAL, LATE, LAST;
|
FIRST, EARLY, NORMAL, LATE, LAST;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,114 +1,119 @@
|
|||||||
package com.velocitypowered.api.event;
|
package com.velocitypowered.api.event;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import java.util.Optional;
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
import net.kyori.text.serializer.ComponentSerializers;
|
import net.kyori.text.serializer.ComponentSerializers;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates an event that has a result attached to it.
|
* Indicates an event that has a result attached to it.
|
||||||
*/
|
*/
|
||||||
public interface ResultedEvent<R extends ResultedEvent.Result> {
|
public interface ResultedEvent<R extends ResultedEvent.Result> {
|
||||||
/**
|
|
||||||
* Returns the result associated with this event.
|
/**
|
||||||
* @return the result of this event
|
* Returns the result associated with this event.
|
||||||
*/
|
*
|
||||||
R getResult();
|
* @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.
|
* Returns whether or not the event is allowed to proceed. Plugins may choose to skip denied
|
||||||
* @param result the new result
|
* 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.
|
* A generic "allowed/denied" result.
|
||||||
*/
|
*/
|
||||||
interface Result {
|
final class GenericResult implements Result {
|
||||||
/**
|
|
||||||
* Returns whether or not the event is allowed to proceed. Plugins may choose to skip denied events, and the
|
private static final GenericResult ALLOWED = new GenericResult(true);
|
||||||
* proxy will respect the result of this method.
|
private static final GenericResult DENIED = new GenericResult(false);
|
||||||
* @return whether or not the event is allowed to proceed
|
|
||||||
*/
|
private final boolean status;
|
||||||
boolean isAllowed();
|
|
||||||
|
private GenericResult(boolean b) {
|
||||||
|
this.status = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* A generic "allowed/denied" result.
|
public boolean isAllowed() {
|
||||||
*/
|
return status;
|
||||||
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
|
||||||
* Represents an "allowed/denied" result with a reason allowed for denial.
|
public String toString() {
|
||||||
*/
|
return status ? "allowed" : "denied";
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,11 @@ import java.lang.annotation.Target;
|
|||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface Subscribe {
|
public @interface Subscribe {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The order events will be posted to this listener.
|
* The order events will be posted to this listener.
|
||||||
*
|
*
|
||||||
* @return the order
|
* @return the order
|
||||||
*/
|
*/
|
||||||
PostOrder order() default PostOrder.NORMAL;
|
PostOrder order() default PostOrder.NORMAL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,20 +7,21 @@ import com.velocitypowered.api.proxy.InboundConnection;
|
|||||||
* This event is fired when a handshake is established between a client and Velocity.
|
* This event is fired when a handshake is established between a client and Velocity.
|
||||||
*/
|
*/
|
||||||
public final class ConnectionHandshakeEvent {
|
public final class ConnectionHandshakeEvent {
|
||||||
private final InboundConnection connection;
|
|
||||||
|
|
||||||
public ConnectionHandshakeEvent(InboundConnection connection) {
|
private final InboundConnection connection;
|
||||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
|
||||||
}
|
|
||||||
|
|
||||||
public InboundConnection getConnection() {
|
public ConnectionHandshakeEvent(InboundConnection connection) {
|
||||||
return connection;
|
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public InboundConnection getConnection() {
|
||||||
public String toString() {
|
return connection;
|
||||||
return "ConnectionHandshakeEvent{" +
|
}
|
||||||
"connection=" + connection +
|
|
||||||
'}';
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
|
return "ConnectionHandshakeEvent{" +
|
||||||
|
"connection=" + connection +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,24 +4,25 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.velocitypowered.api.proxy.Player;
|
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
|
* This event is fired when a player disconnects from the proxy. Operations on the provided player,
|
||||||
* data retrieval operations, may behave in undefined ways.
|
* aside from basic data retrieval operations, may behave in undefined ways.
|
||||||
*/
|
*/
|
||||||
public final class DisconnectEvent {
|
public final class DisconnectEvent {
|
||||||
private final Player player;
|
|
||||||
|
|
||||||
public DisconnectEvent(Player player) {
|
private final Player player;
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
public DisconnectEvent(Player player) {
|
||||||
return player;
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Player getPlayer() {
|
||||||
public String toString() {
|
return player;
|
||||||
return "DisconnectEvent{" +
|
}
|
||||||
"player=" + player +
|
|
||||||
'}';
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
|
return "DisconnectEvent{" +
|
||||||
|
"player=" + player +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,36 +5,38 @@ import com.velocitypowered.api.event.ResultedEvent;
|
|||||||
import com.velocitypowered.api.proxy.Player;
|
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> {
|
public final class LoginEvent implements ResultedEvent<ResultedEvent.ComponentResult> {
|
||||||
private final Player player;
|
|
||||||
private ComponentResult result;
|
|
||||||
|
|
||||||
public LoginEvent(Player player) {
|
private final Player player;
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
private ComponentResult result;
|
||||||
this.result = ComponentResult.allowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
public LoginEvent(Player player) {
|
||||||
return player;
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
this.result = ComponentResult.allowed();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public Player getPlayer() {
|
||||||
public ComponentResult getResult() {
|
return player;
|
||||||
return result;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setResult(ComponentResult result) {
|
public ComponentResult getResult() {
|
||||||
this.result = Preconditions.checkNotNull(result, "result");
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public void setResult(ComponentResult result) {
|
||||||
return "LoginEvent{" +
|
this.result = Preconditions.checkNotNull(result, "result");
|
||||||
"player=" + player +
|
}
|
||||||
", result=" + result +
|
|
||||||
'}';
|
@Override
|
||||||
}
|
public String toString() {
|
||||||
|
return "LoginEvent{" +
|
||||||
|
"player=" + player +
|
||||||
|
", result=" + result +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,99 +7,100 @@ import com.velocitypowered.api.event.ResultedEvent;
|
|||||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
|
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
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})
|
* This event is fired when a plugin message is sent to the proxy, either from a client ({@link
|
||||||
* or a server ({@link com.velocitypowered.api.proxy.ServerConnection}).
|
* com.velocitypowered.api.proxy.Player}) or a server ({@link com.velocitypowered.api.proxy.ServerConnection}).
|
||||||
*/
|
*/
|
||||||
public final class PluginMessageEvent implements ResultedEvent<PluginMessageEvent.ForwardResult> {
|
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) {
|
private final ChannelMessageSource source;
|
||||||
this.source = Preconditions.checkNotNull(source, "source");
|
private final ChannelMessageSink target;
|
||||||
this.target = Preconditions.checkNotNull(target, "target");
|
private final ChannelIdentifier identifier;
|
||||||
this.identifier = Preconditions.checkNotNull(identifier, "identifier");
|
private final byte[] data;
|
||||||
this.data = Preconditions.checkNotNull(data, "data");
|
private ForwardResult result;
|
||||||
this.result = ForwardResult.forward();
|
|
||||||
|
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
|
@Override
|
||||||
public ForwardResult getResult() {
|
public boolean isAllowed() {
|
||||||
return result;
|
return status;
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PluginMessageEvent{" +
|
return status ? "forward to sink" : "handled message at proxy";
|
||||||
"source=" + source +
|
|
||||||
", target=" + target +
|
|
||||||
", identifier=" + identifier +
|
|
||||||
", data=" + Arrays.toString(data) +
|
|
||||||
", result=" + result +
|
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static ForwardResult forward() {
|
||||||
* A result determining whether or not to forward this message on.
|
return ALLOWED;
|
||||||
*/
|
|
||||||
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 handled() {
|
||||||
|
return DENIED;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,25 +4,25 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is fired once the player has been successfully authenticated and
|
* This event is fired once the player has been successfully authenticated and fully initialized and
|
||||||
* fully initialized and player will be connected to server after this event
|
* player will be connected to server after this event
|
||||||
*/
|
*/
|
||||||
public final class PostLoginEvent {
|
public final class PostLoginEvent {
|
||||||
|
|
||||||
private final Player player;
|
private final Player player;
|
||||||
|
|
||||||
public PostLoginEvent(Player player) {
|
public PostLoginEvent(Player player) {
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Player getPlayer() {
|
public Player getPlayer() {
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PostLoginEvent{"
|
return "PostLoginEvent{"
|
||||||
+ "player=" + player
|
+ "player=" + player
|
||||||
+ '}';
|
+ '}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,147 +3,153 @@ package com.velocitypowered.api.event.connection;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.event.ResultedEvent;
|
import com.velocitypowered.api.event.ResultedEvent;
|
||||||
import com.velocitypowered.api.proxy.InboundConnection;
|
import com.velocitypowered.api.proxy.InboundConnection;
|
||||||
|
import java.util.Optional;
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is fired when a player has initiated a connection with the proxy but before the proxy authenticates the
|
* This event is fired when a player has initiated a connection with the proxy but before the proxy
|
||||||
* player with Mojang or before the player's proxy connection is fully established (for offline mode).
|
* 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> {
|
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) {
|
private final InboundConnection connection;
|
||||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
private final String username;
|
||||||
this.username = Preconditions.checkNotNull(username, "username");
|
private PreLoginComponentResult result;
|
||||||
this.result = PreLoginComponentResult.allowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InboundConnection getConnection() {
|
public PreLoginEvent(InboundConnection connection, String username) {
|
||||||
return connection;
|
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||||
}
|
this.username = Preconditions.checkNotNull(username, "username");
|
||||||
|
this.result = PreLoginComponentResult.allowed();
|
||||||
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public InboundConnection getConnection() {
|
||||||
return username;
|
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
|
@Override
|
||||||
public PreLoginComponentResult getResult() {
|
public boolean isAllowed() {
|
||||||
return result;
|
return result != Result.DISALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Optional<Component> getReason() {
|
||||||
public void setResult(@NonNull PreLoginComponentResult result) {
|
return Optional.ofNullable(reason);
|
||||||
this.result = Preconditions.checkNotNull(result, "result");
|
}
|
||||||
|
|
||||||
|
public boolean isOnlineModeAllowed() {
|
||||||
|
return result == Result.FORCE_ONLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isForceOfflineMode() {
|
||||||
|
return result == Result.FORCE_OFFLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PreLoginEvent{" +
|
switch (result) {
|
||||||
"connection=" + connection +
|
case ALLOWED:
|
||||||
", username='" + username + '\'' +
|
return "allowed";
|
||||||
", result=" + result +
|
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 {
|
public static PreLoginComponentResult allowed() {
|
||||||
|
return ALLOWED;
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,52 +12,52 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
* <p>This event is only called once per subject, on initialisation.</p>
|
* <p>This event is only called once per subject, on initialisation.</p>
|
||||||
*/
|
*/
|
||||||
public final class PermissionsSetupEvent {
|
public final class PermissionsSetupEvent {
|
||||||
private final PermissionSubject subject;
|
|
||||||
private final PermissionProvider defaultProvider;
|
|
||||||
private PermissionProvider provider;
|
|
||||||
|
|
||||||
public PermissionsSetupEvent(PermissionSubject subject, PermissionProvider provider) {
|
private final PermissionSubject subject;
|
||||||
this.subject = Preconditions.checkNotNull(subject, "subject");
|
private final PermissionProvider defaultProvider;
|
||||||
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
|
private PermissionProvider provider;
|
||||||
}
|
|
||||||
|
|
||||||
public PermissionSubject getSubject() {
|
public PermissionsSetupEvent(PermissionSubject subject, PermissionProvider provider) {
|
||||||
return this.subject;
|
this.subject = Preconditions.checkNotNull(subject, "subject");
|
||||||
}
|
this.provider = this.defaultProvider = Preconditions.checkNotNull(provider, "provider");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public PermissionSubject getSubject() {
|
||||||
* Uses the provider function to obtain a {@link PermissionFunction} for
|
return this.subject;
|
||||||
* the subject.
|
}
|
||||||
*
|
|
||||||
* @param subject the subject
|
|
||||||
* @return the obtained permission function
|
|
||||||
*/
|
|
||||||
public PermissionFunction createFunction(PermissionSubject subject) {
|
|
||||||
return this.provider.createFunction(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);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
public PermissionProvider getProvider() {
|
||||||
* Sets the {@link PermissionFunction} that should be used for the subject.
|
return this.provider;
|
||||||
*
|
}
|
||||||
* <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() {
|
* Sets the {@link PermissionFunction} that should be used for the subject.
|
||||||
return "PermissionsSetupEvent{" +
|
*
|
||||||
"subject=" + subject +
|
* <p>Specifying <code>null</code> will reset the provider to the default
|
||||||
", defaultProvider=" + defaultProvider +
|
* instance given when the event was posted.</p>
|
||||||
", provider=" + provider +
|
*
|
||||||
'}';
|
* @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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,65 +6,73 @@ import com.velocitypowered.api.util.GameProfile;
|
|||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
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
|
* This event is fired after the {@link com.velocitypowered.api.event.connection.PreLoginEvent} in
|
||||||
* game profile for the user. This can be used to configure a custom profile for a user, i.e. skin replacement.
|
* 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 {
|
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) {
|
private final String username;
|
||||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
private final InboundConnection connection;
|
||||||
this.originalProfile = Preconditions.checkNotNull(originalProfile, "originalProfile");
|
private final GameProfile originalProfile;
|
||||||
this.username = originalProfile.getName();
|
private final boolean onlineMode;
|
||||||
this.onlineMode = onlineMode;
|
private @Nullable GameProfile gameProfile;
|
||||||
}
|
|
||||||
|
|
||||||
public InboundConnection getConnection() {
|
public GameProfileRequestEvent(InboundConnection connection, GameProfile originalProfile,
|
||||||
return connection;
|
boolean onlineMode) {
|
||||||
}
|
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||||
|
this.originalProfile = Preconditions.checkNotNull(originalProfile, "originalProfile");
|
||||||
|
this.username = originalProfile.getName();
|
||||||
|
this.onlineMode = onlineMode;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public InboundConnection getConnection() {
|
||||||
return username;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameProfile getOriginalProfile() {
|
|
||||||
return originalProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOnlineMode() {
|
|
||||||
return onlineMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
public String getUsername() {
|
||||||
* Returns the game profile that will be used to initialize the connection with. Should no profile be currently
|
return username;
|
||||||
* 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 getOriginalProfile() {
|
||||||
*/
|
return originalProfile;
|
||||||
public GameProfile getGameProfile() {
|
}
|
||||||
return gameProfile == null ? originalProfile : gameProfile;
|
|
||||||
}
|
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 +
|
|
||||||
"}";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,111 +8,120 @@ import net.kyori.text.Component;
|
|||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
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
|
* Fired when a player is kicked from a server. You may either allow Velocity to kick the player
|
||||||
* override) or redirect the player to a separate server.
|
* (with an optional reason override) or redirect the player to a separate server.
|
||||||
*/
|
*/
|
||||||
public final class KickedFromServerEvent implements ResultedEvent<KickedFromServerEvent.ServerKickResult> {
|
public final class KickedFromServerEvent implements
|
||||||
private final Player player;
|
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 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) {
|
private RedirectPlayer(RegisteredServer server) {
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
this.server = Preconditions.checkNotNull(server, "server");
|
||||||
this.server = Preconditions.checkNotNull(server, "server");
|
|
||||||
this.originalReason = Preconditions.checkNotNull(originalReason, "originalReason");
|
|
||||||
this.duringLogin = duringLogin;
|
|
||||||
this.result = new DisconnectPlayer(fancyReason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ServerKickResult getResult() {
|
public boolean isAllowed() {
|
||||||
return result;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setResult(@NonNull ServerKickResult result) {
|
|
||||||
this.result = Preconditions.checkNotNull(result, "result");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
|
||||||
return player;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RegisteredServer getServer() {
|
public RegisteredServer getServer() {
|
||||||
return server;
|
return server;
|
||||||
}
|
|
||||||
|
|
||||||
public Component getOriginalReason() {
|
|
||||||
return originalReason;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean kickedDuringLogin() {
|
|
||||||
return duringLogin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {}
|
public static RedirectPlayer create(RegisteredServer server) {
|
||||||
|
return new RedirectPlayer(server);
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,95 +3,96 @@ package com.velocitypowered.api.event.player;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.event.ResultedEvent;
|
import com.velocitypowered.api.event.ResultedEvent;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
|
import java.util.Optional;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is fired when a player types in a chat message.
|
* This event is fired when a player types in a chat message.
|
||||||
*/
|
*/
|
||||||
public final class PlayerChatEvent implements ResultedEvent<PlayerChatEvent.ChatResult> {
|
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) {
|
private final Player player;
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
private final String message;
|
||||||
this.message = Preconditions.checkNotNull(message, "message");
|
private ChatResult result;
|
||||||
this.result = ChatResult.allowed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
public PlayerChatEvent(Player player, String message) {
|
||||||
return player;
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
this.message = Preconditions.checkNotNull(message, "message");
|
||||||
|
this.result = ChatResult.allowed();
|
||||||
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public Player getPlayer() {
|
||||||
return message;
|
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
|
@Override
|
||||||
public ChatResult getResult() {
|
public boolean isAllowed() {
|
||||||
return result;
|
return status;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setResult(ChatResult result) {
|
|
||||||
this.result = Preconditions.checkNotNull(result, "result");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PlayerChatEvent{" +
|
return status ? "allowed" : "denied";
|
||||||
"player=" + player +
|
|
||||||
", message=" + message +
|
|
||||||
", result=" + result +
|
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static ChatResult allowed() {
|
||||||
* Represents the result of the {@link PlayerChatEvent}.
|
return ALLOWED;
|
||||||
*/
|
|
||||||
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,19 +8,20 @@ import com.velocitypowered.api.util.ModInfo;
|
|||||||
* This event is fired when the players ModInfo is changed.
|
* This event is fired when the players ModInfo is changed.
|
||||||
*/
|
*/
|
||||||
public final class PlayerModInfoEvent {
|
public final class PlayerModInfoEvent {
|
||||||
private final Player player;
|
|
||||||
private final ModInfo modInfo;
|
private final Player player;
|
||||||
|
private final ModInfo modInfo;
|
||||||
public PlayerModInfoEvent(Player player, ModInfo modInfo) {
|
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
public PlayerModInfoEvent(Player player, ModInfo modInfo) {
|
||||||
this.modInfo = Preconditions.checkNotNull(modInfo, "modInfo");
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
this.modInfo = Preconditions.checkNotNull(modInfo, "modInfo");
|
||||||
|
}
|
||||||
public Player getPlayer() {
|
|
||||||
return player;
|
public Player getPlayer() {
|
||||||
}
|
return player;
|
||||||
|
}
|
||||||
public ModInfo getModInfo() {
|
|
||||||
return modInfo;
|
public ModInfo getModInfo() {
|
||||||
}
|
return modInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,20 @@ import com.velocitypowered.api.proxy.Player;
|
|||||||
import com.velocitypowered.api.proxy.player.PlayerSettings;
|
import com.velocitypowered.api.proxy.player.PlayerSettings;
|
||||||
|
|
||||||
public final class PlayerSettingsChangedEvent {
|
public final class PlayerSettingsChangedEvent {
|
||||||
private final Player player;
|
|
||||||
private final PlayerSettings playerSettings;
|
|
||||||
|
|
||||||
public PlayerSettingsChangedEvent(Player player, PlayerSettings playerSettings) {
|
private final Player player;
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
private final PlayerSettings playerSettings;
|
||||||
this.playerSettings = Preconditions.checkNotNull(playerSettings, "playerSettings");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
public PlayerSettingsChangedEvent(Player player, PlayerSettings playerSettings) {
|
||||||
return player;
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
this.playerSettings = Preconditions.checkNotNull(playerSettings, "playerSettings");
|
||||||
|
}
|
||||||
|
|
||||||
public PlayerSettings getPlayerSettings() {
|
public Player getPlayer() {
|
||||||
return playerSettings;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlayerSettings getPlayerSettings() {
|
||||||
|
return playerSettings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,31 +5,32 @@ import com.velocitypowered.api.proxy.Player;
|
|||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
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
|
* This event is fired once the player has successfully connected to the target server and the
|
||||||
* server has been de-established.
|
* connection to the previous server has been de-established.
|
||||||
*/
|
*/
|
||||||
public final class ServerConnectedEvent {
|
public final class ServerConnectedEvent {
|
||||||
private final Player player;
|
|
||||||
private final RegisteredServer server;
|
|
||||||
|
|
||||||
public ServerConnectedEvent(Player player, RegisteredServer server) {
|
private final Player player;
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
private final RegisteredServer server;
|
||||||
this.server = Preconditions.checkNotNull(server, "server");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
public ServerConnectedEvent(Player player, RegisteredServer server) {
|
||||||
return player;
|
this.player = Preconditions.checkNotNull(player, "player");
|
||||||
}
|
this.server = Preconditions.checkNotNull(server, "server");
|
||||||
|
}
|
||||||
|
|
||||||
public RegisteredServer getServer() {
|
public Player getPlayer() {
|
||||||
return server;
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public RegisteredServer getServer() {
|
||||||
public String toString() {
|
return server;
|
||||||
return "ServerConnectedEvent{" +
|
}
|
||||||
"player=" + player +
|
|
||||||
", server=" + server +
|
@Override
|
||||||
'}';
|
public String toString() {
|
||||||
}
|
return "ServerConnectedEvent{" +
|
||||||
|
"player=" + player +
|
||||||
|
", server=" + server +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,87 +4,89 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.velocitypowered.api.event.ResultedEvent;
|
import com.velocitypowered.api.event.ResultedEvent;
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is fired before the player connects to a server.
|
* This event is fired before the player connects to a server.
|
||||||
*/
|
*/
|
||||||
public final class ServerPreConnectEvent implements ResultedEvent<ServerPreConnectEvent.ServerResult> {
|
public final class ServerPreConnectEvent implements
|
||||||
private final Player player;
|
ResultedEvent<ServerPreConnectEvent.ServerResult> {
|
||||||
private final RegisteredServer originalServer;
|
|
||||||
private ServerResult result;
|
|
||||||
|
|
||||||
public ServerPreConnectEvent(Player player, RegisteredServer originalServer) {
|
private final Player player;
|
||||||
this.player = Preconditions.checkNotNull(player, "player");
|
private final RegisteredServer originalServer;
|
||||||
this.originalServer = Preconditions.checkNotNull(originalServer, "originalServer");
|
private ServerResult result;
|
||||||
this.result = ServerResult.allowed(originalServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Player getPlayer() {
|
public ServerPreConnectEvent(Player player, RegisteredServer originalServer) {
|
||||||
return player;
|
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
|
@Override
|
||||||
public ServerResult getResult() {
|
public boolean isAllowed() {
|
||||||
return result;
|
return server != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Optional<RegisteredServer> getServer() {
|
||||||
public void setResult(ServerResult result) {
|
return Optional.ofNullable(server);
|
||||||
this.result = Preconditions.checkNotNull(result, "result");
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegisteredServer getOriginalServer() {
|
|
||||||
return originalServer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ServerPreConnectEvent{" +
|
if (server != null) {
|
||||||
"player=" + player +
|
return "allowed: connect to " + server.getServerInfo().getName();
|
||||||
", originalServer=" + originalServer +
|
}
|
||||||
", result=" + result +
|
return "denied";
|
||||||
'}';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static ServerResult denied() {
|
||||||
* Represents the result of the {@link ServerPreConnectEvent}.
|
return DENIED;
|
||||||
*/
|
|
||||||
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 allowed(RegisteredServer server) {
|
||||||
|
Preconditions.checkNotNull(server, "server");
|
||||||
|
return new ServerResult(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package com.velocitypowered.api.event.proxy;
|
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 {
|
public final class ProxyInitializeEvent {
|
||||||
@Override
|
|
||||||
public String toString() {
|
@Override
|
||||||
return "ProxyInitializeEvent";
|
public String toString() {
|
||||||
}
|
return "ProxyInitializeEvent";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
* This event is fired when a server list ping request is sent by a remote client.
|
||||||
*/
|
*/
|
||||||
public final class ProxyPingEvent {
|
public final class ProxyPingEvent {
|
||||||
private final InboundConnection connection;
|
|
||||||
private ServerPing ping;
|
|
||||||
|
|
||||||
public ProxyPingEvent(InboundConnection connection, ServerPing ping) {
|
private final InboundConnection connection;
|
||||||
this.connection = Preconditions.checkNotNull(connection, "connection");
|
private ServerPing ping;
|
||||||
this.ping = Preconditions.checkNotNull(ping, "ping");
|
|
||||||
}
|
|
||||||
|
|
||||||
public InboundConnection getConnection() {
|
public ProxyPingEvent(InboundConnection connection, ServerPing ping) {
|
||||||
return connection;
|
this.connection = Preconditions.checkNotNull(connection, "connection");
|
||||||
}
|
this.ping = Preconditions.checkNotNull(ping, "ping");
|
||||||
|
}
|
||||||
|
|
||||||
public ServerPing getPing() {
|
public InboundConnection getConnection() {
|
||||||
return ping;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPing(ServerPing ping) {
|
public ServerPing getPing() {
|
||||||
this.ping = Preconditions.checkNotNull(ping, "ping");
|
return ping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setPing(ServerPing ping) {
|
||||||
public String toString() {
|
this.ping = Preconditions.checkNotNull(ping, "ping");
|
||||||
return "ProxyPingEvent{" +
|
}
|
||||||
"connection=" + connection +
|
|
||||||
", ping=" + ping +
|
@Override
|
||||||
'}';
|
public String toString() {
|
||||||
}
|
return "ProxyPingEvent{" +
|
||||||
|
"connection=" + connection +
|
||||||
|
", ping=" + ping +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package com.velocitypowered.api.event.proxy;
|
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
|
* This event is fired by the proxy after the proxy has stopped accepting connections but before the
|
||||||
* exits.
|
* proxy process exits.
|
||||||
*/
|
*/
|
||||||
public final class ProxyShutdownEvent {
|
public final class ProxyShutdownEvent {
|
||||||
@Override
|
|
||||||
public String toString() {
|
@Override
|
||||||
return "ProxyShutdownEvent";
|
public String toString() {
|
||||||
}
|
return "ProxyShutdownEvent";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,82 +2,86 @@ package com.velocitypowered.api.event.query;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.velocitypowered.api.proxy.server.QueryResponse;
|
import com.velocitypowered.api.proxy.server.QueryResponse;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This event is fired if proxy is getting queried over GS4 Query protocol
|
* This event is fired if proxy is getting queried over GS4 Query protocol
|
||||||
*/
|
*/
|
||||||
public final class ProxyQueryEvent {
|
public final class ProxyQueryEvent {
|
||||||
private final QueryType queryType;
|
|
||||||
private final InetAddress querierAddress;
|
|
||||||
private QueryResponse response;
|
|
||||||
|
|
||||||
public ProxyQueryEvent(QueryType queryType, InetAddress querierAddress, QueryResponse response) {
|
private final QueryType queryType;
|
||||||
this.queryType = Preconditions.checkNotNull(queryType, "queryType");
|
private final InetAddress querierAddress;
|
||||||
this.querierAddress = Preconditions.checkNotNull(querierAddress, "querierAddress");
|
private QueryResponse response;
|
||||||
this.response = Preconditions.checkNotNull(response, "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
|
* Full query asks pretty much everything present on this event (only hardcoded values cannot be
|
||||||
* @return query type
|
* modified here).
|
||||||
*/
|
*/
|
||||||
@NonNull
|
FULL;
|
||||||
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
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
package com.velocitypowered.api.permission;
|
package com.velocitypowered.api.permission;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function that calculates the permission settings for a given
|
* Function that calculates the permission settings for a given {@link PermissionSubject}.
|
||||||
* {@link PermissionSubject}.
|
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface PermissionFunction {
|
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}.
|
* A permission function that always returns {@link Tristate#TRUE}.
|
||||||
*/
|
*/
|
||||||
PermissionFunction ALWAYS_FALSE = p -> Tristate.FALSE;
|
PermissionFunction ALWAYS_TRUE = p -> Tristate.TRUE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A permission function that always returns {@link Tristate#UNDEFINED}.
|
* A permission function that always returns {@link Tristate#FALSE}.
|
||||||
*/
|
*/
|
||||||
PermissionFunction ALWAYS_UNDEFINED = p -> Tristate.UNDEFINED;
|
PermissionFunction ALWAYS_FALSE = p -> Tristate.FALSE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the subjects setting for a particular permission.
|
* A permission function that always returns {@link Tristate#UNDEFINED}.
|
||||||
*
|
*/
|
||||||
* @param permission the permission
|
PermissionFunction ALWAYS_UNDEFINED = p -> Tristate.UNDEFINED;
|
||||||
* @return the value the permission is set to
|
|
||||||
*/
|
/**
|
||||||
Tristate getPermissionValue(String permission);
|
* Gets the subjects setting for a particular permission.
|
||||||
|
*
|
||||||
|
* @param permission the permission
|
||||||
|
* @return the value the permission is set to
|
||||||
|
*/
|
||||||
|
Tristate getPermissionValue(String permission);
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,12 @@ package com.velocitypowered.api.permission;
|
|||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface PermissionProvider {
|
public interface PermissionProvider {
|
||||||
/**
|
|
||||||
* Creates a {@link PermissionFunction} for the subject.
|
/**
|
||||||
*
|
* Creates a {@link PermissionFunction} for the subject.
|
||||||
* @param subject the subject
|
*
|
||||||
* @return the function
|
* @param subject the subject
|
||||||
*/
|
* @return the function
|
||||||
PermissionFunction createFunction(PermissionSubject subject);
|
*/
|
||||||
|
PermissionFunction createFunction(PermissionSubject subject);
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,22 @@ package com.velocitypowered.api.permission;
|
|||||||
* Represents a object that has a set of queryable permissions.
|
* Represents a object that has a set of queryable permissions.
|
||||||
*/
|
*/
|
||||||
public interface PermissionSubject {
|
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.
|
* Determines whether or not the subject has a particular permission.
|
||||||
*
|
*
|
||||||
* @param permission the permission
|
* @param permission the permission to check for
|
||||||
* @return the value the permission is set to
|
* @return whether or not the subject has the permission
|
||||||
*/
|
*/
|
||||||
Tristate getPermissionValue(String 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);
|
||||||
}
|
}
|
||||||
|
@ -8,69 +8,70 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
* <p>Possible values:</p>
|
* <p>Possible values:</p>
|
||||||
* <p></p>
|
* <p></p>
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #TRUE} - a positive setting</li>
|
* <li>{@link #TRUE} - a positive setting</li>
|
||||||
* <li>{@link #FALSE} - a negative (negated) setting</li>
|
* <li>{@link #FALSE} - a negative (negated) setting</li>
|
||||||
* <li>{@link #UNDEFINED} - a non-existent setting</li>
|
* <li>{@link #UNDEFINED} - a non-existent setting</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public enum Tristate {
|
public enum Tristate {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A value indicating a positive setting
|
* A value indicating a positive setting
|
||||||
*/
|
*/
|
||||||
TRUE(true),
|
TRUE(true),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A value indicating a negative (negated) setting
|
* A value indicating a negative (negated) setting
|
||||||
*/
|
*/
|
||||||
FALSE(false),
|
FALSE(false),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A value indicating a non-existent setting
|
* A value indicating a non-existent setting
|
||||||
*/
|
*/
|
||||||
UNDEFINED(false);
|
UNDEFINED(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@link Tristate} from a boolean
|
* Returns a {@link Tristate} from a boolean
|
||||||
*
|
*
|
||||||
* @param val the boolean value
|
* @param val the boolean value
|
||||||
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or <code>false</code>, respectively.
|
* @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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private final boolean booleanValue;
|
||||||
* 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;
|
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>
|
||||||
/**
|
*
|
||||||
* Returns the value of the Tristate as a boolean.
|
* @return a boolean representation of the Tristate.
|
||||||
*
|
*/
|
||||||
* <p>A value of {@link #UNDEFINED} converts to false.</p>
|
public boolean asBoolean() {
|
||||||
*
|
return this.booleanValue;
|
||||||
* @return a boolean representation of the Tristate.
|
}
|
||||||
*/
|
|
||||||
public boolean asBoolean() {
|
|
||||||
return this.booleanValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,19 +10,19 @@ import java.lang.annotation.Target;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({})
|
@Target({})
|
||||||
public @interface Dependency {
|
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
|
* The plugin ID of the dependency.
|
||||||
* this is {@code false}.
|
*
|
||||||
*
|
* @return The dependency plugin ID
|
||||||
* @return true if the dependency is optional for the plugin to work
|
* @see Plugin#id()
|
||||||
*/
|
*/
|
||||||
boolean optional() default false;
|
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;
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package com.velocitypowered.api.plugin;
|
package com.velocitypowered.api.plugin;
|
||||||
|
|
||||||
public class InvalidPluginException extends Exception {
|
public class InvalidPluginException extends Exception {
|
||||||
public InvalidPluginException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InvalidPluginException(String message) {
|
public InvalidPluginException() {
|
||||||
super(message);
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvalidPluginException(String message, Throwable cause) {
|
public InvalidPluginException(String message) {
|
||||||
super(message, cause);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvalidPluginException(Throwable cause) {
|
public InvalidPluginException(String message, Throwable cause) {
|
||||||
super(cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InvalidPluginException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,56 +11,55 @@ import java.lang.annotation.Target;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
public @interface Plugin {
|
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
|
* The ID of the plugin. This ID should be unique as to not conflict with other plugins.
|
||||||
* similar things.
|
*
|
||||||
*
|
* The plugin ID must match the {@link PluginDescription#ID_PATTERN}.
|
||||||
* @return The plugin name, or an empty string if unknown
|
*
|
||||||
*/
|
* @return the ID for this plugin
|
||||||
String name() default "";
|
*/
|
||||||
|
String id();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the plugin.
|
* The human readable name of the plugin as to be used in descriptions and similar things.
|
||||||
*
|
*
|
||||||
* @return the version of the plugin, or an empty string if unknown
|
* @return The plugin name, or an empty string if unknown
|
||||||
*/
|
*/
|
||||||
String version() default "";
|
String name() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The description of the plugin, explaining what it can be used for.
|
* The version of the plugin.
|
||||||
*
|
*
|
||||||
* @return The plugin description, or an empty string if unknown
|
* @return the version of the plugin, or an empty string if unknown
|
||||||
*/
|
*/
|
||||||
String description() default "";
|
String version() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL or website of the plugin.
|
* The description of the plugin, explaining what it can be used for.
|
||||||
*
|
*
|
||||||
* @return The plugin url, or an empty string if unknown
|
* @return The plugin description, or an empty string if unknown
|
||||||
*/
|
*/
|
||||||
String url() default "";
|
String description() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The author of the plugin.
|
* The URL or website of the plugin.
|
||||||
*
|
*
|
||||||
* @return the plugin's author, or empty if unknown
|
* @return The plugin url, or an empty string if unknown
|
||||||
*/
|
*/
|
||||||
String[] authors() default "";
|
String url() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The dependencies required to load before this plugin.
|
* The author of the plugin.
|
||||||
*
|
*
|
||||||
* @return the plugin dependencies
|
* @return the plugin's author, or empty if unknown
|
||||||
*/
|
*/
|
||||||
Dependency[] dependencies() default {};
|
String[] authors() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The dependencies required to load before this plugin.
|
||||||
|
*
|
||||||
|
* @return the plugin dependencies
|
||||||
|
*/
|
||||||
|
Dependency[] dependencies() default {};
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,20 @@ import java.util.Optional;
|
|||||||
* A wrapper around a plugin loaded by the proxy.
|
* A wrapper around a plugin loaded by the proxy.
|
||||||
*/
|
*/
|
||||||
public interface PluginContainer {
|
public interface PluginContainer {
|
||||||
/**
|
|
||||||
* Returns the plugin's description.
|
|
||||||
*
|
|
||||||
* @return the plugin's description
|
|
||||||
*/
|
|
||||||
PluginDescription getDescription();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the created plugin if it is available.
|
* Returns the plugin's description.
|
||||||
*
|
*
|
||||||
* @return the instance if available
|
* @return the plugin's description
|
||||||
*/
|
*/
|
||||||
default Optional<?> getInstance() {
|
PluginDescription getDescription();
|
||||||
return Optional.empty();
|
|
||||||
}
|
/**
|
||||||
|
* Returns the created plugin if it is available.
|
||||||
|
*
|
||||||
|
* @return the instance if available
|
||||||
|
*/
|
||||||
|
default Optional<?> getInstance() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package com.velocitypowered.api.plugin;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
import com.velocitypowered.api.plugin.meta.PluginDependency;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -14,93 +13,92 @@ import java.util.regex.Pattern;
|
|||||||
* Represents metadata for a specific version of a plugin.
|
* Represents metadata for a specific version of a plugin.
|
||||||
*/
|
*/
|
||||||
public interface PluginDescription {
|
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.
|
* 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
|
||||||
* @return the plugin ID
|
* characters.
|
||||||
* @see Plugin#id()
|
*/
|
||||||
*/
|
Pattern ID_PATTERN = Pattern.compile("[a-z][a-z0-9-_]{0,63}");
|
||||||
String getId();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of the {@link Plugin} within this container.
|
* Gets the qualified ID of the {@link Plugin} within this container.
|
||||||
*
|
*
|
||||||
* @return an {@link Optional} with the plugin name, may be empty
|
* @return the plugin ID
|
||||||
* @see Plugin#name()
|
* @see Plugin#id()
|
||||||
*/
|
*/
|
||||||
default Optional<String> getName() {
|
String getId();
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the version of the {@link Plugin} within this container.
|
* Gets the name of the {@link Plugin} within this container.
|
||||||
*
|
*
|
||||||
* @return an {@link Optional} with the plugin version, may be empty
|
* @return an {@link Optional} with the plugin name, may be empty
|
||||||
* @see Plugin#version()
|
* @see Plugin#name()
|
||||||
*/
|
*/
|
||||||
default Optional<String> getVersion() {
|
default Optional<String> getName() {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the description of the {@link Plugin} within this container.
|
* Gets the version of the {@link Plugin} within this container.
|
||||||
*
|
*
|
||||||
* @return an {@link Optional} with the plugin description, may be empty
|
* @return an {@link Optional} with the plugin version, may be empty
|
||||||
* @see Plugin#description()
|
* @see Plugin#version()
|
||||||
*/
|
*/
|
||||||
default Optional<String> getDescription() {
|
default Optional<String> getVersion() {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the url or website of the {@link Plugin} within this container.
|
* Gets the description of the {@link Plugin} within this container.
|
||||||
*
|
*
|
||||||
* @return an {@link Optional} with the plugin url, may be empty
|
* @return an {@link Optional} with the plugin description, may be empty
|
||||||
* @see Plugin#url()
|
* @see Plugin#description()
|
||||||
*/
|
*/
|
||||||
default Optional<String> getUrl() {
|
default Optional<String> getDescription() {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the authors of the {@link Plugin} within this container.
|
* Gets the url or website of the {@link Plugin} within this container.
|
||||||
*
|
*
|
||||||
* @return the plugin authors, may be empty
|
* @return an {@link Optional} with the plugin url, may be empty
|
||||||
* @see Plugin#authors()
|
* @see Plugin#url()
|
||||||
*/
|
*/
|
||||||
default List<String> getAuthors() {
|
default Optional<String> getUrl() {
|
||||||
return ImmutableList.of();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link Collection} of all dependencies of the {@link Plugin} within
|
* Gets the authors of the {@link Plugin} within this container.
|
||||||
* this container.
|
*
|
||||||
*
|
* @return the plugin authors, may be empty
|
||||||
* @return the plugin dependencies, can be empty
|
* @see Plugin#authors()
|
||||||
* @see Plugin#dependencies()
|
*/
|
||||||
*/
|
default List<String> getAuthors() {
|
||||||
default Collection<PluginDependency> getDependencies() {
|
return ImmutableList.of();
|
||||||
return ImmutableSet.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();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
default Optional<PluginDependency> getDependency(String id) {
|
||||||
* Returns the source the plugin was loaded from.
|
return Optional.empty();
|
||||||
*
|
}
|
||||||
* @return the source the plugin was loaded from or {@link Optional#empty()}
|
|
||||||
* if unknown
|
/**
|
||||||
*/
|
* Returns the source the plugin was loaded from.
|
||||||
default Optional<Path> getSource() {
|
*
|
||||||
return Optional.empty();
|
* @return the source the plugin was loaded from or {@link Optional#empty()} if unknown
|
||||||
}
|
*/
|
||||||
|
default Optional<Path> getSource() {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,47 +5,49 @@ import java.util.Collection;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from plugin instances
|
* Manages plugins loaded on the proxy. This manager can retrieve {@link PluginContainer}s from
|
||||||
* and inject arbitrary JAR files into the plugin classpath with {@link #addToClasspath(Object, Path)}.
|
* plugin instances and inject arbitrary JAR files into the plugin classpath with {@link
|
||||||
|
* #addToClasspath(Object, Path)}.
|
||||||
*/
|
*/
|
||||||
public interface PluginManager {
|
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.
|
* Gets the plugin container from an instance.
|
||||||
*
|
*
|
||||||
* @param id the plugin ID
|
* @param instance the instance
|
||||||
* @return the plugin, if available
|
* @return the container
|
||||||
*/
|
*/
|
||||||
Optional<PluginContainer> getPlugin(String id);
|
Optional<PluginContainer> fromInstance(Object instance);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link Collection} of all {@link PluginContainer}s.
|
* Retrieves a {@link PluginContainer} based on its ID.
|
||||||
*
|
*
|
||||||
* @return the plugins
|
* @param id the plugin ID
|
||||||
*/
|
* @return the plugin, if available
|
||||||
Collection<PluginContainer> getPlugins();
|
*/
|
||||||
|
Optional<PluginContainer> getPlugin(String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a plugin is loaded based on its ID.
|
* Gets a {@link Collection} of all {@link PluginContainer}s.
|
||||||
*
|
*
|
||||||
* @param id the id of the plugin
|
* @return the plugins
|
||||||
* @return {@code true} if loaded
|
*/
|
||||||
*/
|
Collection<PluginContainer> getPlugins();
|
||||||
boolean isLoaded(String id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the specified {@code path} to the plugin classpath.
|
* Checks if a plugin is loaded based on its ID.
|
||||||
*
|
*
|
||||||
* @param plugin the plugin
|
* @param id the id of the plugin
|
||||||
* @param path the path to the JAR you want to inject into the classpath
|
* @return {@code true} if loaded
|
||||||
* @throws UnsupportedOperationException if the operation is not applicable to this plugin
|
*/
|
||||||
*/
|
boolean isLoaded(String id);
|
||||||
void addToClasspath(Object plugin, Path path);
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
package com.velocitypowered.api.plugin.annotation;
|
package com.velocitypowered.api.plugin.annotation;
|
||||||
|
|
||||||
import com.google.inject.BindingAnnotation;
|
import com.google.inject.BindingAnnotation;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This annotation requests that Velocity inject a {@link java.nio.file.Path} instance with a plugin-specific data
|
* This annotation requests that Velocity inject a {@link java.nio.file.Path} instance with a
|
||||||
* directory.
|
* plugin-specific data directory.
|
||||||
*/
|
*/
|
||||||
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@BindingAnnotation
|
@BindingAnnotation
|
||||||
public @interface DataDirectory {
|
public @interface DataDirectory {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,80 +1,83 @@
|
|||||||
package com.velocitypowered.api.plugin.meta;
|
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.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.base.Strings.emptyToNull;
|
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.
|
* Represents a dependency on another plugin.
|
||||||
*/
|
*/
|
||||||
public final class PluginDependency {
|
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) {
|
private final boolean optional;
|
||||||
this.id = checkNotNull(id, "id");
|
|
||||||
checkArgument(!id.isEmpty(), "id cannot be empty");
|
public PluginDependency(String id, @Nullable String version, boolean optional) {
|
||||||
this.version = emptyToNull(version);
|
this.id = checkNotNull(id, "id");
|
||||||
this.optional = optional;
|
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;
|
||||||
}
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
/**
|
return false;
|
||||||
* Returns the plugin ID of this {@link PluginDependency}
|
|
||||||
*
|
|
||||||
* @return the plugin ID
|
|
||||||
*/
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
PluginDependency that = (PluginDependency) o;
|
||||||
|
return optional == that.optional &&
|
||||||
|
Objects.equals(id, that.id) &&
|
||||||
|
Objects.equals(version, that.version);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the version this {@link PluginDependency} should match.
|
public int hashCode() {
|
||||||
*
|
return Objects.hash(id, version, optional);
|
||||||
* @return an {@link Optional} with the plugin version, may be empty
|
}
|
||||||
*/
|
|
||||||
public Optional<String> getVersion() {
|
|
||||||
return Optional.ofNullable(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns whether the dependency is optional for the plugin to work
|
public String toString() {
|
||||||
* correctly.
|
return "PluginDependency{" +
|
||||||
*
|
"id='" + id + '\'' +
|
||||||
* @return true if dependency is optional
|
", version='" + version + '\'' +
|
||||||
*/
|
", optional=" + 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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,91 +1,100 @@
|
|||||||
package com.velocitypowered.api.proxy;
|
package com.velocitypowered.api.proxy;
|
||||||
|
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import net.kyori.text.Component;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
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
|
* Provides a fluent interface to compose and send a connection request to another server behind the
|
||||||
* request is created using {@link Player#createConnectionRequest(RegisteredServer)}.
|
* proxy. A connection request is created using {@link Player#createConnectionRequest(RegisteredServer)}.
|
||||||
*/
|
*/
|
||||||
public interface ConnectionRequestBuilder {
|
public interface ConnectionRequestBuilder {
|
||||||
/**
|
|
||||||
* Returns the server that this connection request represents.
|
/**
|
||||||
* @return the server this request will connect to
|
* Returns the server that this connection request represents.
|
||||||
*/
|
*
|
||||||
RegisteredServer getServer();
|
* @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
|
* Determines whether or not the connection request was successful.
|
||||||
* 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
|
* @return whether or not the request succeeded
|
||||||
*/
|
*/
|
||||||
CompletableFuture<Result> connect();
|
default boolean isSuccessful() {
|
||||||
|
return getStatus() == Status.SUCCESS;
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
Status getStatus();
|
||||||
/**
|
|
||||||
* The player was successfully connected to the server.
|
/**
|
||||||
*/
|
* Returns an (optional) textual reason for the failure to connect to the server.
|
||||||
SUCCESS,
|
*
|
||||||
/**
|
* @return the reason why the user could not connect to the server
|
||||||
* The player is already connected to this server.
|
*/
|
||||||
*/
|
Optional<Component> getReason();
|
||||||
ALREADY_CONNECTED,
|
}
|
||||||
/**
|
|
||||||
* The connection is already in progress.
|
/**
|
||||||
*/
|
* Represents the status of a connection request initiated by a {@link ConnectionRequestBuilder}.
|
||||||
CONNECTION_IN_PROGRESS,
|
*/
|
||||||
/**
|
enum Status {
|
||||||
* A plugin has cancelled this connection.
|
/**
|
||||||
*/
|
* The player was successfully connected to the server.
|
||||||
CONNECTION_CANCELLED,
|
*/
|
||||||
/**
|
SUCCESS,
|
||||||
* The server disconnected the user. A reason may be provided in the {@link Result} object.
|
/**
|
||||||
*/
|
* The player is already connected to this server.
|
||||||
SERVER_DISCONNECTED
|
*/
|
||||||
}
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,27 +7,32 @@ import java.util.Optional;
|
|||||||
* Represents an incoming connection to the proxy.
|
* Represents an incoming connection to the proxy.
|
||||||
*/
|
*/
|
||||||
public interface InboundConnection {
|
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.
|
* Returns the player's IP address.
|
||||||
* @return the hostname from the client
|
*
|
||||||
*/
|
* @return the player's IP
|
||||||
Optional<InetSocketAddress> getVirtualHost();
|
*/
|
||||||
|
InetSocketAddress getRemoteAddress();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether or not the player remains online.
|
* Returns the hostname that the user entered into the client, if applicable.
|
||||||
* @return whether or not the player active
|
*
|
||||||
*/
|
* @return the hostname from the client
|
||||||
boolean isActive();
|
*/
|
||||||
|
Optional<InetSocketAddress> getVirtualHost();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current protocol version this connection uses.
|
* Determine whether or not the player remains online.
|
||||||
* @return the protocol version the connection uses
|
*
|
||||||
*/
|
* @return whether or not the player active
|
||||||
int getProtocolVersion();
|
*/
|
||||||
|
boolean isActive();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current protocol version this connection uses.
|
||||||
|
*
|
||||||
|
* @return the protocol version the connection uses
|
||||||
|
*/
|
||||||
|
int getProtocolVersion();
|
||||||
}
|
}
|
||||||
|
@ -4,136 +4,150 @@ import com.velocitypowered.api.command.CommandSource;
|
|||||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
|
import com.velocitypowered.api.proxy.messages.ChannelMessageSource;
|
||||||
import com.velocitypowered.api.proxy.player.PlayerSettings;
|
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.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
import com.velocitypowered.api.proxy.player.TabList;
|
|
||||||
import com.velocitypowered.api.util.MessagePosition;
|
import com.velocitypowered.api.util.MessagePosition;
|
||||||
import com.velocitypowered.api.util.ModInfo;
|
import com.velocitypowered.api.util.ModInfo;
|
||||||
import com.velocitypowered.api.util.title.Title;
|
import com.velocitypowered.api.util.title.Title;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import net.kyori.text.Component;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import net.kyori.text.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a player who is connected to the proxy.
|
* Represents a player who is connected to the proxy.
|
||||||
*/
|
*/
|
||||||
public interface Player extends CommandSource, InboundConnection, ChannelMessageSource, ChannelMessageSink {
|
public interface Player extends CommandSource, InboundConnection, ChannelMessageSource,
|
||||||
/**
|
ChannelMessageSink {
|
||||||
* Returns the player's current username.
|
|
||||||
* @return the username
|
|
||||||
*/
|
|
||||||
String getUsername();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the player's UUID.
|
* Returns the player's current username.
|
||||||
* @return the UUID
|
*
|
||||||
*/
|
* @return the username
|
||||||
UUID getUniqueId();
|
*/
|
||||||
|
String getUsername();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the server that the player is currently connected to.
|
* Returns the player's UUID.
|
||||||
* @return an {@link Optional} the server that the player is connected to, which may be empty
|
*
|
||||||
*/
|
* @return the UUID
|
||||||
Optional<ServerConnection> getCurrentServer();
|
*/
|
||||||
|
UUID getUniqueId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the player settings
|
* Returns the server that the player is currently connected to.
|
||||||
* @return the settings
|
*
|
||||||
*/
|
* @return an {@link Optional} the server that the player is connected to, which may be empty
|
||||||
PlayerSettings getPlayerSettings();
|
*/
|
||||||
|
Optional<ServerConnection> getCurrentServer();
|
||||||
/**
|
|
||||||
* 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 current player's ping
|
* Returns the player settings
|
||||||
* @return the player's ping or -1 if ping information is currently unknown
|
*
|
||||||
*/
|
* @return the settings
|
||||||
long getPing();
|
*/
|
||||||
|
PlayerSettings getPlayerSettings();
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a chat message to the player's client in the specified position.
|
* Returns the player's mod info if they have a modded client.
|
||||||
* @param component the chat message to send
|
*
|
||||||
* @param position the position for the message
|
* @return an {@link Optional} the mod info. which may be empty
|
||||||
*/
|
*/
|
||||||
void sendMessage(Component component, MessagePosition position);
|
Optional<ModInfo> getModInfo();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new connection request so that the player can connect to another server.
|
* Returns the current player's ping
|
||||||
* @param server the server to connect to
|
*
|
||||||
* @return a new connection request
|
* @return the player's ping or -1 if ping information is currently unknown
|
||||||
*/
|
*/
|
||||||
ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
|
long getPing();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the player's profile properties.
|
* Sends a chat message to the player's client.
|
||||||
*
|
*
|
||||||
* <p>The returned list may be unmodifiable.</p>
|
* @param component the chat message to send
|
||||||
*
|
*/
|
||||||
* @return the player's profile properties
|
default void sendMessage(Component component) {
|
||||||
*/
|
sendMessage(component, MessagePosition.CHAT);
|
||||||
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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the tab list header and footer for the player.
|
* Sends a chat message to the player's client in the specified position.
|
||||||
* @deprecated Use {@link TabList#clearHeaderAndFooter()}.
|
*
|
||||||
*/
|
* @param component the chat message to send
|
||||||
@Deprecated
|
* @param position the position for the message
|
||||||
void clearHeaderAndFooter();
|
*/
|
||||||
|
void sendMessage(Component component, MessagePosition position);
|
||||||
/**
|
|
||||||
* 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}
|
* Creates a new connection request so that the player can connect to another server.
|
||||||
* methods will become undefined.
|
*
|
||||||
* @param reason component with the reason
|
* @param server the server to connect to
|
||||||
*/
|
* @return a new connection request
|
||||||
void disconnect(Component reason);
|
*/
|
||||||
|
ConnectionRequestBuilder createConnectionRequest(RegisteredServer server);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the specified title to the client.
|
* Gets the player's profile properties.
|
||||||
* @param title the title to send
|
*
|
||||||
*/
|
* <p>The returned list may be unmodifiable.</p>
|
||||||
void sendTitle(Title title);
|
*
|
||||||
|
* @return the player's profile properties
|
||||||
|
*/
|
||||||
|
List<GameProfile.Property> getGameProfileProperties();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends chat input onto the players current server as if they typed it
|
* Sets the player's profile properties.
|
||||||
* into the client chat box.
|
*
|
||||||
* @param input the chat input to send
|
* @param properties the properties
|
||||||
*/
|
*/
|
||||||
void spoofChatInput(String input);
|
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);
|
||||||
}
|
}
|
||||||
|
@ -10,132 +10,151 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
|
|||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
import com.velocitypowered.api.scheduler.Scheduler;
|
import com.velocitypowered.api.scheduler.Scheduler;
|
||||||
import com.velocitypowered.api.util.ProxyVersion;
|
import com.velocitypowered.api.util.ProxyVersion;
|
||||||
import net.kyori.text.Component;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import net.kyori.text.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an interface to a Minecraft server proxy.
|
* Provides an interface to a Minecraft server proxy.
|
||||||
*/
|
*/
|
||||||
public interface ProxyServer {
|
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.
|
* Retrieves the player currently connected to this proxy by their Minecraft username. The search
|
||||||
* @param uuid the UUID
|
* is case-insensitive.
|
||||||
* @return an {@link Optional} with the player, which may be empty
|
*
|
||||||
*/
|
* @param username the username to search for
|
||||||
Optional<Player> getPlayer(UUID uuid);
|
* @return an {@link Optional} with the player, which may be empty
|
||||||
|
*/
|
||||||
|
Optional<Player> getPlayer(String username);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcasts a message to all players currently online.
|
* Retrieves the player currently connected to this proxy by their Minecraft UUID.
|
||||||
* @param component the message to send
|
*
|
||||||
*/
|
* @param uuid the UUID
|
||||||
void broadcast(Component component);
|
* @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
|
* Broadcasts a message to all players currently online.
|
||||||
* online.
|
*
|
||||||
* @return the players online on this proxy
|
* @param component the message to send
|
||||||
*/
|
*/
|
||||||
Collection<Player> getAllPlayers();
|
void broadcast(Component component);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of players currently connected to this proxy.
|
* Retrieves all players currently connected to this proxy. This call may or may not be a snapshot
|
||||||
* @return the players on this proxy
|
* of all players online.
|
||||||
*/
|
*
|
||||||
int getPlayerCount();
|
* @return the players online on this proxy
|
||||||
|
*/
|
||||||
|
Collection<Player> getAllPlayers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a registered {@link RegisteredServer} instance by its name. The search is case-insensitive.
|
* Returns the number of players currently connected to this proxy.
|
||||||
* @param name the name of the server
|
*
|
||||||
* @return the registered server, which may be empty
|
* @return the players on this proxy
|
||||||
*/
|
*/
|
||||||
Optional<RegisteredServer> getServer(String name);
|
int getPlayerCount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all {@link RegisteredServer}s registered with this proxy.
|
* Retrieves a registered {@link RegisteredServer} instance by its name. The search is
|
||||||
* @return the servers registered with this proxy
|
* case-insensitive.
|
||||||
*/
|
*
|
||||||
Collection<RegisteredServer> getAllServers();
|
* @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.
|
* Retrieves all {@link RegisteredServer}s registered with this proxy.
|
||||||
* @param server the server to register
|
*
|
||||||
* @return the newly registered server
|
* @return the servers registered with this proxy
|
||||||
*/
|
*/
|
||||||
RegisteredServer registerServer(ServerInfo server);
|
Collection<RegisteredServer> getAllServers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters this server from the proxy.
|
* Registers a server with this proxy. A server with this name should not already exist.
|
||||||
* @param server the server to unregister
|
*
|
||||||
*/
|
* @param server the server to register
|
||||||
void unregisterServer(ServerInfo server);
|
* @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
|
* Unregisters this server from the proxy.
|
||||||
* 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.
|
* @param server the server to unregister
|
||||||
* @return the console command invoker
|
*/
|
||||||
*/
|
void unregisterServer(ServerInfo server);
|
||||||
CommandSource getConsoleCommandSource();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link PluginManager} instance.
|
* 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
|
||||||
* @return the plugin manager instance
|
* strongly urged to implement their own {@link CommandSource} instead of using the console
|
||||||
*/
|
* invoker.
|
||||||
PluginManager getPluginManager();
|
*
|
||||||
|
* @return the console command invoker
|
||||||
|
*/
|
||||||
|
CommandSource getConsoleCommandSource();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link EventManager} instance.
|
* Gets the {@link PluginManager} instance.
|
||||||
*
|
*
|
||||||
* @return the event manager instance
|
* @return the plugin manager instance
|
||||||
*/
|
*/
|
||||||
EventManager getEventManager();
|
PluginManager getPluginManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link CommandManager} instance.
|
* Gets the {@link EventManager} instance.
|
||||||
* @return the command manager
|
*
|
||||||
*/
|
* @return the event manager instance
|
||||||
CommandManager getCommandManager();
|
*/
|
||||||
|
EventManager getEventManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link Scheduler} instance.
|
* Gets the {@link CommandManager} instance.
|
||||||
* @return the scheduler instance
|
*
|
||||||
*/
|
* @return the command manager
|
||||||
Scheduler getScheduler();
|
*/
|
||||||
|
CommandManager getCommandManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link ChannelRegistrar} instance.
|
* Gets the {@link Scheduler} instance.
|
||||||
* @return the channel registrar
|
*
|
||||||
*/
|
* @return the scheduler instance
|
||||||
ChannelRegistrar getChannelRegistrar();
|
*/
|
||||||
|
Scheduler getScheduler();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the address that this proxy is bound to. This does not necessarily indicate the external IP address of the
|
* Gets the {@link ChannelRegistrar} instance.
|
||||||
* proxy.
|
*
|
||||||
* @return the address the proxy is bound to
|
* @return the channel registrar
|
||||||
*/
|
*/
|
||||||
InetSocketAddress getBoundAddress();
|
ChannelRegistrar getChannelRegistrar();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link ProxyConfig} instance.
|
* Gets the address that this proxy is bound to. This does not necessarily indicate the external
|
||||||
* @return the proxy config
|
* IP address of the proxy.
|
||||||
* */
|
*
|
||||||
ProxyConfig getConfiguration();
|
* @return the address the proxy is bound to
|
||||||
|
*/
|
||||||
|
InetSocketAddress getBoundAddress();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the version of the proxy.
|
* Gets the {@link ProxyConfig} instance.
|
||||||
* @return the proxy version
|
*
|
||||||
*/
|
* @return the proxy config
|
||||||
ProxyVersion getVersion();
|
*/
|
||||||
|
ProxyConfig getConfiguration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the version of the proxy.
|
||||||
|
*
|
||||||
|
* @return the proxy version
|
||||||
|
*/
|
||||||
|
ProxyVersion getVersion();
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,25 @@ import com.velocitypowered.api.proxy.server.ServerInfo;
|
|||||||
* Represents a connection to a backend server from the proxy for a client.
|
* Represents a connection to a backend server from the proxy for a client.
|
||||||
*/
|
*/
|
||||||
public interface ServerConnection extends ChannelMessageSource, ChannelMessageSink {
|
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.
|
* Returns the server that this connection is connected to.
|
||||||
* @return the server info for this connection
|
*
|
||||||
*/
|
* @return the server this connection is connected to
|
||||||
ServerInfo getServerInfo();
|
*/
|
||||||
|
RegisteredServer getServer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the player that this connection is associated with.
|
* Returns the server info for this connection.
|
||||||
* @return the player for this connection
|
*
|
||||||
*/
|
* @return the server info for this connection
|
||||||
Player getPlayer();
|
*/
|
||||||
|
ServerInfo getServerInfo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the player that this connection is associated with.
|
||||||
|
*
|
||||||
|
* @return the player for this connection
|
||||||
|
*/
|
||||||
|
Player getPlayer();
|
||||||
}
|
}
|
||||||
|
@ -1,116 +1,133 @@
|
|||||||
package com.velocitypowered.api.proxy.config;
|
package com.velocitypowered.api.proxy.config;
|
||||||
|
|
||||||
import com.velocitypowered.api.util.Favicon;
|
import com.velocitypowered.api.util.Favicon;
|
||||||
import net.kyori.text.Component;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import net.kyori.text.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an interface to a proxy configuration
|
* Provides an interface to a proxy configuration
|
||||||
*/
|
*/
|
||||||
public interface ProxyConfig {
|
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
|
* Whether GameSpy 4 queries are accepted by the proxy
|
||||||
* @return the query port
|
*
|
||||||
*/
|
* @return queries enabled
|
||||||
int getQueryPort();
|
*/
|
||||||
|
boolean isQueryEnabled();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the map name reported to GameSpy 4 query services
|
* Get the port GameSpy 4 queries are accepted on
|
||||||
* @return the map name
|
*
|
||||||
*/
|
* @return the query port
|
||||||
String getQueryMap();
|
*/
|
||||||
|
int getQueryPort();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether GameSpy 4 queries should show plugins installed on
|
* Get the map name reported to GameSpy 4 query services
|
||||||
* Velocity by default
|
*
|
||||||
* @return show plugins in query
|
* @return the map name
|
||||||
*/
|
*/
|
||||||
boolean shouldQueryShowPlugins();
|
String getQueryMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the MOTD component shown in the tab list
|
* Whether GameSpy 4 queries should show plugins installed on Velocity by default
|
||||||
* @return the motd component
|
*
|
||||||
*/
|
* @return show plugins in query
|
||||||
Component getMotdComponent();
|
*/
|
||||||
|
boolean shouldQueryShowPlugins();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum players shown in the tab list
|
* Get the MOTD component shown in the tab list
|
||||||
* @return max players
|
*
|
||||||
*/
|
* @return the motd component
|
||||||
int getShowMaxPlayers();
|
*/
|
||||||
|
Component getMotdComponent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether the proxy is online mode. This determines if players are authenticated with Mojang servers.
|
* Get the maximum players shown in the tab list
|
||||||
* @return online mode enabled
|
*
|
||||||
*/
|
* @return max players
|
||||||
boolean isOnlineMode();
|
*/
|
||||||
|
int getShowMaxPlayers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a Map of all servers registered on this proxy
|
* Get whether the proxy is online mode. This determines if players are authenticated with Mojang
|
||||||
* @return registered servers map
|
* servers.
|
||||||
*/
|
*
|
||||||
Map<String, String> getServers();
|
* @return online mode enabled
|
||||||
|
*/
|
||||||
|
boolean isOnlineMode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the order of servers that players will be connected to
|
* Get a Map of all servers registered on this proxy
|
||||||
* @return connection order list
|
*
|
||||||
*/
|
* @return registered servers map
|
||||||
List<String> getAttemptConnectionOrder();
|
*/
|
||||||
|
Map<String, String> getServers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get forced servers mapped to given virtual host
|
* Get the order of servers that players will be connected to
|
||||||
* @return list of server names
|
*
|
||||||
*/
|
* @return connection order list
|
||||||
Map<String, List<String>> getForcedHosts();
|
*/
|
||||||
|
List<String> getAttemptConnectionOrder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the minimum compression threshold for packets
|
* Get forced servers mapped to given virtual host
|
||||||
* @return the compression threshold
|
*
|
||||||
*/
|
* @return list of server names
|
||||||
int getCompressionThreshold();
|
*/
|
||||||
|
Map<String, List<String>> getForcedHosts();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the level of compression that packets will be compressed to
|
* Get the minimum compression threshold for packets
|
||||||
* @return the compression level
|
*
|
||||||
*/
|
* @return the compression threshold
|
||||||
int getCompressionLevel();
|
*/
|
||||||
|
int getCompressionThreshold();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the limit for how long a player must wait to log back in
|
* Get the level of compression that packets will be compressed to
|
||||||
* @return the login rate limit (in milliseconds)
|
*
|
||||||
*/
|
* @return the compression level
|
||||||
int getLoginRatelimit();
|
*/
|
||||||
|
int getCompressionLevel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the proxy favicon shown in the tablist
|
* Get the limit for how long a player must wait to log back in
|
||||||
* @return optional favicon
|
*
|
||||||
*/
|
* @return the login rate limit (in milliseconds)
|
||||||
Optional<Favicon> getFavicon();
|
*/
|
||||||
|
int getLoginRatelimit();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get whether this proxy displays that it supports Forge/FML
|
* Get the proxy favicon shown in the tablist
|
||||||
* @return forge announce enabled
|
*
|
||||||
*/
|
* @return optional favicon
|
||||||
boolean isAnnounceForge();
|
*/
|
||||||
|
Optional<Favicon> getFavicon();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get how long this proxy will wait until performing a read timeout
|
* Get whether this proxy displays that it supports Forge/FML
|
||||||
* @return connection timeout (in milliseconds)
|
*
|
||||||
*/
|
* @return forge announce enabled
|
||||||
int getConnectTimeout();
|
*/
|
||||||
|
boolean isAnnounceForge();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get how long this proxy will wait until performing a read timeout
|
* Get how long this proxy will wait until performing a read timeout
|
||||||
* @return read timeout (in milliseconds)
|
*
|
||||||
*/
|
* @return connection timeout (in milliseconds)
|
||||||
int getReadTimeout();
|
*/
|
||||||
|
int getConnectTimeout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get how long this proxy will wait until performing a read timeout
|
||||||
|
*
|
||||||
|
* @return read timeout (in milliseconds)
|
||||||
|
*/
|
||||||
|
int getReadTimeout();
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,11 @@ package com.velocitypowered.api.proxy.messages;
|
|||||||
* Represents a kind of channel identifier.
|
* Represents a kind of channel identifier.
|
||||||
*/
|
*/
|
||||||
public interface ChannelIdentifier {
|
public interface ChannelIdentifier {
|
||||||
/**
|
|
||||||
* Returns the textual representation of this identifier.
|
/**
|
||||||
* @return the textual representation of the identifier
|
* Returns the textual representation of this identifier.
|
||||||
*/
|
*
|
||||||
String getId();
|
* @return the textual representation of the identifier
|
||||||
|
*/
|
||||||
|
String getId();
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,13 @@ package com.velocitypowered.api.proxy.messages;
|
|||||||
* Represents something that can send plugin messages.
|
* Represents something that can send plugin messages.
|
||||||
*/
|
*/
|
||||||
public interface ChannelMessageSink {
|
public interface ChannelMessageSink {
|
||||||
/**
|
|
||||||
* Sends a plugin message to this target.
|
/**
|
||||||
* @param identifier the channel identifier to send the message on
|
* Sends a plugin message to this target.
|
||||||
* @param data the data to send
|
*
|
||||||
* @return whether or not the message could be sent
|
* @param identifier the channel identifier to send the message on
|
||||||
*/
|
* @param data the data to send
|
||||||
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data);
|
* @return whether or not the message could be sent
|
||||||
|
*/
|
||||||
|
boolean sendPluginMessage(ChannelIdentifier identifier, byte[] data);
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,5 @@ package com.velocitypowered.api.proxy.messages;
|
|||||||
* A marker interface that indicates a source of plugin messages.
|
* A marker interface that indicates a source of plugin messages.
|
||||||
*/
|
*/
|
||||||
public interface ChannelMessageSource {
|
public interface ChannelMessageSource {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,22 @@
|
|||||||
package com.velocitypowered.api.proxy.messages;
|
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 {
|
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.
|
* Registers the specified message identifiers to listen on for the
|
||||||
* @param identifiers the identifiers to unregister
|
*
|
||||||
*/
|
* @param identifiers the channel identifiers to register
|
||||||
void unregister(ChannelIdentifier... identifiers);
|
*/
|
||||||
|
void register(ChannelIdentifier... identifiers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the handler for the specified channel.
|
||||||
|
*
|
||||||
|
* @param identifiers the identifiers to unregister
|
||||||
|
*/
|
||||||
|
void unregister(ChannelIdentifier... identifiers);
|
||||||
}
|
}
|
||||||
|
@ -2,46 +2,51 @@ package com.velocitypowered.api.proxy.messages;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import java.util.Objects;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
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
|
* Reperesents a legacy channel identifier (for Minecraft 1.12 and below). For modern 1.13 plugin
|
||||||
* {@link MinecraftChannelIdentifier}. This class is immutable and safe for multi-threaded use.
|
* messages, please see {@link MinecraftChannelIdentifier}. This class is immutable and safe for
|
||||||
|
* multi-threaded use.
|
||||||
*/
|
*/
|
||||||
public final class LegacyChannelIdentifier implements ChannelIdentifier {
|
public final class LegacyChannelIdentifier implements ChannelIdentifier {
|
||||||
private final String name;
|
|
||||||
|
|
||||||
public LegacyChannelIdentifier(String name) {
|
private final String name;
|
||||||
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "provided name is empty");
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
public LegacyChannelIdentifier(String name) {
|
||||||
return name;
|
Preconditions.checkArgument(!Strings.isNullOrEmpty(name), "provided name is empty");
|
||||||
}
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public String getName() {
|
||||||
public String toString() {
|
return name;
|
||||||
return name + " (legacy)";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object o) {
|
public String toString() {
|
||||||
if (this == o) return true;
|
return name + " (legacy)";
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
}
|
||||||
LegacyChannelIdentifier that = (LegacyChannelIdentifier) o;
|
|
||||||
return Objects.equals(name, that.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public boolean equals(@Nullable Object o) {
|
||||||
return Objects.hash(name);
|
if (this == o) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LegacyChannelIdentifier that = (LegacyChannelIdentifier) o;
|
||||||
|
return Objects.equals(name, that.name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public int hashCode() {
|
||||||
return this.getName();
|
return Objects.hash(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return this.getName();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,78 +2,87 @@ package com.velocitypowered.api.proxy.messages;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
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 {
|
public final class MinecraftChannelIdentifier implements ChannelIdentifier {
|
||||||
private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9\\-_]+");
|
|
||||||
|
|
||||||
private final String namespace;
|
private static final Pattern VALID_IDENTIFIER_REGEX = Pattern.compile("[a-z0-9\\-_]+");
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private MinecraftChannelIdentifier(String namespace, String name) {
|
private final String namespace;
|
||||||
this.namespace = namespace;
|
private final String name;
|
||||||
this.name = 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;
|
||||||
}
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
/**
|
return false;
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
MinecraftChannelIdentifier that = (MinecraftChannelIdentifier) o;
|
||||||
|
return Objects.equals(namespace, that.namespace) &&
|
||||||
|
Objects.equals(name, that.name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Creates an identifier in the specified namespace.
|
public int hashCode() {
|
||||||
* @param namespace the namespace to use
|
return Objects.hash(namespace, name);
|
||||||
* @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() {
|
@Override
|
||||||
return namespace;
|
public String getId() {
|
||||||
}
|
return namespace + ":" + name;
|
||||||
|
}
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
package com.velocitypowered.api.proxy.messages;
|
@ -6,51 +6,58 @@ import java.util.Locale;
|
|||||||
* Represents the client settings for the player.
|
* Represents the client settings for the player.
|
||||||
*/
|
*/
|
||||||
public interface PlayerSettings {
|
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
|
* Returns the locale of the Minecraft client.
|
||||||
* servers are responsible for sending the chunks.
|
*
|
||||||
* @return the client view distance
|
* @return the client locale
|
||||||
*/
|
*/
|
||||||
byte getViewDistance();
|
Locale getLocale();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the chat setting for the client.
|
* Returns the client's view distance. This does not guarantee the client will see this many
|
||||||
* @return the chat setting
|
* chunks, since your servers are responsible for sending the chunks.
|
||||||
*/
|
*
|
||||||
ChatMode getChatMode();
|
* @return the client view distance
|
||||||
|
*/
|
||||||
|
byte getViewDistance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether or not the client has chat colors disabled.
|
* Returns the chat setting for the client.
|
||||||
* @return whether or not the client has chat colors disabled
|
*
|
||||||
*/
|
* @return the chat setting
|
||||||
boolean hasChatColors();
|
*/
|
||||||
|
ChatMode getChatMode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parts of player skins the client will show.
|
* Returns whether or not the client has chat colors disabled.
|
||||||
* @return the skin parts for the client
|
*
|
||||||
*/
|
* @return whether or not the client has chat colors disabled
|
||||||
SkinParts getSkinParts();
|
*/
|
||||||
|
boolean hasChatColors();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the primary hand of the client.
|
* Returns the parts of player skins the client will show.
|
||||||
* @return the primary hand of the client
|
*
|
||||||
*/
|
* @return the skin parts for the client
|
||||||
MainHand getMainHand();
|
*/
|
||||||
|
SkinParts getSkinParts();
|
||||||
|
|
||||||
enum ChatMode {
|
/**
|
||||||
SHOWN,
|
* Returns the primary hand of the client.
|
||||||
COMMANDS_ONLY,
|
*
|
||||||
HIDDEN
|
* @return the primary hand of the client
|
||||||
}
|
*/
|
||||||
|
MainHand getMainHand();
|
||||||
|
|
||||||
enum MainHand {
|
enum ChatMode {
|
||||||
LEFT,
|
SHOWN,
|
||||||
RIGHT
|
COMMANDS_ONLY,
|
||||||
}
|
HIDDEN
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MainHand {
|
||||||
|
LEFT,
|
||||||
|
RIGHT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,37 +1,38 @@
|
|||||||
package com.velocitypowered.api.proxy.player;
|
package com.velocitypowered.api.proxy.player;
|
||||||
|
|
||||||
public final class SkinParts {
|
public final class SkinParts {
|
||||||
private final byte bitmask;
|
|
||||||
|
|
||||||
public SkinParts(byte skinBitmask) {
|
private final byte bitmask;
|
||||||
this.bitmask = skinBitmask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasCape() {
|
public SkinParts(byte skinBitmask) {
|
||||||
return (bitmask & 1) == 1;
|
this.bitmask = skinBitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasJacket() {
|
public boolean hasCape() {
|
||||||
return ((bitmask >> 1) & 1) == 1;
|
return (bitmask & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasLeftSleeve() {
|
public boolean hasJacket() {
|
||||||
return ((bitmask >> 2) & 1) == 1;
|
return ((bitmask >> 1) & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRightSleeve() {
|
public boolean hasLeftSleeve() {
|
||||||
return ((bitmask >> 3) & 1) == 1;
|
return ((bitmask >> 2) & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasLeftPants() {
|
public boolean hasRightSleeve() {
|
||||||
return ((bitmask >> 4) & 1) == 1;
|
return ((bitmask >> 3) & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRightPants() {
|
public boolean hasLeftPants() {
|
||||||
return ((bitmask >> 5) & 1) == 1;
|
return ((bitmask >> 4) & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasHat() {
|
public boolean hasRightPants() {
|
||||||
return ((bitmask >> 6) & 1) == 1;
|
return ((bitmask >> 5) & 1) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasHat() {
|
||||||
|
return ((bitmask >> 6) & 1) == 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,51 +2,56 @@ package com.velocitypowered.api.proxy.player;
|
|||||||
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
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.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import net.kyori.text.Component;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the tab list of a {@link Player}.
|
* Represents the tab list of a {@link Player}.
|
||||||
*/
|
*/
|
||||||
public interface TabList {
|
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.
|
* Sets the tab list header and footer for the player.
|
||||||
*/
|
*
|
||||||
void clearHeaderAndFooter();
|
* @param header the header component
|
||||||
|
* @param footer the footer component
|
||||||
/**
|
*/
|
||||||
* Adds a {@link TabListEntry} to the {@link Player}'s tab list.
|
void setHeaderAndFooter(Component header, Component footer);
|
||||||
* @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
|
* Clears the tab list header and footer for the player.
|
||||||
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency, int gameMode);
|
*/
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -1,177 +1,195 @@
|
|||||||
package com.velocitypowered.api.proxy.player;
|
package com.velocitypowered.api.proxy.player;
|
||||||
|
|
||||||
import com.velocitypowered.api.util.GameProfile;
|
import com.velocitypowered.api.util.GameProfile;
|
||||||
|
import java.util.Optional;
|
||||||
import net.kyori.text.Component;
|
import net.kyori.text.Component;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single entry in a {@link TabList}.
|
* Represents a single entry in a {@link TabList}.
|
||||||
*/
|
*/
|
||||||
public interface TabListEntry {
|
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() {}
|
/**
|
||||||
|
* Returns the parent {@link TabList} of this {@code this} {@link TabListEntry}.
|
||||||
/**
|
*
|
||||||
* Sets the parent {@link TabList} for this entry,
|
* @return parent {@link TabList}
|
||||||
* the entry will only be able to be added to that specific {@link TabList}.
|
*/
|
||||||
* @param tabList to set
|
TabList getTabList();
|
||||||
* @return {@code this}, for chaining
|
|
||||||
*/
|
/**
|
||||||
public Builder tabList(TabList tabList) {
|
* Returns the {@link GameProfile} of the entry, which uniquely identifies the entry with the
|
||||||
this.tabList = tabList;
|
* containing {@link java.util.UUID}, as well as deciding what is shown as the player head in the
|
||||||
return this;
|
* tab list.
|
||||||
}
|
*
|
||||||
|
* @return {@link GameProfile} of the entry
|
||||||
/**
|
*/
|
||||||
* Sets the {@link GameProfile} of the {@link TabListEntry}.
|
GameProfile getProfile();
|
||||||
* @see TabListEntry#getProfile()
|
|
||||||
* @param profile to set
|
/**
|
||||||
* @return {@code this}, for chaining
|
* 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.
|
||||||
public Builder profile(GameProfile profile) {
|
*
|
||||||
this.profile = profile;
|
* @return {@link Optional} text {@link Component} of name displayed in the tab list
|
||||||
return this;
|
*/
|
||||||
}
|
Optional<Component> getDisplayName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the displayed name of the {@link TabListEntry}
|
* Sets the text {@link Component} to be displayed for {@code this} {@link TabListEntry}. If
|
||||||
* @see TabListEntry#getDisplayName()
|
* {@code null}, {@link GameProfile#getName()} will be shown.
|
||||||
* @param displayName to set
|
*
|
||||||
* @return {@code this}, for chaining
|
* @param displayName to show in the {@link TabList} for {@code this} entry
|
||||||
*/
|
* @return {@code this}, for chaining
|
||||||
public Builder displayName(@Nullable Component displayName) {
|
*/
|
||||||
this.displayName = displayName;
|
TabListEntry setDisplayName(@Nullable Component displayName);
|
||||||
return this;
|
|
||||||
}
|
/**
|
||||||
|
* 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>
|
||||||
* Sets the latency of the {@link TabListEntry}
|
* <ul>
|
||||||
* @see TabListEntry#getLatency()
|
* <li>A negative latency will display the no connection icon</li>
|
||||||
* @param latency to set
|
* <li>0-150 will display 5 bars</li>
|
||||||
* @return {@code this}, for chaining
|
* <li>150-300 will display 4 bars</li>
|
||||||
*/
|
* <li>300-600 will display 3 bars</li>
|
||||||
public Builder latency(int latency) {
|
* <li>600-1000 will display 2 bars</li>
|
||||||
this.latency = latency;
|
* <li>A latency move than 1 second will display 1 bar</li>
|
||||||
return this;
|
* <li></li>
|
||||||
}
|
* </ul>
|
||||||
|
*
|
||||||
/**
|
* @return latency set for {@code this} entry
|
||||||
* Sets the game mode of the {@link TabListEntry}
|
*/
|
||||||
* @see TabListEntry#getGameMode()
|
int getLatency();
|
||||||
* @param gameMode to set
|
|
||||||
* @return {@code this}, for chaining
|
/**
|
||||||
*/
|
* Sets the latency for {@code this} entry to the specified value
|
||||||
public Builder gameMode(int gameMode) {
|
*
|
||||||
this.gameMode = gameMode;
|
* @param latency to changed to
|
||||||
return this;
|
* @return {@code this}, for chaining
|
||||||
}
|
* @see #getLatency()
|
||||||
|
*/
|
||||||
/**
|
TabListEntry setLatency(int latency);
|
||||||
* Constructs the {@link TabListEntry} specified by {@code this} {@link Builder}.
|
|
||||||
* @return the constructed {@link TabListEntry}
|
/**
|
||||||
*/
|
* Gets the game mode {@code this} entry has been set to.
|
||||||
public TabListEntry build() {
|
* <p>The number corresponds to the game mode in the following way:</p>
|
||||||
if (tabList == null) {
|
* <ol start="0">
|
||||||
throw new IllegalStateException("The Tablist must be set when building a TabListEntry");
|
* <li>Survival</li>
|
||||||
}
|
* <li>Creative</li>
|
||||||
if (profile == null) {
|
* <li>Adventure</li>
|
||||||
throw new IllegalStateException("The GameProfile must be set when building a TabListEntry");
|
* <li>Spectator</li>
|
||||||
}
|
* </ol>
|
||||||
return tabList.buildEntry(profile, displayName, latency, gameMode);
|
*
|
||||||
}
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,301 +3,323 @@ package com.velocitypowered.api.proxy.server;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
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.
|
* GS4 query response. This class is immutable.
|
||||||
*/
|
*/
|
||||||
public final class QueryResponse {
|
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) {
|
private final String hostname;
|
||||||
this.hostname = hostname;
|
private final String gameVersion;
|
||||||
this.gameVersion = gameVersion;
|
private final String map;
|
||||||
this.map = map;
|
private final int currentPlayers;
|
||||||
this.currentPlayers = currentPlayers;
|
private final int maxPlayers;
|
||||||
this.maxPlayers = maxPlayers;
|
private final String proxyHost;
|
||||||
this.proxyHost = proxyHost;
|
private final int proxyPort;
|
||||||
this.proxyPort = proxyPort;
|
private final Collection<String> players;
|
||||||
this.players = players;
|
private final String proxyVersion;
|
||||||
this.proxyVersion = proxyVersion;
|
private final Collection<PluginInformation> plugins;
|
||||||
this.plugins = 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.
|
* Builds new {@link QueryResponse} with supplied data
|
||||||
* @return hostname
|
*
|
||||||
|
* @return response
|
||||||
*/
|
*/
|
||||||
public String getHostname() {
|
public QueryResponse build() {
|
||||||
return hostname;
|
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() {
|
||||||
* Get game version which will be used to reply to the query. By default supported Minecraft versions range is sent.
|
return name;
|
||||||
* @return game version
|
|
||||||
*/
|
|
||||||
public String getGameVersion() {
|
|
||||||
return gameVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setName(String name) {
|
||||||
* Get map name which will be used to reply to the query. By default {@link ProxyConfig#getQueryMap()} is sent.
|
this.name = name;
|
||||||
* @return map name
|
|
||||||
*/
|
|
||||||
public String getMap() {
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setVersion(@Nullable String version) {
|
||||||
* Get current online player count which will be used to reply to the query.
|
this.version = version;
|
||||||
* @return online player count
|
|
||||||
*/
|
|
||||||
public int getCurrentPlayers() {
|
|
||||||
return currentPlayers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Nullable
|
||||||
* Get max player count which will be used to reply to the query.
|
public String getVersion() {
|
||||||
* @return max player count
|
return version;
|
||||||
*/
|
|
||||||
public int getMaxPlayers() {
|
|
||||||
return maxPlayers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static PluginInformation of(String name, @Nullable String version) {
|
||||||
* Get proxy (public facing) hostname
|
return new PluginInformation(name, version);
|
||||||
* @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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.api.proxy.server;
|
|||||||
|
|
||||||
import com.velocitypowered.api.proxy.Player;
|
import com.velocitypowered.api.proxy.Player;
|
||||||
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@ -10,21 +9,25 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
* Represents a server that has been registered with the proxy.
|
* Represents a server that has been registered with the proxy.
|
||||||
*/
|
*/
|
||||||
public interface RegisteredServer extends ChannelMessageSink {
|
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.
|
* Returns the {@link ServerInfo} for this server.
|
||||||
* @return the players on this proxy
|
*
|
||||||
*/
|
* @return the server info
|
||||||
Collection<Player> getPlayersConnected();
|
*/
|
||||||
|
ServerInfo getServerInfo();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to ping the remote server and return the server list ping result.
|
* Returns a list of all the players currently connected to this server on this proxy.
|
||||||
* @return the server ping result from the server
|
*
|
||||||
*/
|
* @return the players on this proxy
|
||||||
CompletableFuture<ServerPing> ping();
|
*/
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,61 @@
|
|||||||
package com.velocitypowered.api.proxy.server;
|
package com.velocitypowered.api.proxy.server;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Objects;
|
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 {
|
public final class ServerInfo {
|
||||||
private final String name;
|
|
||||||
private final InetSocketAddress address;
|
|
||||||
|
|
||||||
/**
|
private final String name;
|
||||||
* Creates a new ServerInfo object.
|
private final InetSocketAddress address;
|
||||||
* @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 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() {
|
public final String getName() {
|
||||||
return address;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public final InetSocketAddress getAddress() {
|
||||||
public String toString() {
|
return address;
|
||||||
return "ServerInfo{" +
|
}
|
||||||
"name='" + name + '\'' +
|
|
||||||
", address=" + address +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean equals(@Nullable Object o) {
|
public String toString() {
|
||||||
if (this == o) return true;
|
return "ServerInfo{" +
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
"name='" + name + '\'' +
|
||||||
ServerInfo that = (ServerInfo) o;
|
", address=" + address +
|
||||||
return Objects.equals(name, that.name) &&
|
'}';
|
||||||
Objects.equals(address, that.address);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int hashCode() {
|
public final boolean equals(@Nullable Object o) {
|
||||||
return Objects.hash(name, address);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,308 +4,319 @@ import com.google.common.base.Preconditions;
|
|||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.velocitypowered.api.util.Favicon;
|
import com.velocitypowered.api.util.Favicon;
|
||||||
import com.velocitypowered.api.util.ModInfo;
|
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 net.kyori.text.Component;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a 1.7 and above server list ping response. This class is immutable.
|
* Represents a 1.7 and above server list ping response. This class is immutable.
|
||||||
*/
|
*/
|
||||||
public final class ServerPing {
|
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) {
|
private final Version version;
|
||||||
this(version, players, description, favicon, ModInfo.DEFAULT);
|
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) {
|
public Builder version(Version version) {
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
this.version = Preconditions.checkNotNull(version, "version");
|
||||||
this.players = players;
|
return this;
|
||||||
this.description = Preconditions.checkNotNull(description, "description");
|
}
|
||||||
this.favicon = favicon;
|
|
||||||
this.modinfo = modinfo;
|
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() {
|
public Version getVersion() {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Players> getPlayers() {
|
public int getOnlinePlayers() {
|
||||||
return Optional.ofNullable(players);
|
return onlinePlayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Component getDescription() {
|
public int getMaximumPlayers() {
|
||||||
return description;
|
return maximumPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SamplePlayer> getSamplePlayers() {
|
||||||
|
return samplePlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Component> getDescription() {
|
||||||
|
return Optional.ofNullable(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Favicon> getFavicon() {
|
public Optional<Favicon> getFavicon() {
|
||||||
return Optional.ofNullable(favicon);
|
return Optional.ofNullable(favicon);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<ModInfo> getModinfo() {
|
public String getModType() {
|
||||||
return Optional.ofNullable(modinfo);
|
return modType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ModInfo.Mod> getMods() {
|
||||||
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ServerPing{" +
|
return "Builder{" +
|
||||||
"version=" + version +
|
"version=" + version +
|
||||||
", players=" + players +
|
", onlinePlayers=" + onlinePlayers +
|
||||||
", description=" + description +
|
", maximumPlayers=" + maximumPlayers +
|
||||||
", favicon='" + favicon + '\'' +
|
", 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() {
|
public int getProtocol() {
|
||||||
Builder builder = new Builder();
|
return protocol;
|
||||||
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() {
|
public String getName() {
|
||||||
return new Builder();
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* A builder for {@link ServerPing} objects.
|
public String toString() {
|
||||||
*/
|
return "Version{" +
|
||||||
public static final class Builder {
|
"protocol=" + protocol +
|
||||||
private Version version = new Version(0, "Unknown");
|
", name='" + name + '\'' +
|
||||||
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 static final class Players {
|
||||||
|
|
||||||
}
|
private final int online;
|
||||||
|
private final int max;
|
||||||
|
private final List<SamplePlayer> sample;
|
||||||
|
|
||||||
public Builder version(Version version) {
|
public Players(int online, int max, List<SamplePlayer> sample) {
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
this.online = online;
|
||||||
return this;
|
this.max = max;
|
||||||
}
|
this.sample = ImmutableList.copyOf(sample);
|
||||||
|
|
||||||
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 static final class Version {
|
public int getOnline() {
|
||||||
private final int protocol;
|
return online;
|
||||||
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 static final class Players {
|
public int getMax() {
|
||||||
private final int online;
|
return max;
|
||||||
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 static final class SamplePlayer {
|
public List<SamplePlayer> getSample() {
|
||||||
private final String name;
|
return sample;
|
||||||
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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,24 @@ package com.velocitypowered.api.scheduler;
|
|||||||
* Represents a task that is scheduled to run on the proxy.
|
* Represents a task that is scheduled to run on the proxy.
|
||||||
*/
|
*/
|
||||||
public interface ScheduledTask {
|
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.
|
* Returns the plugin that scheduled this task.
|
||||||
* @return the current status of this task
|
*
|
||||||
*/
|
* @return the plugin that scheduled this task
|
||||||
TaskStatus status();
|
*/
|
||||||
|
Object plugin();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels this task. If the task is already running, the thread in which it is running will be interrupted.
|
* Returns the current status of this task.
|
||||||
* If the task is not currently running, Velocity will terminate it safely.
|
*
|
||||||
*/
|
* @return the current status of this task
|
||||||
void cancel();
|
*/
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,65 @@
|
|||||||
package com.velocitypowered.api.scheduler;
|
package com.velocitypowered.api.scheduler;
|
||||||
|
|
||||||
import org.checkerframework.common.value.qual.IntRange;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import org.checkerframework.common.value.qual.IntRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a scheduler to execute tasks on the proxy.
|
* Represents a scheduler to execute tasks on the proxy.
|
||||||
*/
|
*/
|
||||||
public interface Scheduler {
|
public interface Scheduler {
|
||||||
/**
|
|
||||||
* Initializes a new {@link TaskBuilder} for creating a task on the proxy.
|
/**
|
||||||
* @param plugin the plugin to request the task for
|
* Initializes a new {@link TaskBuilder} for creating a task on the proxy.
|
||||||
* @param runnable the task to run when scheduled
|
*
|
||||||
* @return the task builder
|
* @param plugin the plugin to request the task for
|
||||||
*/
|
* @param runnable the task to run when scheduled
|
||||||
TaskBuilder buildTask(Object plugin, Runnable runnable);
|
* @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 {
|
TaskBuilder delay(@IntRange(from = 0) long time, TimeUnit unit);
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies that the task should continue running after waiting for the specified amount, until it is cancelled.
|
* Specifies that the task should continue running after waiting for the specified amount, until
|
||||||
* @param time the time to delay by
|
* it is cancelled.
|
||||||
* @param unit the unit of time for {@code time}
|
*
|
||||||
* @return this builder, for chaining
|
* @param time the time to delay by
|
||||||
*/
|
* @param unit the unit of time for {@code time}
|
||||||
TaskBuilder repeat(@IntRange(from = 0) long time, TimeUnit unit);
|
* @return this builder, for chaining
|
||||||
|
*/
|
||||||
|
TaskBuilder repeat(@IntRange(from = 0) long time, TimeUnit unit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the delay on this task.
|
* Clears the delay on this task.
|
||||||
* @return this builder, for chaining
|
*
|
||||||
*/
|
* @return this builder, for chaining
|
||||||
TaskBuilder clearDelay();
|
*/
|
||||||
|
TaskBuilder clearDelay();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the repeat interval on this task.
|
* Clears the repeat interval on this task.
|
||||||
* @return this builder, for chaining
|
*
|
||||||
*/
|
* @return this builder, for chaining
|
||||||
TaskBuilder clearRepeat();
|
*/
|
||||||
|
TaskBuilder clearRepeat();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules this task for execution.
|
* Schedules this task for execution.
|
||||||
* @return the scheduled task
|
*
|
||||||
*/
|
* @return the scheduled task
|
||||||
ScheduledTask schedule();
|
*/
|
||||||
}
|
ScheduledTask schedule();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package com.velocitypowered.api.scheduler;
|
package com.velocitypowered.api.scheduler;
|
||||||
|
|
||||||
public enum TaskStatus {
|
public enum TaskStatus {
|
||||||
/**
|
/**
|
||||||
* The task is scheduled and is currently running.
|
* The task is scheduled and is currently running.
|
||||||
*/
|
*/
|
||||||
SCHEDULED,
|
SCHEDULED,
|
||||||
/**
|
/**
|
||||||
* The task was cancelled with {@link ScheduledTask#cancel()}.
|
* The task was cancelled with {@link ScheduledTask#cancel()}.
|
||||||
*/
|
*/
|
||||||
CANCELLED,
|
CANCELLED,
|
||||||
/**
|
/**
|
||||||
* The task has run to completion. This is applicable only for tasks without a repeat.
|
* The task has run to completion. This is applicable only for tasks without a repeat.
|
||||||
*/
|
*/
|
||||||
FINISHED
|
FINISHED
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
package com.velocitypowered.api.util;
|
package com.velocitypowered.api.util;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -12,78 +9,92 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Objects;
|
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
|
* Represents a Minecraft server favicon. A Minecraft server favicon is a 64x64 image that can be
|
||||||
* client that sends a Server List Ping packet, and is automatically displayed in the Minecraft client.
|
* displayed to a remote client that sends a Server List Ping packet, and is automatically displayed
|
||||||
|
* in the Minecraft client.
|
||||||
*/
|
*/
|
||||||
public final class Favicon {
|
public final class Favicon {
|
||||||
private final String base64Url;
|
|
||||||
|
|
||||||
/**
|
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Base64-encoded URI for this image.
|
* Directly create a favicon using its Base64 URL directly. You are generally better served by the
|
||||||
* @return a URL representing this favicon
|
* create() series of functions.
|
||||||
*/
|
*
|
||||||
public String getBase64Url() {
|
* @param base64Url the url for use with this favicon
|
||||||
return base64Url;
|
*/
|
||||||
}
|
public Favicon(String base64Url) {
|
||||||
|
this.base64Url = Preconditions.checkNotNull(base64Url, "base64Url");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean equals(@Nullable Object o) {
|
* Returns the Base64-encoded URI for this image.
|
||||||
if (this == o) return true;
|
*
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
* @return a URL representing this favicon
|
||||||
Favicon favicon = (Favicon) o;
|
*/
|
||||||
return Objects.equals(base64Url, favicon.base64Url);
|
public String getBase64Url() {
|
||||||
}
|
return base64Url;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public boolean equals(@Nullable Object o) {
|
||||||
return Objects.hash(base64Url);
|
if (this == o) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Favicon favicon = (Favicon) o;
|
||||||
|
return Objects.equals(base64Url, favicon.base64Url);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public int hashCode() {
|
||||||
return "Favicon{" +
|
return Objects.hash(base64Url);
|
||||||
"base64Url='" + base64Url + '\'' +
|
}
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Creates a new {@code Favicon} from the specified {@code image}.
|
public String toString() {
|
||||||
* @param image the image to use for the favicon
|
return "Favicon{" +
|
||||||
* @return the created {@link Favicon} instance
|
"base64Url='" + base64Url + '\'' +
|
||||||
*/
|
'}';
|
||||||
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}.
|
* Creates a new {@code Favicon} from the specified {@code image}.
|
||||||
* @param path the path to the image to create a favicon for
|
*
|
||||||
* @return the created {@link Favicon} instance
|
* @param image the image to use for the favicon
|
||||||
* @throws IOException if the file could not be read from the path
|
* @return the created {@link Favicon} instance
|
||||||
*/
|
*/
|
||||||
public static Favicon create(Path path) throws IOException {
|
public static Favicon create(BufferedImage image) {
|
||||||
try (InputStream stream = Files.newInputStream(path)) {
|
Preconditions.checkNotNull(image, "image");
|
||||||
return create(ImageIO.read(stream));
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -10,82 +9,85 @@ import java.util.UUID;
|
|||||||
* Represents a Mojang game profile. This class is immutable.
|
* Represents a Mojang game profile. This class is immutable.
|
||||||
*/
|
*/
|
||||||
public final class GameProfile {
|
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 String name;
|
||||||
private final List<Property> properties;
|
private final String value;
|
||||||
|
private final String signature;
|
||||||
|
|
||||||
public GameProfile(String id, String name, List<Property> properties) {
|
public Property(String name, String value, String signature) {
|
||||||
this.id = Preconditions.checkNotNull(id, "id");
|
this.name = Preconditions.checkNotNull(name, "name");
|
||||||
this.name = Preconditions.checkNotNull(name, "name");
|
this.value = Preconditions.checkNotNull(value, "value");
|
||||||
this.properties = ImmutableList.copyOf(properties);
|
this.signature = Preconditions.checkNotNull(signature, "signature");
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID idAsUuid() {
|
|
||||||
return UuidUtils.fromUndashed(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Property> getProperties() {
|
public String getValue() {
|
||||||
return properties;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public String getSignature() {
|
||||||
* Creates a game profile suitable for use in offline-mode.
|
return signature;
|
||||||
* @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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GameProfile{" +
|
return "Property{" +
|
||||||
"id='" + id + '\'' +
|
"name='" + name + '\'' +
|
||||||
", name='" + name + '\'' +
|
", value='" + value + '\'' +
|
||||||
", properties=" + properties +
|
", signature='" + signature + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
|
||||||
|
|
||||||
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 + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,17 +4,18 @@ package com.velocitypowered.api.util;
|
|||||||
* Represents where a chat message is going to be sent.
|
* Represents where a chat message is going to be sent.
|
||||||
*/
|
*/
|
||||||
public enum MessagePosition {
|
public enum MessagePosition {
|
||||||
/**
|
/**
|
||||||
* The chat message will appear in the client's HUD. These messages can be filtered out by the client.
|
* The chat message will appear in the client's HUD. These messages can be filtered out by the
|
||||||
*/
|
* client.
|
||||||
CHAT,
|
*/
|
||||||
/**
|
CHAT,
|
||||||
* The chat message will appear in the client's HUD and can't be dismissed.
|
/**
|
||||||
*/
|
* The chat message will appear in the client's HUD and can't be dismissed.
|
||||||
SYSTEM,
|
*/
|
||||||
/**
|
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.
|
* 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
|
*/
|
||||||
|
ACTION_BAR
|
||||||
}
|
}
|
||||||
|
@ -2,59 +2,60 @@ package com.velocitypowered.api.util;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class ModInfo {
|
public final class ModInfo {
|
||||||
public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of());
|
|
||||||
|
public static final ModInfo DEFAULT = new ModInfo("FML", ImmutableList.of());
|
||||||
private final String type;
|
|
||||||
private final List<Mod> modList;
|
private final String type;
|
||||||
|
private final List<Mod> modList;
|
||||||
public ModInfo(String type, List<Mod> modList) {
|
|
||||||
this.type = Preconditions.checkNotNull(type, "type");
|
public ModInfo(String type, List<Mod> modList) {
|
||||||
this.modList = ImmutableList.copyOf(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() {
|
public String getId() {
|
||||||
return type;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Mod> getMods() {
|
public String getVersion() {
|
||||||
return modList;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ModInfo{" +
|
return "Mod{" +
|
||||||
"type='" + type + '\'' +
|
"id='" + id + '\'' +
|
||||||
", modList=" + modList +
|
", version='" + version + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
|
||||||
|
|
||||||
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 + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,57 +1,61 @@
|
|||||||
package com.velocitypowered.api.util;
|
package com.velocitypowered.api.util;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a version object for the proxy.
|
* Provides a version object for the proxy.
|
||||||
*/
|
*/
|
||||||
public final class ProxyVersion {
|
public final class ProxyVersion {
|
||||||
private final String name;
|
|
||||||
private final String vendor;
|
|
||||||
private final String version;
|
|
||||||
|
|
||||||
public ProxyVersion(String name, String vendor, String version) {
|
private final String name;
|
||||||
this.name = Preconditions.checkNotNull(name, "name");
|
private final String vendor;
|
||||||
this.vendor = Preconditions.checkNotNull(vendor, "vendor");
|
private final String version;
|
||||||
this.version = Preconditions.checkNotNull(version, "version");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
public ProxyVersion(String name, String vendor, String version) {
|
||||||
return name;
|
this.name = Preconditions.checkNotNull(name, "name");
|
||||||
}
|
this.vendor = Preconditions.checkNotNull(vendor, "vendor");
|
||||||
|
this.version = Preconditions.checkNotNull(version, "version");
|
||||||
|
}
|
||||||
|
|
||||||
public String getVendor() {
|
public String getName() {
|
||||||
return vendor;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getVersion() {
|
public String getVendor() {
|
||||||
return version;
|
return vendor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public String getVersion() {
|
||||||
public boolean equals(@Nullable Object o) {
|
return version;
|
||||||
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
|
@Override
|
||||||
public int hashCode() {
|
public boolean equals(@Nullable Object o) {
|
||||||
return Objects.hash(name, vendor, version);
|
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
|
@Override
|
||||||
public String toString() {
|
public int hashCode() {
|
||||||
return "ProxyVersion{" +
|
return Objects.hash(name, vendor, version);
|
||||||
"name='" + name + '\'' +
|
}
|
||||||
", vendor='" + vendor + '\'' +
|
|
||||||
", version='" + version + '\'' +
|
@Override
|
||||||
'}';
|
public String toString() {
|
||||||
}
|
return "ProxyVersion{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", vendor='" + vendor + '\'' +
|
||||||
|
", version='" + version + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.velocitypowered.api.util;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -11,41 +10,45 @@ import java.util.UUID;
|
|||||||
* Provides a small, useful selection of utilities for working with Minecraft UUIDs.
|
* Provides a small, useful selection of utilities for working with Minecraft UUIDs.
|
||||||
*/
|
*/
|
||||||
public final class UuidUtils {
|
public final class UuidUtils {
|
||||||
private UuidUtils() {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
private UuidUtils() {
|
||||||
* Converts from an undashed Mojang-style UUID into a Java {@link UUID} object.
|
throw new AssertionError();
|
||||||
* @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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts from a Java {@link UUID} object into an undashed Mojang-style UUID.
|
* Converts from an undashed Mojang-style UUID into a Java {@link UUID} object.
|
||||||
* @param uuid the UUID to convert
|
*
|
||||||
* @return the undashed UUID
|
* @param string the string to convert
|
||||||
*/
|
* @return the UUID object
|
||||||
public static String toUndashed(final UUID uuid) {
|
*/
|
||||||
Preconditions.checkNotNull(uuid, "uuid");
|
public static UUID fromUndashed(final String string) {
|
||||||
return Strings.padStart(Long.toHexString(uuid.getMostSignificantBits()), 16, '0') +
|
Objects.requireNonNull(string, "string");
|
||||||
Strings.padStart(Long.toHexString(uuid.getLeastSignificantBits()), 16, '0');
|
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.
|
* Converts from a Java {@link UUID} object into an undashed Mojang-style UUID.
|
||||||
* @param username the username to use
|
*
|
||||||
* @return the offline mode UUID
|
* @param uuid the UUID to convert
|
||||||
*/
|
* @return the undashed UUID
|
||||||
public static UUID generateOfflinePlayerUuid(String username) {
|
*/
|
||||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,236 +1,251 @@
|
|||||||
package com.velocitypowered.api.util.title;
|
package com.velocitypowered.api.util.title;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
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.Objects;
|
||||||
import java.util.Optional;
|
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.
|
* Represents a "full" title, including all components. This class is immutable.
|
||||||
*/
|
*/
|
||||||
public final class TextTitle implements Title {
|
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) {
|
private final @Nullable Component title;
|
||||||
this.title = builder.title;
|
private final @Nullable Component subtitle;
|
||||||
this.subtitle = builder.subtitle;
|
private final int stay;
|
||||||
this.stay = builder.stay;
|
private final int fadeIn;
|
||||||
this.fadeIn = builder.fadeIn;
|
private final int fadeOut;
|
||||||
this.fadeOut = builder.fadeOut;
|
private final boolean resetBeforeSend;
|
||||||
this.resetBeforeSend = builder.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() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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() {
|
public boolean isResetBeforeSend() {
|
||||||
return resetBeforeSend;
|
return resetBeforeSend;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public TextTitle build() {
|
||||||
* Determines whether or not this title has times set on it. If none are set, it will update the previous title
|
return new TextTitle(this);
|
||||||
* 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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "TextTitle{" +
|
return "Builder{" +
|
||||||
"title=" + title +
|
"title=" + title +
|
||||||
", subtitle=" + subtitle +
|
", subtitle=" + subtitle +
|
||||||
", stay=" + stay +
|
", stay=" + stay +
|
||||||
", fadeIn=" + fadeIn +
|
", fadeIn=" + fadeIn +
|
||||||
", fadeOut=" + fadeOut +
|
", fadeOut=" + fadeOut +
|
||||||
", resetBeforeSend=" + resetBeforeSend +
|
", 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 +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,5 @@ package com.velocitypowered.api.util.title;
|
|||||||
* Represents a title that can be sent to a Minecraft client.
|
* Represents a title that can be sent to a Minecraft client.
|
||||||
*/
|
*/
|
||||||
public interface Title {
|
public interface Title {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,47 +4,51 @@ package com.velocitypowered.api.util.title;
|
|||||||
* Provides special-purpose titles.
|
* Provides special-purpose titles.
|
||||||
*/
|
*/
|
||||||
public final class 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() {
|
private static final Title HIDE = new Title() {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "reset title";
|
return "hide 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;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a title that, when sent to the client, will cause any existing title to be hidden. The title may be
|
* Returns a title that, when sent to the client, will cause all title data to be reset and any
|
||||||
* restored by a {@link TextTitle} with no title or subtitle (only a time).
|
* existing title to be hidden.
|
||||||
* @return the hide title
|
*
|
||||||
*/
|
* @return the reset title
|
||||||
public static Title hide() {
|
*/
|
||||||
return HIDE;
|
public static Title reset() {
|
||||||
}
|
return RESET;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a builder for {@link TextTitle}s.
|
* Returns a title that, when sent to the client, will cause any existing title to be hidden. The
|
||||||
* @return a builder for text titles
|
* title may be restored by a {@link TextTitle} with no title or subtitle (only a time).
|
||||||
*/
|
*
|
||||||
public static TextTitle.Builder text() {
|
* @return the hide title
|
||||||
return TextTitle.builder();
|
*/
|
||||||
}
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,67 +1,81 @@
|
|||||||
package com.velocitypowered.api.util;
|
package com.velocitypowered.api.util;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
|
|
||||||
class UuidUtilsTest {
|
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 UUID EXPECTED_DASHED_UUID = UUID
|
||||||
private static final String ISSUE_109_ZERO_UUID_UNDASHED = "00000000000000000000000000000000";
|
.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 UUID ISSUE_109_ZERO_UUID = new UUID(0, 0);
|
||||||
private static final String ISSUE_109_ONE_LSB_UUID_UNDASHED = "00000000000000000000000000000001";
|
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 UUID ISSUE_109_ONE_LSB_UUID = new UUID(0, 1);
|
||||||
private static final String ISSUE_109_ONE_MLSB_UUID_UNDASHED = "00000000000000010000000000000001";
|
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 UUID ISSUE_109_ONE_MLSB_UUID = new UUID(1, 1);
|
||||||
private static final String ISSUE_109_LEADING_ZERO_UNDASHED = "0d470a25041648a1b7a62a27aa5eb251";
|
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 UUID ISSUE_109_LEADING_ZERO_UUID = UUID
|
||||||
private static final String TEST_OFFLINE_PLAYER = "tuxed";
|
.fromString("0d470a25-0416-48a1-b7a6-2a27aa5eb251");
|
||||||
|
private static final String ISSUE_109_LEADING_ZERO_UNDASHED = "0d470a25041648a1b7a62a27aa5eb251";
|
||||||
|
|
||||||
@Test
|
private static final UUID TEST_OFFLINE_PLAYER_UUID = UUID
|
||||||
void generateOfflinePlayerUuid() {
|
.fromString("708f6260-183d-3912-bbde-5e279a5e739a");
|
||||||
assertEquals(TEST_OFFLINE_PLAYER_UUID, UuidUtils.generateOfflinePlayerUuid(TEST_OFFLINE_PLAYER), "UUIDs do not match");
|
private static final String TEST_OFFLINE_PLAYER = "tuxed";
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void fromUndashed() {
|
void generateOfflinePlayerUuid() {
|
||||||
assertEquals(EXPECTED_DASHED_UUID, UuidUtils.fromUndashed(ACTUAL_UNDASHED_UUID), "UUIDs do not match");
|
assertEquals(TEST_OFFLINE_PLAYER_UUID, UuidUtils.generateOfflinePlayerUuid(TEST_OFFLINE_PLAYER),
|
||||||
}
|
"UUIDs do not match");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void toUndashed() {
|
void fromUndashed() {
|
||||||
assertEquals(ACTUAL_UNDASHED_UUID, UuidUtils.toUndashed(EXPECTED_DASHED_UUID), "UUIDs do not match");
|
assertEquals(EXPECTED_DASHED_UUID, UuidUtils.fromUndashed(ACTUAL_UNDASHED_UUID),
|
||||||
}
|
"UUIDs do not match");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void zeroUuidIssue109() {
|
void toUndashed() {
|
||||||
assertEquals(ISSUE_109_ZERO_UUID, UuidUtils.fromUndashed(ISSUE_109_ZERO_UUID_UNDASHED), "UUIDs do not match");
|
assertEquals(ACTUAL_UNDASHED_UUID, UuidUtils.toUndashed(EXPECTED_DASHED_UUID),
|
||||||
assertEquals(ISSUE_109_ZERO_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ZERO_UUID), "UUIDs do not match");
|
"UUIDs do not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void leadingZeroUuidIssue109() {
|
void zeroUuidIssue109() {
|
||||||
assertEquals(ISSUE_109_LEADING_ZERO_UUID, UuidUtils.fromUndashed(ISSUE_109_LEADING_ZERO_UNDASHED), "UUIDs do not match");
|
assertEquals(ISSUE_109_ZERO_UUID, UuidUtils.fromUndashed(ISSUE_109_ZERO_UUID_UNDASHED),
|
||||||
assertEquals(ISSUE_109_LEADING_ZERO_UNDASHED, UuidUtils.toUndashed(ISSUE_109_LEADING_ZERO_UUID), "UUIDs do not match");
|
"UUIDs do not match");
|
||||||
}
|
assertEquals(ISSUE_109_ZERO_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ZERO_UUID),
|
||||||
|
"UUIDs do not match");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void oneUuidLsbIssue109() {
|
void leadingZeroUuidIssue109() {
|
||||||
assertEquals(ISSUE_109_ONE_LSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_LSB_UUID_UNDASHED), "UUIDs do not match");
|
assertEquals(ISSUE_109_LEADING_ZERO_UUID,
|
||||||
assertEquals(ISSUE_109_ONE_LSB_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ONE_LSB_UUID), "UUIDs do not match");
|
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
|
@Test
|
||||||
void oneUuidMsbAndLsbIssue109() {
|
void oneUuidLsbIssue109() {
|
||||||
assertEquals(ISSUE_109_ONE_MLSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_MLSB_UUID_UNDASHED), "UUIDs do not match");
|
assertEquals(ISSUE_109_ONE_LSB_UUID, UuidUtils.fromUndashed(ISSUE_109_ONE_LSB_UUID_UNDASHED),
|
||||||
assertEquals(ISSUE_109_ONE_MLSB_UUID_UNDASHED, UuidUtils.toUndashed(ISSUE_109_ONE_MLSB_UUID), "UUIDs do not match");
|
"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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
256
config/checkstyle/checkstyle.xml
Normale Datei
256
config/checkstyle/checkstyle.xml
Normale Datei
@ -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>
|
@ -25,7 +25,7 @@ dependencies {
|
|||||||
implementation "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
|
implementation "org.checkerframework:checker-qual:${checkerFrameworkVersion}"
|
||||||
} else if (System.getenv("CHECKERFRAMEWORK") == null) {
|
} else if (System.getenv("CHECKERFRAMEWORK") == null) {
|
||||||
throw new GradleException("Environment variable CHECKERFRAMEWORK is not set")
|
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"));
|
throw new GradleException("Environment variable CHECKERFRAMEWORK is set to non-existent directory " + System.getenv("CHECKERFRAMEWORK"));
|
||||||
} else {
|
} else {
|
||||||
ext.checkerframeworkdist = "$System.env.CHECKERFRAMEWORK/checker/dist"
|
ext.checkerframeworkdist = "$System.env.CHECKERFRAMEWORK/checker/dist"
|
||||||
|
4
gradle/checkstyle.gradle
Normale Datei
4
gradle/checkstyle.gradle
Normale Datei
@ -0,0 +1,4 @@
|
|||||||
|
checkstyle {
|
||||||
|
toolVersion '8.14'
|
||||||
|
configFile new File(project.rootDir, ['config', 'checkstyle', 'checkstyle.xml'].join(File.separator))
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
id 'java'
|
||||||
|
id 'checkstyle'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: '../gradle/checkerframework.gradle'
|
apply from: '../gradle/checkerframework.gradle'
|
||||||
|
apply from: '../gradle/checkstyle.gradle'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "com.google.guava:guava:${guavaVersion}"
|
compile "com.google.guava:guava:${guavaVersion}"
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package com.velocitypowered.natives;
|
package com.velocitypowered.natives;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This marker interface indicates that this object should be explicitly disposed before the object can no longer be used.
|
* This marker interface indicates that this object should be explicitly disposed before the object
|
||||||
* Not disposing these objects will likely leak native resources and eventually lead to resource exhaustion.
|
* can no longer be used. Not disposing these objects will likely leak native resources and
|
||||||
|
* eventually lead to resource exhaustion.
|
||||||
*/
|
*/
|
||||||
public interface Disposable {
|
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.
|
* 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
|
||||||
void dispose();
|
* disposed.
|
||||||
|
*/
|
||||||
|
void dispose();
|
||||||
}
|
}
|
||||||
|
@ -2,62 +2,62 @@ package com.velocitypowered.natives.compression;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
public class JavaVelocityCompressor implements VelocityCompressor {
|
public class JavaVelocityCompressor implements VelocityCompressor {
|
||||||
public static final VelocityCompressorFactory FACTORY = JavaVelocityCompressor::new;
|
|
||||||
|
|
||||||
private final Deflater deflater;
|
public static final VelocityCompressorFactory FACTORY = JavaVelocityCompressor::new;
|
||||||
private final Inflater inflater;
|
|
||||||
private final byte[] buf;
|
|
||||||
private boolean disposed = false;
|
|
||||||
|
|
||||||
private JavaVelocityCompressor(int level) {
|
private final Deflater deflater;
|
||||||
this.deflater = new Deflater(level);
|
private final Inflater inflater;
|
||||||
this.inflater = new Inflater();
|
private final byte[] buf;
|
||||||
this.buf = new byte[ZLIB_BUFFER_SIZE];
|
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
|
@Override
|
||||||
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
||||||
ensureNotDisposed();
|
ensureNotDisposed();
|
||||||
|
|
||||||
byte[] inData = new byte[source.readableBytes()];
|
byte[] inData = new byte[source.readableBytes()];
|
||||||
source.readBytes(inData);
|
source.readBytes(inData);
|
||||||
inflater.setInput(inData);
|
deflater.setInput(inData);
|
||||||
while (!inflater.finished()) {
|
deflater.finish();
|
||||||
int read = inflater.inflate(buf);
|
while (!deflater.finished()) {
|
||||||
destination.writeBytes(buf, 0, read);
|
int bytes = deflater.deflate(buf);
|
||||||
}
|
destination.writeBytes(buf, 0, bytes);
|
||||||
inflater.reset();
|
|
||||||
}
|
}
|
||||||
|
deflater.reset();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
public void dispose() {
|
||||||
ensureNotDisposed();
|
disposed = true;
|
||||||
|
deflater.end();
|
||||||
|
inflater.end();
|
||||||
|
}
|
||||||
|
|
||||||
byte[] inData = new byte[source.readableBytes()];
|
private void ensureNotDisposed() {
|
||||||
source.readBytes(inData);
|
Preconditions.checkState(!disposed, "Object already disposed");
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,75 +2,78 @@ package com.velocitypowered.natives.compression;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
|
|
||||||
public class NativeVelocityCompressor implements VelocityCompressor {
|
public class NativeVelocityCompressor implements VelocityCompressor {
|
||||||
public static final VelocityCompressorFactory FACTORY = NativeVelocityCompressor::new;
|
|
||||||
|
|
||||||
private final NativeZlibInflate inflate = new NativeZlibInflate();
|
public static final VelocityCompressorFactory FACTORY = NativeVelocityCompressor::new;
|
||||||
private final long inflateCtx;
|
|
||||||
private final NativeZlibDeflate deflate = new NativeZlibDeflate();
|
|
||||||
private final long deflateCtx;
|
|
||||||
private boolean disposed = false;
|
|
||||||
|
|
||||||
private NativeVelocityCompressor(int level) {
|
private final NativeZlibInflate inflate = new NativeZlibInflate();
|
||||||
this.inflateCtx = inflate.init();
|
private final long inflateCtx;
|
||||||
this.deflateCtx = deflate.init(level);
|
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
|
inflate.reset(inflateCtx);
|
||||||
public void inflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
inflate.consumed = 0;
|
||||||
ensureNotDisposed();
|
inflate.finished = false;
|
||||||
source.memoryAddress();
|
}
|
||||||
destination.memoryAddress();
|
|
||||||
|
|
||||||
while (!inflate.finished && source.isReadable()) {
|
@Override
|
||||||
if (!destination.isWritable()) {
|
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
||||||
destination.ensureWritable(ZLIB_BUFFER_SIZE);
|
ensureNotDisposed();
|
||||||
}
|
source.memoryAddress();
|
||||||
int produced = inflate.process(inflateCtx, source.memoryAddress() + source.readerIndex(), source.readableBytes(),
|
destination.memoryAddress();
|
||||||
destination.memoryAddress() + destination.writerIndex(), destination.writableBytes());
|
|
||||||
source.readerIndex(source.readerIndex() + inflate.consumed);
|
|
||||||
destination.writerIndex(destination.writerIndex() + produced);
|
|
||||||
}
|
|
||||||
|
|
||||||
inflate.reset(inflateCtx);
|
while (!deflate.finished) {
|
||||||
inflate.consumed = 0;
|
if (!destination.isWritable()) {
|
||||||
inflate.finished = false;
|
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
|
deflate.reset(deflateCtx);
|
||||||
public void deflate(ByteBuf source, ByteBuf destination) throws DataFormatException {
|
deflate.consumed = 0;
|
||||||
ensureNotDisposed();
|
deflate.finished = false;
|
||||||
source.memoryAddress();
|
}
|
||||||
destination.memoryAddress();
|
|
||||||
|
|
||||||
while (!deflate.finished) {
|
private void ensureNotDisposed() {
|
||||||
if (!destination.isWritable()) {
|
Preconditions.checkState(!disposed, "Object already disposed");
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
deflate.reset(deflateCtx);
|
@Override
|
||||||
deflate.consumed = 0;
|
public void dispose() {
|
||||||
deflate.finished = false;
|
if (!disposed) {
|
||||||
}
|
inflate.free(inflateCtx);
|
||||||
|
deflate.free(deflateCtx);
|
||||||
private void ensureNotDisposed() {
|
|
||||||
Preconditions.checkState(!disposed, "Object already disposed");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispose() {
|
|
||||||
if (!disposed) {
|
|
||||||
inflate.free(inflateCtx);
|
|
||||||
deflate.free(deflateCtx);
|
|
||||||
}
|
|
||||||
disposed = true;
|
|
||||||
}
|
}
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,21 +4,23 @@ package com.velocitypowered.natives.compression;
|
|||||||
* Represents a native interface for zlib's deflate functions.
|
* Represents a native interface for zlib's deflate functions.
|
||||||
*/
|
*/
|
||||||
class NativeZlibDeflate {
|
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,
|
native long free(long ctx);
|
||||||
boolean flush);
|
|
||||||
|
|
||||||
native void reset(long ctx);
|
native int process(long ctx, long sourceAddress, int sourceLength, long destinationAddress,
|
||||||
|
int destinationLength,
|
||||||
|
boolean flush);
|
||||||
|
|
||||||
static {
|
native void reset(long ctx);
|
||||||
initIDs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native void initIDs();
|
static {
|
||||||
|
initIDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void initIDs();
|
||||||
}
|
}
|
||||||
|
@ -4,20 +4,22 @@ package com.velocitypowered.natives.compression;
|
|||||||
* Represents a native interface for zlib's inflate functions.
|
* Represents a native interface for zlib's inflate functions.
|
||||||
*/
|
*/
|
||||||
class NativeZlibInflate {
|
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 {
|
native void reset(long ctx);
|
||||||
initIDs();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native void initIDs();
|
static {
|
||||||
|
initIDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void initIDs();
|
||||||
}
|
}
|
||||||
|
@ -2,19 +2,19 @@ package com.velocitypowered.natives.compression;
|
|||||||
|
|
||||||
import com.velocitypowered.natives.Disposable;
|
import com.velocitypowered.natives.Disposable;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an interface to inflate and deflate {@link ByteBuf}s using zlib.
|
* Provides an interface to inflate and deflate {@link ByteBuf}s using zlib.
|
||||||
*/
|
*/
|
||||||
public interface VelocityCompressor extends Disposable {
|
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;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.velocitypowered.natives.compression;
|
package com.velocitypowered.natives.compression;
|
||||||
|
|
||||||
public interface VelocityCompressorFactory {
|
public interface VelocityCompressorFactory {
|
||||||
VelocityCompressor create(int level);
|
|
||||||
|
VelocityCompressor create(int level);
|
||||||
}
|
}
|
||||||
|
@ -2,53 +2,54 @@ package com.velocitypowered.natives.encryption;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
public class JavaVelocityCipher implements VelocityCipher {
|
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 static final VelocityCipherFactory FACTORY = new VelocityCipherFactory() {
|
||||||
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
|
@Override
|
||||||
return new JavaVelocityCipher(false, key);
|
public VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException {
|
||||||
}
|
return new JavaVelocityCipher(true, 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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
|
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
|
||||||
ensureNotDisposed();
|
return new JavaVelocityCipher(false, key);
|
||||||
|
|
||||||
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
|
private final Cipher cipher;
|
||||||
public void dispose() {
|
private boolean disposed = false;
|
||||||
disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureNotDisposed() {
|
private JavaVelocityCipher(boolean encrypt, SecretKey key) throws GeneralSecurityException {
|
||||||
Preconditions.checkState(!disposed, "Object already disposed");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package com.velocitypowered.natives.encryption;
|
package com.velocitypowered.natives.encryption;
|
||||||
|
|
||||||
public class MbedtlsAesImpl {
|
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);
|
||||||
}
|
}
|
||||||
|
@ -1,55 +1,55 @@
|
|||||||
package com.velocitypowered.natives.encryption;
|
package com.velocitypowered.natives.encryption;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
import java.security.GeneralSecurityException;
|
|
||||||
|
|
||||||
public class NativeVelocityCipher implements VelocityCipher {
|
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 static final VelocityCipherFactory FACTORY = new VelocityCipherFactory() {
|
||||||
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
|
@Override
|
||||||
return new NativeVelocityCipher(false, key);
|
public VelocityCipher forEncryption(SecretKey key) throws GeneralSecurityException {
|
||||||
}
|
return new NativeVelocityCipher(true, 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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(ByteBuf source, ByteBuf destination) throws ShortBufferException {
|
public VelocityCipher forDecryption(SecretKey key) throws GeneralSecurityException {
|
||||||
source.memoryAddress();
|
return new NativeVelocityCipher(false, key);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
private static final MbedtlsAesImpl impl = new MbedtlsAesImpl();
|
||||||
|
|
||||||
@Override
|
private final long ctx;
|
||||||
public void dispose() {
|
private final boolean encrypt;
|
||||||
if (!disposed) {
|
private boolean disposed = false;
|
||||||
impl.free(ctx);
|
|
||||||
}
|
private NativeVelocityCipher(boolean encrypt, SecretKey key) {
|
||||||
disposed = true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ package com.velocitypowered.natives.encryption;
|
|||||||
|
|
||||||
import com.velocitypowered.natives.Disposable;
|
import com.velocitypowered.natives.Disposable;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
|
|
||||||
public interface VelocityCipher extends Disposable {
|
public interface VelocityCipher extends Disposable {
|
||||||
void process(ByteBuf source, ByteBuf destination) throws ShortBufferException;
|
|
||||||
|
void process(ByteBuf source, ByteBuf destination) throws ShortBufferException;
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package com.velocitypowered.natives.encryption;
|
package com.velocitypowered.natives.encryption;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
public interface VelocityCipherFactory {
|
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;
|
||||||
}
|
}
|
||||||
|
@ -1,81 +1,85 @@
|
|||||||
package com.velocitypowered.natives.util;
|
package com.velocitypowered.natives.util;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
public final class NativeCodeLoader<T> implements Supplier<T> {
|
public final class NativeCodeLoader<T> implements Supplier<T> {
|
||||||
private final Variant<T> selected;
|
|
||||||
|
|
||||||
NativeCodeLoader(List<Variant<T>> variants) {
|
private final Variant<T> selected;
|
||||||
this.selected = getVariant(variants);
|
|
||||||
|
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 @Nullable T get() {
|
||||||
public T get() {
|
if (status == Status.NOT_AVAILABLE || status == Status.SETUP_FAILURE) {
|
||||||
return selected.object;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Variant<T> getVariant(List<Variant<T>> variants) {
|
// Make sure setup happens only once
|
||||||
for (Variant<T> variant : variants) {
|
if (status == Status.POSSIBLY_AVAILABLE) {
|
||||||
T got = variant.get();
|
try {
|
||||||
if (got == null) {
|
setup.run();
|
||||||
continue;
|
status = Status.SETUP;
|
||||||
}
|
} catch (Exception e) {
|
||||||
return variant;
|
status = Status.SETUP_FAILURE;
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Can't find any suitable variants");
|
}
|
||||||
|
|
||||||
|
return object;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getLoadedVariant() {
|
private enum Status {
|
||||||
return selected.name;
|
NOT_AVAILABLE,
|
||||||
}
|
POSSIBLY_AVAILABLE,
|
||||||
|
SETUP,
|
||||||
|
SETUP_FAILURE
|
||||||
|
}
|
||||||
|
|
||||||
static class Variant<T> {
|
static final BooleanSupplier MACOS = () ->
|
||||||
private Status status;
|
System.getProperty("os.name", "").equalsIgnoreCase("Mac OS X") &&
|
||||||
private final Runnable setup;
|
System.getProperty("os.arch").equals("x86_64");
|
||||||
private final String name;
|
static final BooleanSupplier LINUX = () ->
|
||||||
private final T object;
|
System.getProperties().getProperty("os.name", "").equalsIgnoreCase("Linux") &&
|
||||||
|
System.getProperty("os.arch").equals("amd64");
|
||||||
Variant(BooleanSupplier possiblyAvailable, Runnable setup, String name, T object) {
|
static final BooleanSupplier ALWAYS = () -> true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import com.velocitypowered.natives.compression.NativeVelocityCompressor;
|
|||||||
import com.velocitypowered.natives.compression.VelocityCompressorFactory;
|
import com.velocitypowered.natives.compression.VelocityCompressorFactory;
|
||||||
import com.velocitypowered.natives.encryption.JavaVelocityCipher;
|
import com.velocitypowered.natives.encryption.JavaVelocityCipher;
|
||||||
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
|
import com.velocitypowered.natives.encryption.VelocityCipherFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -14,55 +13,58 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
|
||||||
public class Natives {
|
public class Natives {
|
||||||
private Natives() {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Runnable copyAndLoadNative(String path) {
|
private Natives() {
|
||||||
return () -> {
|
throw new AssertionError();
|
||||||
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.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
private static Runnable copyAndLoadNative(String path) {
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
return () -> {
|
||||||
try {
|
try {
|
||||||
Files.deleteIfExists(tempFile);
|
Path tempFile = Files.createTempFile("native-", path.substring(path.lastIndexOf('.')));
|
||||||
} catch (IOException ignored) {
|
InputStream nativeLib = Natives.class.getResourceAsStream(path);
|
||||||
// Well, it doesn't matter...
|
if (nativeLib == null) {
|
||||||
}
|
throw new IllegalStateException("Native library " + path + " not found.");
|
||||||
}));
|
}
|
||||||
System.load(tempFile.toAbsolutePath().toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final NativeCodeLoader<VelocityCompressorFactory> compressor = new NativeCodeLoader<>(
|
Files.copy(nativeLib, tempFile, StandardCopyOption.REPLACE_EXISTING);
|
||||||
ImmutableList.of(
|
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||||
new NativeCodeLoader.Variant<>(NativeCodeLoader.MACOS,
|
try {
|
||||||
copyAndLoadNative("/macosx/velocity-compress.dylib"), "native (macOS)",
|
Files.deleteIfExists(tempFile);
|
||||||
NativeVelocityCompressor.FACTORY),
|
} catch (IOException ignored) {
|
||||||
new NativeCodeLoader.Variant<>(NativeCodeLoader.LINUX,
|
// Well, it doesn't matter...
|
||||||
copyAndLoadNative("/linux_x64/velocity-compress.so"), "native (Linux amd64)",
|
}
|
||||||
NativeVelocityCompressor.FACTORY),
|
}));
|
||||||
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {}, "Java", JavaVelocityCompressor.FACTORY)
|
System.load(tempFile.toAbsolutePath().toString());
|
||||||
)
|
} catch (IOException e) {
|
||||||
);
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static final NativeCodeLoader<VelocityCipherFactory> cipher = new NativeCodeLoader<>(
|
public static final NativeCodeLoader<VelocityCompressorFactory> compressor = new NativeCodeLoader<>(
|
||||||
ImmutableList.of(
|
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,
|
/*new NativeCodeLoader.Variant<>(NativeCodeLoader.MACOS,
|
||||||
copyAndLoadNative("/macosx/velocity-cipher.dylib"), "mbed TLS (macOS)",
|
copyAndLoadNative("/macosx/velocity-cipher.dylib"), "mbed TLS (macOS)",
|
||||||
NativeVelocityCipher.FACTORY),
|
NativeVelocityCipher.FACTORY),
|
||||||
new NativeCodeLoader.Variant<>(NativeCodeLoader.LINUX,
|
new NativeCodeLoader.Variant<>(NativeCodeLoader.LINUX,
|
||||||
copyAndLoadNative("/linux_x64/velocity-cipher.so"), "mbed TLS (Linux amd64)",
|
copyAndLoadNative("/linux_x64/velocity-cipher.so"), "mbed TLS (Linux amd64)",
|
||||||
NativeVelocityCipher.FACTORY),*/
|
NativeVelocityCipher.FACTORY),*/
|
||||||
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {}, "Java", JavaVelocityCipher.FACTORY)
|
new NativeCodeLoader.Variant<>(NativeCodeLoader.ALWAYS, () -> {
|
||||||
)
|
}, "Java", JavaVelocityCipher.FACTORY)
|
||||||
);
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,65 +1,66 @@
|
|||||||
package com.velocitypowered.natives.compression;
|
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.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
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.LINUX;
|
||||||
import static org.junit.jupiter.api.condition.OS.MAC;
|
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 {
|
class VelocityCompressorTest {
|
||||||
@BeforeAll
|
|
||||||
static void checkNatives() {
|
@BeforeAll
|
||||||
Natives.compressor.getLoadedVariant();
|
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
|
@Test
|
||||||
@EnabledOnOs({ MAC, LINUX })
|
void javaIntegrityCheck() throws DataFormatException {
|
||||||
void nativeIntegrityCheck() throws DataFormatException {
|
VelocityCompressor compressor = JavaVelocityCompressor.FACTORY
|
||||||
VelocityCompressor compressor = Natives.compressor.get().create(Deflater.DEFAULT_COMPRESSION);
|
.create(Deflater.DEFAULT_COMPRESSION);
|
||||||
if (compressor instanceof JavaVelocityCompressor) {
|
check(compressor);
|
||||||
compressor.dispose();
|
}
|
||||||
fail("Loaded regular compressor");
|
|
||||||
}
|
private void check(VelocityCompressor compressor) throws DataFormatException {
|
||||||
check(compressor);
|
ByteBuf source = Unpooled.directBuffer();
|
||||||
}
|
ByteBuf dest = Unpooled.directBuffer();
|
||||||
|
ByteBuf decompressed = Unpooled.directBuffer();
|
||||||
@Test
|
|
||||||
void javaIntegrityCheck() throws DataFormatException {
|
Random random = new Random(1);
|
||||||
VelocityCompressor compressor = JavaVelocityCompressor.FACTORY.create(Deflater.DEFAULT_COMPRESSION);
|
byte[] randomBytes = new byte[1 << 16];
|
||||||
check(compressor);
|
random.nextBytes(randomBytes);
|
||||||
}
|
source.writeBytes(randomBytes);
|
||||||
|
|
||||||
private void check(VelocityCompressor compressor) throws DataFormatException {
|
try {
|
||||||
ByteBuf source = Unpooled.directBuffer();
|
compressor.deflate(source, dest);
|
||||||
ByteBuf dest = Unpooled.directBuffer();
|
compressor.inflate(dest, decompressed);
|
||||||
ByteBuf decompressed = Unpooled.directBuffer();
|
source.readerIndex(0);
|
||||||
|
assertTrue(ByteBufUtil.equals(source, decompressed));
|
||||||
Random random = new Random(1);
|
} finally {
|
||||||
byte[] randomBytes = new byte[1 << 16];
|
source.release();
|
||||||
random.nextBytes(randomBytes);
|
dest.release();
|
||||||
source.writeBytes(randomBytes);
|
decompressed.release();
|
||||||
|
compressor.dispose();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,71 +1,71 @@
|
|||||||
package com.velocitypowered.natives.encryption;
|
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 com.velocitypowered.natives.util.Natives;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
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.BeforeAll;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
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 {
|
class VelocityCipherTest {
|
||||||
private static final int ENCRYPT_DATA_SIZE = 1 << 16;
|
|
||||||
|
|
||||||
@BeforeAll
|
private static final int ENCRYPT_DATA_SIZE = 1 << 16;
|
||||||
static void checkNatives() {
|
|
||||||
Natives.cipher.getLoadedVariant();
|
@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
|
@Test
|
||||||
@Disabled
|
void javaIntegrityCheck() throws GeneralSecurityException {
|
||||||
void nativeIntegrityCheck() throws GeneralSecurityException {
|
check(JavaVelocityCipher.FACTORY);
|
||||||
VelocityCipherFactory factory = Natives.cipher.get();
|
}
|
||||||
if (factory == JavaVelocityCipher.FACTORY) {
|
|
||||||
fail("Loaded regular compressor");
|
private void check(VelocityCipherFactory factory) throws GeneralSecurityException {
|
||||||
}
|
// Generate a random 16-byte key.
|
||||||
check(factory);
|
Random random = new Random(1);
|
||||||
}
|
byte[] key = new byte[16];
|
||||||
|
random.nextBytes(key);
|
||||||
@Test
|
|
||||||
void javaIntegrityCheck() throws GeneralSecurityException {
|
VelocityCipher decrypt = factory.forDecryption(new SecretKeySpec(key, "AES"));
|
||||||
check(JavaVelocityCipher.FACTORY);
|
VelocityCipher encrypt = factory.forEncryption(new SecretKeySpec(key, "AES"));
|
||||||
}
|
|
||||||
|
ByteBuf source = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
|
||||||
private void check(VelocityCipherFactory factory) throws GeneralSecurityException {
|
ByteBuf dest = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
|
||||||
// Generate a random 16-byte key.
|
ByteBuf decryptionBuf = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
|
||||||
Random random = new Random(1);
|
|
||||||
byte[] key = new byte[16];
|
byte[] randomBytes = new byte[ENCRYPT_DATA_SIZE];
|
||||||
random.nextBytes(key);
|
random.nextBytes(randomBytes);
|
||||||
|
source.writeBytes(randomBytes);
|
||||||
VelocityCipher decrypt = factory.forDecryption(new SecretKeySpec(key, "AES"));
|
|
||||||
VelocityCipher encrypt = factory.forEncryption(new SecretKeySpec(key, "AES"));
|
try {
|
||||||
|
encrypt.process(source, dest);
|
||||||
ByteBuf source = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
|
decrypt.process(dest, decryptionBuf);
|
||||||
ByteBuf dest = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
|
source.readerIndex(0);
|
||||||
ByteBuf decryptionBuf = Unpooled.directBuffer(ENCRYPT_DATA_SIZE);
|
assertTrue(ByteBufUtil.equals(source, decryptionBuf));
|
||||||
|
} finally {
|
||||||
byte[] randomBytes = new byte[ENCRYPT_DATA_SIZE];
|
source.release();
|
||||||
random.nextBytes(randomBytes);
|
dest.release();
|
||||||
source.writeBytes(randomBytes);
|
decryptionBuf.release();
|
||||||
|
decrypt.dispose();
|
||||||
try {
|
encrypt.dispose();
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,11 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
||||||
id 'de.sebastianboegl.shadow.transformer.log4j' version '2.1.1'
|
id 'de.sebastianboegl.shadow.transformer.log4j' version '2.1.1'
|
||||||
|
id 'checkstyle'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: '../gradle/checkerframework.gradle'
|
apply from: '../gradle/checkerframework.gradle'
|
||||||
|
apply from: '../gradle/checkstyle.gradle'
|
||||||
compileJava {
|
|
||||||
options.compilerArgs += ['-proc:none']
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestJava {
|
|
||||||
options.compilerArgs += ['-proc:none']
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
package com.velocitypowered.proxy;
|
package com.velocitypowered.proxy;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
|
|
||||||
public class Velocity {
|
public class Velocity {
|
||||||
private static final Logger logger = LogManager.getLogger(Velocity.class);
|
|
||||||
|
|
||||||
static {
|
private static final Logger logger = LogManager.getLogger(Velocity.class);
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String... args) {
|
static {
|
||||||
long startTime = System.currentTimeMillis();
|
// 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();
|
public static void main(String... args) {
|
||||||
server.start();
|
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;
|
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdown, "Shutdown thread"));
|
||||||
logger.info("Done ({}s)!", new DecimalFormat("#.##").format(bootTime));
|
|
||||||
server.getConsoleCommandSource().start();
|
double bootTime = (System.currentTimeMillis() - startTime) / 1000d;
|
||||||
}
|
logger.info("Done ({}s)!", new DecimalFormat("#.##").format(bootTime));
|
||||||
|
server.getConsoleCommandSource().start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import com.velocitypowered.proxy.config.AnnotatedConfig;
|
|||||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||||
import com.velocitypowered.proxy.console.VelocityConsole;
|
import com.velocitypowered.proxy.console.VelocityConsole;
|
||||||
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
|
|
||||||
import com.velocitypowered.proxy.network.ConnectionManager;
|
import com.velocitypowered.proxy.network.ConnectionManager;
|
||||||
import com.velocitypowered.proxy.network.http.NettyHttpClient;
|
import com.velocitypowered.proxy.network.http.NettyHttpClient;
|
||||||
import com.velocitypowered.proxy.plugin.VelocityEventManager;
|
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.AddressUtil;
|
||||||
import com.velocitypowered.proxy.util.EncryptionUtils;
|
import com.velocitypowered.proxy.util.EncryptionUtils;
|
||||||
import com.velocitypowered.proxy.util.Ratelimiter;
|
import com.velocitypowered.proxy.util.Ratelimiter;
|
||||||
|
import com.velocitypowered.proxy.util.VelocityChannelRegistrar;
|
||||||
import io.netty.bootstrap.Bootstrap;
|
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.net.InetSocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.KeyPair;
|
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.ConcurrentHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
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 {
|
public class VelocityServer implements ProxyServer {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(VelocityServer.class);
|
private static final Logger logger = LogManager.getLogger(VelocityServer.class);
|
||||||
public static final Gson GSON = new GsonBuilder()
|
public static final Gson GSON = new GsonBuilder()
|
||||||
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
|
.registerTypeHierarchyAdapter(Component.class, new GsonComponentSerializer())
|
||||||
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
|
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
private @MonotonicNonNull ConnectionManager cm;
|
private @MonotonicNonNull ConnectionManager cm;
|
||||||
private @MonotonicNonNull VelocityConfiguration configuration;
|
private @MonotonicNonNull VelocityConfiguration configuration;
|
||||||
private @MonotonicNonNull NettyHttpClient httpClient;
|
private @MonotonicNonNull NettyHttpClient httpClient;
|
||||||
private @MonotonicNonNull KeyPair serverKeyPair;
|
private @MonotonicNonNull KeyPair serverKeyPair;
|
||||||
private @MonotonicNonNull ServerMap servers;
|
private @MonotonicNonNull ServerMap servers;
|
||||||
private final VelocityCommandManager commandManager = new VelocityCommandManager();
|
private final VelocityCommandManager commandManager = new VelocityCommandManager();
|
||||||
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
private final AtomicBoolean shutdownInProgress = new AtomicBoolean(false);
|
||||||
private boolean shutdown = false;
|
private boolean shutdown = false;
|
||||||
private @MonotonicNonNull VelocityPluginManager pluginManager;
|
private @MonotonicNonNull VelocityPluginManager pluginManager;
|
||||||
|
|
||||||
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
private final Map<UUID, ConnectedPlayer> connectionsByUuid = new ConcurrentHashMap<>();
|
||||||
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
private final Map<String, ConnectedPlayer> connectionsByName = new ConcurrentHashMap<>();
|
||||||
private @MonotonicNonNull VelocityConsole console;
|
private @MonotonicNonNull VelocityConsole console;
|
||||||
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
|
private @MonotonicNonNull Ratelimiter ipAttemptLimiter;
|
||||||
private @MonotonicNonNull VelocityEventManager eventManager;
|
private @MonotonicNonNull VelocityEventManager eventManager;
|
||||||
private @MonotonicNonNull VelocityScheduler scheduler;
|
private @MonotonicNonNull VelocityScheduler scheduler;
|
||||||
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
|
private final VelocityChannelRegistrar channelRegistrar = new VelocityChannelRegistrar();
|
||||||
|
|
||||||
public KeyPair getServerKeyPair() {
|
public KeyPair getServerKeyPair() {
|
||||||
if (serverKeyPair == null) {
|
if (serverKeyPair == null) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
}
|
}
|
||||||
return serverKeyPair;
|
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() {
|
return new ProxyVersion(implName, implVendor, implVersion);
|
||||||
VelocityConfiguration cfg = this.configuration;
|
}
|
||||||
if (cfg == null) {
|
|
||||||
throw new IllegalStateException("Configuration not initialized!");
|
@Override
|
||||||
}
|
public VelocityCommandManager getCommandManager() {
|
||||||
return cfg;
|
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
|
for (Map.Entry<String, String> entry : configuration.getServers().entrySet()) {
|
||||||
public ProxyVersion getVersion() {
|
servers.register(new ServerInfo(entry.getKey(), AddressUtil.parseAddress(entry.getValue())));
|
||||||
Package pkg = VelocityServer.class.getPackage();
|
}
|
||||||
String implName;
|
|
||||||
String implVersion;
|
ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit());
|
||||||
String implVendor;
|
httpClient = new NettyHttpClient(this);
|
||||||
if (pkg != null) {
|
loadPlugins();
|
||||||
implName = MoreObjects.firstNonNull(pkg.getImplementationTitle(), "Velocity");
|
|
||||||
implVersion = MoreObjects.firstNonNull(pkg.getImplementationVersion(), "<unknown>");
|
// Go ahead and fire the proxy initialization event. We block since plugins should have a chance
|
||||||
implVendor = MoreObjects.firstNonNull(pkg.getImplementationVendor(), "Velocity Contributors");
|
// to fully initialize before we accept any connections to the server.
|
||||||
} else {
|
eventManager.fire(new ProxyInitializeEvent()).join();
|
||||||
implName = "Velocity";
|
|
||||||
implVersion = "<unknown>";
|
// init console permissions after plugins are loaded
|
||||||
implVendor = "Velocity Contributors";
|
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
|
// Register the plugin main classes so that we may proceed with firing the proxy initialize event
|
||||||
public VelocityCommandManager getCommandManager() {
|
for (PluginContainer plugin : pluginManager.getPlugins()) {
|
||||||
return commandManager;
|
Optional<?> instance = plugin.getInstance();
|
||||||
|
if (instance.isPresent()) {
|
||||||
|
eventManager.register(instance.get(), instance.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnsuresNonNull({"serverKeyPair", "servers", "pluginManager", "eventManager", "scheduler", "console", "cm", "configuration"})
|
logger.info("Loaded {} plugins", pluginManager.getPlugins().size());
|
||||||
public void start() {
|
}
|
||||||
logger.info("Booting up {} {}...", getVersion().getName(), getVersion().getVersion());
|
|
||||||
|
|
||||||
serverKeyPair = EncryptionUtils.createRsaKeyPair(1024);
|
public Bootstrap initializeGenericBootstrap() {
|
||||||
pluginManager = new VelocityPluginManager(this);
|
if (cm == null) {
|
||||||
eventManager = new VelocityEventManager(pluginManager);
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
scheduler = new VelocityScheduler(pluginManager);
|
}
|
||||||
console = new VelocityConsole(this);
|
return this.cm.createWorker();
|
||||||
cm = new ConnectionManager(this);
|
}
|
||||||
servers = new ServerMap(this);
|
|
||||||
|
|
||||||
cm.logChannelInformation();
|
public boolean isShutdown() {
|
||||||
|
return shutdown;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize commands first
|
public void shutdown() {
|
||||||
commandManager.register(new VelocityCommand(this), "velocity");
|
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
|
||||||
commandManager.register(new ServerCommand(this), "server");
|
throw new AssertionError();
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresNonNull({"pluginManager", "eventManager"})
|
if (!shutdownInProgress.compareAndSet(false, true)) {
|
||||||
private void loadPlugins() {
|
return;
|
||||||
logger.info("Loading plugins...");
|
}
|
||||||
|
logger.info("Shutting down the proxy...");
|
||||||
|
|
||||||
try {
|
for (ConnectedPlayer player : ImmutableList.copyOf(connectionsByUuid.values())) {
|
||||||
Path pluginPath = Paths.get("plugins");
|
player.close(TextComponent.of("Proxy shutting down."));
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bootstrap initializeGenericBootstrap() {
|
this.cm.shutdown();
|
||||||
if (cm == null) {
|
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
eventManager.fire(new ProxyShutdownEvent());
|
||||||
}
|
try {
|
||||||
return this.cm.createWorker();
|
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() {
|
shutdown = true;
|
||||||
return shutdown;
|
}
|
||||||
|
|
||||||
|
public NettyHttpClient getHttpClient() {
|
||||||
|
if (httpClient == null) {
|
||||||
|
throw new IllegalStateException("HTTP client not initialized");
|
||||||
}
|
}
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public Ratelimiter getIpAttemptLimiter() {
|
||||||
if (eventManager == null || pluginManager == null || cm == null || scheduler == null) {
|
if (ipAttemptLimiter == null) {
|
||||||
throw new AssertionError();
|
throw new IllegalStateException("Ratelimiter not initialized");
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
return ipAttemptLimiter;
|
||||||
|
}
|
||||||
|
|
||||||
public NettyHttpClient getHttpClient() {
|
public boolean registerConnection(ConnectedPlayer connection) {
|
||||||
if (httpClient == null) {
|
String lowerName = connection.getUsername().toLowerCase(Locale.US);
|
||||||
throw new IllegalStateException("HTTP client not initialized");
|
if (connectionsByName.putIfAbsent(lowerName, connection) != null) {
|
||||||
}
|
return false;
|
||||||
return httpClient;
|
|
||||||
}
|
}
|
||||||
|
if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) {
|
||||||
|
connectionsByName.remove(lowerName, connection);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public Ratelimiter getIpAttemptLimiter() {
|
public void unregisterConnection(ConnectedPlayer connection) {
|
||||||
if (ipAttemptLimiter == null) {
|
connectionsByName.remove(connection.getUsername().toLowerCase(Locale.US), connection);
|
||||||
throw new IllegalStateException("Ratelimiter not initialized");
|
connectionsByUuid.remove(connection.getUniqueId(), connection);
|
||||||
}
|
}
|
||||||
return ipAttemptLimiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean registerConnection(ConnectedPlayer connection) {
|
@Override
|
||||||
String lowerName = connection.getUsername().toLowerCase(Locale.US);
|
public Optional<Player> getPlayer(String username) {
|
||||||
if (connectionsByName.putIfAbsent(lowerName, connection) != null) {
|
Preconditions.checkNotNull(username, "username");
|
||||||
return false;
|
return Optional.ofNullable(connectionsByName.get(username.toLowerCase(Locale.US)));
|
||||||
}
|
}
|
||||||
if (connectionsByUuid.putIfAbsent(connection.getUniqueId(), connection) != null) {
|
|
||||||
connectionsByName.remove(lowerName, connection);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterConnection(ConnectedPlayer connection) {
|
@Override
|
||||||
connectionsByName.remove(connection.getUsername().toLowerCase(Locale.US), connection);
|
public Optional<Player> getPlayer(UUID uuid) {
|
||||||
connectionsByUuid.remove(connection.getUniqueId(), connection);
|
Preconditions.checkNotNull(uuid, "uuid");
|
||||||
}
|
return Optional.ofNullable(connectionsByUuid.get(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Player> getPlayer(String username) {
|
public void broadcast(Component component) {
|
||||||
Preconditions.checkNotNull(username, "username");
|
Preconditions.checkNotNull(component, "component");
|
||||||
return Optional.ofNullable(connectionsByName.get(username.toLowerCase(Locale.US)));
|
Chat chat = Chat.createClientbound(component);
|
||||||
|
for (ConnectedPlayer player : connectionsByUuid.values()) {
|
||||||
|
player.getConnection().write(chat);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<Player> getPlayer(UUID uuid) {
|
public Collection<Player> getAllPlayers() {
|
||||||
Preconditions.checkNotNull(uuid, "uuid");
|
return ImmutableList.copyOf(connectionsByUuid.values());
|
||||||
return Optional.ofNullable(connectionsByUuid.get(uuid));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void broadcast(Component component) {
|
public int getPlayerCount() {
|
||||||
Preconditions.checkNotNull(component, "component");
|
return connectionsByUuid.size();
|
||||||
Chat chat = Chat.createClientbound(component);
|
}
|
||||||
for (ConnectedPlayer player : connectionsByUuid.values()) {
|
|
||||||
player.getConnection().write(chat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Player> getAllPlayers() {
|
public Optional<RegisteredServer> getServer(String name) {
|
||||||
return ImmutableList.copyOf(connectionsByUuid.values());
|
Preconditions.checkNotNull(name, "name");
|
||||||
|
if (servers == null) {
|
||||||
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
}
|
||||||
|
return servers.getServer(name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPlayerCount() {
|
public Collection<RegisteredServer> getAllServers() {
|
||||||
return connectionsByUuid.size();
|
if (servers == null) {
|
||||||
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
}
|
||||||
|
return servers.getAllServers();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<RegisteredServer> getServer(String name) {
|
public RegisteredServer registerServer(ServerInfo server) {
|
||||||
Preconditions.checkNotNull(name, "name");
|
if (servers == null) {
|
||||||
if (servers == null) {
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
|
||||||
}
|
|
||||||
return servers.getServer(name);
|
|
||||||
}
|
}
|
||||||
|
return servers.register(server);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<RegisteredServer> getAllServers() {
|
public void unregisterServer(ServerInfo server) {
|
||||||
if (servers == null) {
|
if (servers == null) {
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
|
||||||
return servers.getAllServers();
|
|
||||||
}
|
}
|
||||||
|
servers.unregister(server);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RegisteredServer registerServer(ServerInfo server) {
|
public VelocityConsole getConsoleCommandSource() {
|
||||||
if (servers == null) {
|
if (console == null) {
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
|
||||||
return servers.register(server);
|
|
||||||
}
|
}
|
||||||
|
return console;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unregisterServer(ServerInfo server) {
|
public PluginManager getPluginManager() {
|
||||||
if (servers == null) {
|
if (pluginManager == null) {
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
|
||||||
servers.unregister(server);
|
|
||||||
}
|
}
|
||||||
|
return pluginManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VelocityConsole getConsoleCommandSource() {
|
public EventManager getEventManager() {
|
||||||
if (console == null) {
|
if (eventManager == null) {
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
|
||||||
return console;
|
|
||||||
}
|
}
|
||||||
|
return eventManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginManager getPluginManager() {
|
public VelocityScheduler getScheduler() {
|
||||||
if (pluginManager == null) {
|
if (scheduler == null) {
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
throw new IllegalStateException("Server did not initialize properly.");
|
||||||
}
|
|
||||||
return pluginManager;
|
|
||||||
}
|
}
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EventManager getEventManager() {
|
public VelocityChannelRegistrar getChannelRegistrar() {
|
||||||
if (eventManager == null) {
|
return channelRegistrar;
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
}
|
||||||
}
|
|
||||||
return eventManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VelocityScheduler getScheduler() {
|
public InetSocketAddress getBoundAddress() {
|
||||||
if (scheduler == null) {
|
if (configuration == null) {
|
||||||
throw new IllegalStateException("Server did not initialize properly.");
|
throw new IllegalStateException(
|
||||||
}
|
"No configuration"); // even though you'll never get the chance... heh, heh
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
return configuration.getBind();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,90 +9,96 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import com.velocitypowered.api.proxy.ServerConnection;
|
import com.velocitypowered.api.proxy.ServerConnection;
|
||||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||||
import com.velocitypowered.api.proxy.server.ServerInfo;
|
import com.velocitypowered.api.proxy.server.ServerInfo;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import net.kyori.text.TextComponent;
|
import net.kyori.text.TextComponent;
|
||||||
import net.kyori.text.event.ClickEvent;
|
import net.kyori.text.event.ClickEvent;
|
||||||
import net.kyori.text.event.HoverEvent;
|
import net.kyori.text.event.HoverEvent;
|
||||||
import net.kyori.text.format.TextColor;
|
import net.kyori.text.format.TextColor;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
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 {
|
public class ServerCommand implements Command {
|
||||||
private final ProxyServer server;
|
|
||||||
|
|
||||||
public ServerCommand(ProxyServer server) {
|
private final ProxyServer server;
|
||||||
this.server = 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
|
Player player = (Player) source;
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
if (args.length == 1) {
|
||||||
if (!(source instanceof Player)) {
|
// Trying to connect to a server.
|
||||||
source.sendMessage(TextComponent.of("Only players may run this command.", TextColor.RED));
|
String serverName = args[0];
|
||||||
return;
|
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;
|
player.createConnectionRequest(toConnect.get()).fireAndForget();
|
||||||
if (args.length == 1) {
|
} else {
|
||||||
// Trying to connect to a server.
|
String currentServer = player.getCurrentServer().map(ServerConnection::getServerInfo)
|
||||||
String serverName = args[0];
|
.map(ServerInfo::getName)
|
||||||
Optional<RegisteredServer> toConnect = server.getServer(serverName);
|
.orElse("<unknown>");
|
||||||
if (!toConnect.isPresent()) {
|
player.sendMessage(TextComponent
|
||||||
player.sendMessage(TextComponent.of("Server " + serverName + " doesn't exist.", TextColor.RED));
|
.of("You are currently connected to " + currentServer + ".", TextColor.YELLOW));
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
String currentServer = player.getCurrentServer().map(ServerConnection::getServerInfo).map(ServerInfo::getName)
|
infoComponent = infoComponent.color(TextColor.GRAY)
|
||||||
.orElse("<unknown>");
|
.clickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND,
|
||||||
player.sendMessage(TextComponent.of("You are currently connected to " + currentServer + ".", TextColor.YELLOW));
|
"/server " + rs.getServerInfo().getName()))
|
||||||
|
.hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
|
||||||
// Assemble the list of servers as components
|
TextComponent.of("Click to connect to this server\n" + playersText)));
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
serverListBuilder.append(infoComponent);
|
||||||
|
if (i != infos.size() - 1) {
|
||||||
@Override
|
serverListBuilder.append(TextComponent.of(", ", TextColor.GRAY));
|
||||||
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
|
player.sendMessage(serverListBuilder.build());
|
||||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
|
||||||
return source.getPermissionValue("velocity.command.server") != Tristate.FALSE;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,23 +8,25 @@ import net.kyori.text.format.TextColor;
|
|||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
public class ShutdownCommand implements Command {
|
public class ShutdownCommand implements Command {
|
||||||
private final VelocityServer server;
|
|
||||||
|
|
||||||
public ShutdownCommand(VelocityServer server) {
|
private final VelocityServer server;
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public ShutdownCommand(VelocityServer server) {
|
||||||
public void execute(CommandSource source, String @NonNull [] args) {
|
this.server = server;
|
||||||
if (source != server.getConsoleCommandSource()) {
|
}
|
||||||
source.sendMessage(TextComponent.of("You are not allowed to use this command.", TextColor.RED));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
server.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(CommandSource source, String @NonNull [] args) {
|
public void execute(CommandSource source, String @NonNull [] args) {
|
||||||
return source == server.getConsoleCommandSource();
|
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
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren