mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-28 23:38:25 +01:00
Don't call AsyncTabCompleteEvent from netty IO thread (#8218)
This commit is contained in:
parent
aea4847e26
commit
3669c95014
7 changed files with 464 additions and 609 deletions
|
@ -1,407 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Thu, 1 Apr 2021 00:34:41 -0700
|
||||
Subject: [PATCH] Allow for Component suggestion tooltips in
|
||||
AsyncTabCompleteEvent
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java b/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
|
||||
@@ -0,0 +0,0 @@
|
||||
package com.destroystokyo.paper.event.server;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
+import io.papermc.paper.util.TransformingRandomAccessList;
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.examination.Examinable;
|
||||
+import net.kyori.examination.ExaminableProperty;
|
||||
+import net.kyori.examination.string.StringExaminer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@@ -0,0 +0,0 @@ import org.bukkit.event.HandlerList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
+import java.util.stream.Stream;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -0,0 +0,0 @@ public class AsyncTabCompleteEvent extends Event implements Cancellable {
|
||||
private final boolean isCommand;
|
||||
@Nullable
|
||||
private final Location loc;
|
||||
- @NotNull private List<String> completions;
|
||||
+ private final List<Completion> completions = new ArrayList<>();
|
||||
+ private final List<String> stringCompletions = new TransformingRandomAccessList<>(
|
||||
+ this.completions,
|
||||
+ Completion::suggestion,
|
||||
+ Completion::completion
|
||||
+ );
|
||||
private boolean cancelled;
|
||||
private boolean handled = false;
|
||||
private boolean fireSyncHandler = true;
|
||||
|
||||
+ public AsyncTabCompleteEvent(@NotNull CommandSender sender, @NotNull String buffer, boolean isCommand, @Nullable Location loc) {
|
||||
+ super(true);
|
||||
+ this.sender = sender;
|
||||
+ this.buffer = buffer;
|
||||
+ this.isCommand = isCommand;
|
||||
+ this.loc = loc;
|
||||
+ }
|
||||
+
|
||||
+ @Deprecated
|
||||
public AsyncTabCompleteEvent(@NotNull CommandSender sender, @NotNull List<String> completions, @NotNull String buffer, boolean isCommand, @Nullable Location loc) {
|
||||
super(true);
|
||||
this.sender = sender;
|
||||
- this.completions = completions;
|
||||
+ this.completions.addAll(fromStrings(completions));
|
||||
this.buffer = buffer;
|
||||
this.isCommand = isCommand;
|
||||
this.loc = loc;
|
||||
@@ -0,0 +0,0 @@ public class AsyncTabCompleteEvent extends Event implements Cancellable {
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getCompletions() {
|
||||
- return completions;
|
||||
+ return this.stringCompletions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -0,0 +0,0 @@ public class AsyncTabCompleteEvent extends Event implements Cancellable {
|
||||
* @param completions the new completions
|
||||
*/
|
||||
public void setCompletions(@NotNull List<String> completions) {
|
||||
+ if (completions == this.stringCompletions) {
|
||||
+ return;
|
||||
+ }
|
||||
Preconditions.checkNotNull(completions);
|
||||
- this.completions = new ArrayList<>(completions);
|
||||
+ this.completions.clear();
|
||||
+ this.completions.addAll(fromStrings(completions));
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * The list of {@link Completion completions} which will be offered to the sender, in order.
|
||||
+ * This list is mutable and reflects what will be offered.
|
||||
+ * <p>
|
||||
+ * If this collection is not empty after the event is fired, then
|
||||
+ * the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
|
||||
+ * or current player names will not be called.
|
||||
+ *
|
||||
+ * @return a list of offered completions
|
||||
+ */
|
||||
+ public @NotNull List<Completion> completions() {
|
||||
+ return this.completions;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Set the {@link Completion completions} offered, overriding any already set.
|
||||
+ * If this collection is not empty after the event is fired, then
|
||||
+ * the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
|
||||
+ * or current player names will not be called.
|
||||
+ * <p>
|
||||
+ * The passed collection will be cloned to a new List. You must call {{@link #completions()}} to mutate from here
|
||||
+ *
|
||||
+ * @param newCompletions the new completions
|
||||
+ */
|
||||
+ public void completions(final @NotNull List<Completion> newCompletions) {
|
||||
+ Preconditions.checkNotNull(newCompletions, "new completions");
|
||||
+ this.completions.clear();
|
||||
+ this.completions.addAll(newCompletions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -0,0 +0,0 @@ public class AsyncTabCompleteEvent extends Event implements Cancellable {
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
+
|
||||
+ private static @NotNull List<Completion> fromStrings(final @NotNull List<String> strings) {
|
||||
+ final List<Completion> list = new ArrayList<>();
|
||||
+ for (final String it : strings) {
|
||||
+ list.add(new CompletionImpl(it, null));
|
||||
+ }
|
||||
+ return list;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * A rich tab completion, consisting of a string suggestion, and a nullable {@link Component} tooltip.
|
||||
+ */
|
||||
+ public interface Completion extends Examinable {
|
||||
+ /**
|
||||
+ * Get the suggestion string for this {@link Completion}.
|
||||
+ *
|
||||
+ * @return suggestion string
|
||||
+ */
|
||||
+ @NotNull String suggestion();
|
||||
+
|
||||
+ /**
|
||||
+ * Get the suggestion tooltip for this {@link Completion}.
|
||||
+ *
|
||||
+ * @return tooltip component
|
||||
+ */
|
||||
+ @Nullable Component tooltip();
|
||||
+
|
||||
+ @Override
|
||||
+ default @NotNull Stream<? extends ExaminableProperty> examinableProperties() {
|
||||
+ return Stream.of(ExaminableProperty.of("suggestion", this.suggestion()), ExaminableProperty.of("tooltip", this.tooltip()));
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Create a new {@link Completion} from a suggestion string.
|
||||
+ *
|
||||
+ * @param suggestion suggestion string
|
||||
+ * @return new completion instance
|
||||
+ */
|
||||
+ static @NotNull Completion completion(final @NotNull String suggestion) {
|
||||
+ return new CompletionImpl(suggestion, null);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Create a new {@link Completion} from a suggestion string and a tooltip {@link Component}.
|
||||
+ *
|
||||
+ * <p>If the provided component is null, the suggestion will not have a tooltip.</p>
|
||||
+ *
|
||||
+ * @param suggestion suggestion string
|
||||
+ * @param tooltip tooltip component, or null
|
||||
+ * @return new completion instance
|
||||
+ */
|
||||
+ static @NotNull Completion completion(final @NotNull String suggestion, final @Nullable Component tooltip) {
|
||||
+ return new CompletionImpl(suggestion, tooltip);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ static final class CompletionImpl implements Completion {
|
||||
+ private final String suggestion;
|
||||
+ private final Component tooltip;
|
||||
+
|
||||
+ CompletionImpl(final @NotNull String suggestion, final @Nullable Component tooltip) {
|
||||
+ this.suggestion = suggestion;
|
||||
+ this.tooltip = tooltip;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull String suggestion() {
|
||||
+ return this.suggestion;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @Nullable Component tooltip() {
|
||||
+ return this.tooltip;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean equals(final @Nullable Object o) {
|
||||
+ if (this == o) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (o == null || this.getClass() != o.getClass()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ final CompletionImpl that = (CompletionImpl) o;
|
||||
+ return this.suggestion.equals(that.suggestion)
|
||||
+ && java.util.Objects.equals(this.tooltip, that.tooltip);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int hashCode() {
|
||||
+ return java.util.Objects.hash(this.suggestion, this.tooltip);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull String toString() {
|
||||
+ return StringExaminer.simpleEscaping().examine(this);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java b/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+import java.util.AbstractList;
|
||||
+import java.util.Iterator;
|
||||
+import java.util.List;
|
||||
+import java.util.ListIterator;
|
||||
+import java.util.RandomAccess;
|
||||
+import java.util.function.Function;
|
||||
+import java.util.function.Predicate;
|
||||
+
|
||||
+import static com.google.common.base.Preconditions.checkNotNull;
|
||||
+
|
||||
+/**
|
||||
+ * Modified version of the Guava class with the same name to support add operations.
|
||||
+ *
|
||||
+ * @param <F> backing list element type
|
||||
+ * @param <T> transformed list element type
|
||||
+ */
|
||||
+public final class TransformingRandomAccessList<F, T> extends AbstractList<T> implements RandomAccess {
|
||||
+ final List<F> fromList;
|
||||
+ final Function<? super F, ? extends T> toFunction;
|
||||
+ final Function<? super T, ? extends F> fromFunction;
|
||||
+
|
||||
+ /**
|
||||
+ * Create a new {@link TransformingRandomAccessList}.
|
||||
+ *
|
||||
+ * @param fromList backing list
|
||||
+ * @param toFunction function mapping backing list element type to transformed list element type
|
||||
+ * @param fromFunction function mapping transformed list element type to backing list element type
|
||||
+ */
|
||||
+ public TransformingRandomAccessList(
|
||||
+ final @NonNull List<F> fromList,
|
||||
+ final @NonNull Function<? super F, ? extends T> toFunction,
|
||||
+ final @NonNull Function<? super T, ? extends F> fromFunction
|
||||
+ ) {
|
||||
+ this.fromList = checkNotNull(fromList);
|
||||
+ this.toFunction = checkNotNull(toFunction);
|
||||
+ this.fromFunction = checkNotNull(fromFunction);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void clear() {
|
||||
+ this.fromList.clear();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public T get(int index) {
|
||||
+ return this.toFunction.apply(this.fromList.get(index));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull Iterator<T> iterator() {
|
||||
+ return this.listIterator();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull ListIterator<T> listIterator(int index) {
|
||||
+ return new TransformedListIterator<F, T>(this.fromList.listIterator(index)) {
|
||||
+ @Override
|
||||
+ T transform(F from) {
|
||||
+ return TransformingRandomAccessList.this.toFunction.apply(from);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ F transformBack(T from) {
|
||||
+ return TransformingRandomAccessList.this.fromFunction.apply(from);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isEmpty() {
|
||||
+ return this.fromList.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean removeIf(Predicate<? super T> filter) {
|
||||
+ checkNotNull(filter);
|
||||
+ return this.fromList.removeIf(element -> filter.test(this.toFunction.apply(element)));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public T remove(int index) {
|
||||
+ return this.toFunction.apply(this.fromList.remove(index));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int size() {
|
||||
+ return this.fromList.size();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public T set(int i, T t) {
|
||||
+ return this.toFunction.apply(this.fromList.set(i, this.fromFunction.apply(t)));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void add(int i, T t) {
|
||||
+ this.fromList.add(i, this.fromFunction.apply(t));
|
||||
+ }
|
||||
+
|
||||
+ static abstract class TransformedListIterator<F, T> implements ListIterator<T>, Iterator<T> {
|
||||
+ final Iterator<F> backingIterator;
|
||||
+
|
||||
+ TransformedListIterator(ListIterator<F> backingIterator) {
|
||||
+ this.backingIterator = checkNotNull((Iterator<F>) backingIterator);
|
||||
+ }
|
||||
+
|
||||
+ private ListIterator<F> backingIterator() {
|
||||
+ return cast(this.backingIterator);
|
||||
+ }
|
||||
+
|
||||
+ static <A> ListIterator<A> cast(Iterator<A> iterator) {
|
||||
+ return (ListIterator<A>) iterator;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean hasPrevious() {
|
||||
+ return this.backingIterator().hasPrevious();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final T previous() {
|
||||
+ return this.transform(this.backingIterator().previous());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final int nextIndex() {
|
||||
+ return this.backingIterator().nextIndex();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final int previousIndex() {
|
||||
+ return this.backingIterator().previousIndex();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void set(T element) {
|
||||
+ this.backingIterator().set(this.transformBack(element));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void add(T element) {
|
||||
+ this.backingIterator().add(this.transformBack(element));
|
||||
+ }
|
||||
+
|
||||
+ abstract T transform(F from);
|
||||
+
|
||||
+ abstract F transformBack(T to);
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean hasNext() {
|
||||
+ return this.backingIterator.hasNext();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final T next() {
|
||||
+ return this.transform(this.backingIterator.next());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final void remove() {
|
||||
+ this.backingIterator.remove();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/test/java/org/bukkit/AnnotationTest.java
|
||||
+++ b/src/test/java/org/bukkit/AnnotationTest.java
|
||||
@@ -0,0 +0,0 @@ public class AnnotationTest {
|
||||
// Generic functional interface
|
||||
"org/bukkit/util/Consumer",
|
||||
// Paper start
|
||||
+ "io/papermc/paper/util/TransformingRandomAccessList",
|
||||
+ "io/papermc/paper/util/TransformingRandomAccessList$TransformedListIterator",
|
||||
// Timings history is broken in terms of nullability due to guavas Function defining that the param is NonNull
|
||||
"co/aikar/timings/TimingHistory$2",
|
||||
"co/aikar/timings/TimingHistory$2$1",
|
|
@ -1,5 +1,5 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Sun, 26 Nov 2017 13:17:09 -0500
|
||||
Subject: [PATCH] AsyncTabCompleteEvent
|
||||
|
||||
|
@ -11,6 +11,8 @@ and avoid going to main for tab completions.
|
|||
Especially useful if you need to query a database in order to obtain the results for tab
|
||||
completion, such as offline players.
|
||||
|
||||
Co-authored-by: Aikar <aikar@aikar.co>
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java b/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
|
@ -43,6 +45,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+package com.destroystokyo.paper.event.server;
|
||||
+
|
||||
+import com.google.common.base.Preconditions;
|
||||
+import io.papermc.paper.util.TransformingRandomAccessList;
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.examination.Examinable;
|
||||
+import net.kyori.examination.ExaminableProperty;
|
||||
+import net.kyori.examination.string.StringExaminer;
|
||||
+import org.bukkit.Location;
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
|
@ -52,6 +59,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.List;
|
||||
+import java.util.stream.Stream;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+import org.jetbrains.annotations.Nullable;
|
||||
+
|
||||
|
@ -66,15 +74,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ private final boolean isCommand;
|
||||
+ @Nullable
|
||||
+ private final Location loc;
|
||||
+ @NotNull private List<String> completions;
|
||||
+ private final List<Completion> completions = new ArrayList<>();
|
||||
+ private final List<String> stringCompletions = new TransformingRandomAccessList<>(
|
||||
+ this.completions,
|
||||
+ Completion::suggestion,
|
||||
+ Completion::completion
|
||||
+ );
|
||||
+ private boolean cancelled;
|
||||
+ private boolean handled = false;
|
||||
+ private boolean fireSyncHandler = true;
|
||||
+
|
||||
+ public AsyncTabCompleteEvent(@NotNull CommandSender sender, @NotNull String buffer, boolean isCommand, @Nullable Location loc) {
|
||||
+ super(true);
|
||||
+ this.sender = sender;
|
||||
+ this.buffer = buffer;
|
||||
+ this.isCommand = isCommand;
|
||||
+ this.loc = loc;
|
||||
+ }
|
||||
+
|
||||
+ @Deprecated
|
||||
+ public AsyncTabCompleteEvent(@NotNull CommandSender sender, @NotNull List<String> completions, @NotNull String buffer, boolean isCommand, @Nullable Location loc) {
|
||||
+ super(true);
|
||||
+ this.sender = sender;
|
||||
+ this.completions = completions;
|
||||
+ this.completions.addAll(fromStrings(completions));
|
||||
+ this.buffer = buffer;
|
||||
+ this.isCommand = isCommand;
|
||||
+ this.loc = loc;
|
||||
|
@ -102,7 +124,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ */
|
||||
+ @NotNull
|
||||
+ public List<String> getCompletions() {
|
||||
+ return completions;
|
||||
+ return this.stringCompletions;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
|
@ -116,8 +138,42 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ * @param completions the new completions
|
||||
+ */
|
||||
+ public void setCompletions(@NotNull List<String> completions) {
|
||||
+ if (completions == this.stringCompletions) {
|
||||
+ return;
|
||||
+ }
|
||||
+ Preconditions.checkNotNull(completions);
|
||||
+ this.completions = new ArrayList<>(completions);
|
||||
+ this.completions.clear();
|
||||
+ this.completions.addAll(fromStrings(completions));
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * The list of {@link Completion completions} which will be offered to the sender, in order.
|
||||
+ * This list is mutable and reflects what will be offered.
|
||||
+ * <p>
|
||||
+ * If this collection is not empty after the event is fired, then
|
||||
+ * the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
|
||||
+ * or current player names will not be called.
|
||||
+ *
|
||||
+ * @return a list of offered completions
|
||||
+ */
|
||||
+ public @NotNull List<Completion> completions() {
|
||||
+ return this.completions;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Set the {@link Completion completions} offered, overriding any already set.
|
||||
+ * If this collection is not empty after the event is fired, then
|
||||
+ * the standard process of calling {@link Command#tabComplete(CommandSender, String, String[])}
|
||||
+ * or current player names will not be called.
|
||||
+ * <p>
|
||||
+ * The passed collection will be cloned to a new List. You must call {{@link #completions()}} to mutate from here
|
||||
+ *
|
||||
+ * @param newCompletions the new completions
|
||||
+ */
|
||||
+ public void completions(final @NotNull List<Completion> newCompletions) {
|
||||
+ Preconditions.checkNotNull(newCompletions, "new completions");
|
||||
+ this.completions.clear();
|
||||
+ this.completions.addAll(newCompletions);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
|
@ -192,6 +248,279 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ public static HandlerList getHandlerList() {
|
||||
+ return handlers;
|
||||
+ }
|
||||
+
|
||||
+ private static @NotNull List<Completion> fromStrings(final @NotNull List<String> strings) {
|
||||
+ final List<Completion> list = new ArrayList<>();
|
||||
+ for (final String it : strings) {
|
||||
+ list.add(new CompletionImpl(it, null));
|
||||
+ }
|
||||
+ return list;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * A rich tab completion, consisting of a string suggestion, and a nullable {@link Component} tooltip.
|
||||
+ */
|
||||
+ public interface Completion extends Examinable {
|
||||
+ /**
|
||||
+ * Get the suggestion string for this {@link Completion}.
|
||||
+ *
|
||||
+ * @return suggestion string
|
||||
+ */
|
||||
+ @NotNull String suggestion();
|
||||
+
|
||||
+ /**
|
||||
+ * Get the suggestion tooltip for this {@link Completion}.
|
||||
+ *
|
||||
+ * @return tooltip component
|
||||
+ */
|
||||
+ @Nullable Component tooltip();
|
||||
+
|
||||
+ @Override
|
||||
+ default @NotNull Stream<? extends ExaminableProperty> examinableProperties() {
|
||||
+ return Stream.of(ExaminableProperty.of("suggestion", this.suggestion()), ExaminableProperty.of("tooltip", this.tooltip()));
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Create a new {@link Completion} from a suggestion string.
|
||||
+ *
|
||||
+ * @param suggestion suggestion string
|
||||
+ * @return new completion instance
|
||||
+ */
|
||||
+ static @NotNull Completion completion(final @NotNull String suggestion) {
|
||||
+ return new CompletionImpl(suggestion, null);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Create a new {@link Completion} from a suggestion string and a tooltip {@link Component}.
|
||||
+ *
|
||||
+ * <p>If the provided component is null, the suggestion will not have a tooltip.</p>
|
||||
+ *
|
||||
+ * @param suggestion suggestion string
|
||||
+ * @param tooltip tooltip component, or null
|
||||
+ * @return new completion instance
|
||||
+ */
|
||||
+ static @NotNull Completion completion(final @NotNull String suggestion, final @Nullable Component tooltip) {
|
||||
+ return new CompletionImpl(suggestion, tooltip);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ static final class CompletionImpl implements Completion {
|
||||
+ private final String suggestion;
|
||||
+ private final Component tooltip;
|
||||
+
|
||||
+ CompletionImpl(final @NotNull String suggestion, final @Nullable Component tooltip) {
|
||||
+ this.suggestion = suggestion;
|
||||
+ this.tooltip = tooltip;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull String suggestion() {
|
||||
+ return this.suggestion;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @Nullable Component tooltip() {
|
||||
+ return this.tooltip;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean equals(final @Nullable Object o) {
|
||||
+ if (this == o) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (o == null || this.getClass() != o.getClass()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ final CompletionImpl that = (CompletionImpl) o;
|
||||
+ return this.suggestion.equals(that.suggestion)
|
||||
+ && java.util.Objects.equals(this.tooltip, that.tooltip);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int hashCode() {
|
||||
+ return java.util.Objects.hash(this.suggestion, this.tooltip);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull String toString() {
|
||||
+ return StringExaminer.simpleEscaping().examine(this);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java b/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/TransformingRandomAccessList.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+import java.util.AbstractList;
|
||||
+import java.util.Iterator;
|
||||
+import java.util.List;
|
||||
+import java.util.ListIterator;
|
||||
+import java.util.RandomAccess;
|
||||
+import java.util.function.Function;
|
||||
+import java.util.function.Predicate;
|
||||
+
|
||||
+import static com.google.common.base.Preconditions.checkNotNull;
|
||||
+
|
||||
+/**
|
||||
+ * Modified version of the Guava class with the same name to support add operations.
|
||||
+ *
|
||||
+ * @param <F> backing list element type
|
||||
+ * @param <T> transformed list element type
|
||||
+ */
|
||||
+public final class TransformingRandomAccessList<F, T> extends AbstractList<T> implements RandomAccess {
|
||||
+ final List<F> fromList;
|
||||
+ final Function<? super F, ? extends T> toFunction;
|
||||
+ final Function<? super T, ? extends F> fromFunction;
|
||||
+
|
||||
+ /**
|
||||
+ * Create a new {@link TransformingRandomAccessList}.
|
||||
+ *
|
||||
+ * @param fromList backing list
|
||||
+ * @param toFunction function mapping backing list element type to transformed list element type
|
||||
+ * @param fromFunction function mapping transformed list element type to backing list element type
|
||||
+ */
|
||||
+ public TransformingRandomAccessList(
|
||||
+ final @NonNull List<F> fromList,
|
||||
+ final @NonNull Function<? super F, ? extends T> toFunction,
|
||||
+ final @NonNull Function<? super T, ? extends F> fromFunction
|
||||
+ ) {
|
||||
+ this.fromList = checkNotNull(fromList);
|
||||
+ this.toFunction = checkNotNull(toFunction);
|
||||
+ this.fromFunction = checkNotNull(fromFunction);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void clear() {
|
||||
+ this.fromList.clear();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public T get(int index) {
|
||||
+ return this.toFunction.apply(this.fromList.get(index));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull Iterator<T> iterator() {
|
||||
+ return this.listIterator();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull ListIterator<T> listIterator(int index) {
|
||||
+ return new TransformedListIterator<F, T>(this.fromList.listIterator(index)) {
|
||||
+ @Override
|
||||
+ T transform(F from) {
|
||||
+ return TransformingRandomAccessList.this.toFunction.apply(from);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ F transformBack(T from) {
|
||||
+ return TransformingRandomAccessList.this.fromFunction.apply(from);
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isEmpty() {
|
||||
+ return this.fromList.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean removeIf(Predicate<? super T> filter) {
|
||||
+ checkNotNull(filter);
|
||||
+ return this.fromList.removeIf(element -> filter.test(this.toFunction.apply(element)));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public T remove(int index) {
|
||||
+ return this.toFunction.apply(this.fromList.remove(index));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int size() {
|
||||
+ return this.fromList.size();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public T set(int i, T t) {
|
||||
+ return this.toFunction.apply(this.fromList.set(i, this.fromFunction.apply(t)));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void add(int i, T t) {
|
||||
+ this.fromList.add(i, this.fromFunction.apply(t));
|
||||
+ }
|
||||
+
|
||||
+ static abstract class TransformedListIterator<F, T> implements ListIterator<T>, Iterator<T> {
|
||||
+ final Iterator<F> backingIterator;
|
||||
+
|
||||
+ TransformedListIterator(ListIterator<F> backingIterator) {
|
||||
+ this.backingIterator = checkNotNull((Iterator<F>) backingIterator);
|
||||
+ }
|
||||
+
|
||||
+ private ListIterator<F> backingIterator() {
|
||||
+ return cast(this.backingIterator);
|
||||
+ }
|
||||
+
|
||||
+ static <A> ListIterator<A> cast(Iterator<A> iterator) {
|
||||
+ return (ListIterator<A>) iterator;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean hasPrevious() {
|
||||
+ return this.backingIterator().hasPrevious();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final T previous() {
|
||||
+ return this.transform(this.backingIterator().previous());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final int nextIndex() {
|
||||
+ return this.backingIterator().nextIndex();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final int previousIndex() {
|
||||
+ return this.backingIterator().previousIndex();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void set(T element) {
|
||||
+ this.backingIterator().set(this.transformBack(element));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void add(T element) {
|
||||
+ this.backingIterator().add(this.transformBack(element));
|
||||
+ }
|
||||
+
|
||||
+ abstract T transform(F from);
|
||||
+
|
||||
+ abstract F transformBack(T to);
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean hasNext() {
|
||||
+ return this.backingIterator.hasNext();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final T next() {
|
||||
+ return this.transform(this.backingIterator.next());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final void remove() {
|
||||
+ this.backingIterator.remove();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/org/bukkit/event/server/TabCompleteEvent.java b/src/main/java/org/bukkit/event/server/TabCompleteEvent.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
|
@ -248,3 +577,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/test/java/org/bukkit/AnnotationTest.java b/src/test/java/org/bukkit/AnnotationTest.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/test/java/org/bukkit/AnnotationTest.java
|
||||
+++ b/src/test/java/org/bukkit/AnnotationTest.java
|
||||
@@ -0,0 +0,0 @@ public class AnnotationTest {
|
||||
// Generic functional interface
|
||||
"org/bukkit/util/Consumer",
|
||||
// Paper start
|
||||
+ "io/papermc/paper/util/TransformingRandomAccessList",
|
||||
+ "io/papermc/paper/util/TransformingRandomAccessList$TransformedListIterator",
|
||||
// Timings history is broken in terms of nullability due to guavas Function defining that the param is NonNull
|
||||
"co/aikar/timings/TimingHistory$2",
|
||||
"co/aikar/timings/TimingHistory$2$1",
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Thu, 1 Apr 2021 00:34:02 -0700
|
||||
Subject: [PATCH] Allow for Component suggestion tooltips in
|
||||
AsyncTabCompleteEvent
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
|
||||
// Paper start - async tab completion
|
||||
com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
||||
- java.util.List<String> completions = new java.util.ArrayList<>();
|
||||
String buffer = packet.getCommand();
|
||||
- event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(), completions,
|
||||
+ event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(),
|
||||
buffer, true, null);
|
||||
event.callEvent();
|
||||
- completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
||||
+ java.util.List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.completions();
|
||||
// If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
|
||||
if (!event.isHandled()) {
|
||||
if (!event.isCancelled()) {
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
});
|
||||
}
|
||||
} else if (!completions.isEmpty()) {
|
||||
- com.mojang.brigadier.suggestion.SuggestionsBuilder builder = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringreader.getTotalLength());
|
||||
+ com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringreader.getTotalLength());
|
||||
|
||||
- builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
|
||||
- completions.forEach(builder::suggest);
|
||||
+ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder = builder0.createOffset(builder0.getInput().lastIndexOf(' ') + 1);
|
||||
+ completions.forEach(completion -> {
|
||||
+ final Integer intSuggestion = com.google.common.primitives.Ints.tryParse(completion.suggestion());
|
||||
+ if (intSuggestion != null) {
|
||||
+ builder.suggest(intSuggestion, PaperAdventure.asVanilla(completion.tooltip()));
|
||||
+ } else {
|
||||
+ builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip()));
|
||||
+ }
|
||||
+ });
|
||||
com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join();
|
||||
com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, buffer);
|
||||
suggestEvent.setCancelled(suggestions.isEmpty());
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
||||
@@ -0,0 +0,0 @@ public class ConsoleCommandCompleter implements Completer {
|
||||
final CraftServer server = this.server.server;
|
||||
final String buffer = line.line();
|
||||
// Async Tab Complete
|
||||
- com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
||||
- java.util.List<String> completions = new java.util.ArrayList<>();
|
||||
- event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), completions,
|
||||
- buffer, true, null);
|
||||
+ final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event =
|
||||
+ new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), buffer, true, null);
|
||||
event.callEvent();
|
||||
- completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
||||
+ final List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.completions();
|
||||
|
||||
if (event.isCancelled() || event.isHandled()) {
|
||||
// Still fire sync event with the provided completions, if someone is listening
|
||||
if (!event.isCancelled() && TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||
- List<String> finalCompletions = completions;
|
||||
+ List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> finalCompletions = new java.util.ArrayList<>(completions);
|
||||
Waitable<List<String>> syncCompletions = new Waitable<List<String>>() {
|
||||
@Override
|
||||
protected List<String> evaluate() {
|
||||
- org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer, finalCompletions);
|
||||
+ org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer,
|
||||
+ finalCompletions.stream()
|
||||
+ .map(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion::suggestion)
|
||||
+ .collect(java.util.stream.Collectors.toList()));
|
||||
return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
|
||||
}
|
||||
};
|
||||
server.getServer().processQueue.add(syncCompletions);
|
||||
try {
|
||||
- completions = syncCompletions.get();
|
||||
+ final List<String> legacyCompletions = syncCompletions.get();
|
||||
+ completions.removeIf(it -> !legacyCompletions.contains(it.suggestion())); // remove any suggestions that were removed
|
||||
+ // add any new suggestions
|
||||
+ for (final String completion : legacyCompletions) {
|
||||
+ if (notNewSuggestion(completions, completion)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ completions.add(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion.completion(completion));
|
||||
+ }
|
||||
} catch (InterruptedException | ExecutionException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!completions.isEmpty()) {
|
||||
- candidates.addAll(completions.stream().map(Candidate::new).collect(java.util.stream.Collectors.toList()));
|
||||
+ for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) {
|
||||
+ if (completion.suggestion().isEmpty()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ candidates.add(new Candidate(
|
||||
+ completion.suggestion(),
|
||||
+ completion.suggestion(),
|
||||
+ null,
|
||||
+ io.papermc.paper.adventure.PaperAdventure.PLAIN.serializeOr(completion.tooltip(), null),
|
||||
+ null,
|
||||
+ null,
|
||||
+ false
|
||||
+ ));
|
||||
+ }
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class ConsoleCommandCompleter implements Completer {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ private boolean notNewSuggestion(final List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> completions, final String completion) {
|
||||
+ for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion it : completions) {
|
||||
+ if (it.suggestion().equals(completion)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Sun, 26 Nov 2017 13:19:58 -0500
|
||||
Subject: [PATCH] AsyncTabCompleteEvent
|
||||
|
||||
|
@ -13,12 +13,20 @@ completion, such as offline players.
|
|||
|
||||
Also adds isCommand and getLocation to the sync TabCompleteEvent
|
||||
|
||||
Co-authored-by: Aikar <aikar@aikar.co>
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ private static final java.util.concurrent.ExecutorService TAB_COMPLETE_EXECUTOR = java.util.concurrent.Executors.newFixedThreadPool(4,
|
||||
+ new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon(true).setNameFormat("Async Tab Complete Thread - #%d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build());
|
||||
+ // Paper end
|
||||
@Override
|
||||
public void handleCustomCommandSuggestions(ServerboundCommandSuggestionPacket packet) {
|
||||
- PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
||||
|
@ -30,43 +38,48 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
+ // Paper start - async tab completion
|
||||
+ TAB_COMPLETE_EXECUTOR.execute(() -> {
|
||||
StringReader stringreader = new StringReader(packet.getCommand());
|
||||
|
||||
if (stringreader.canRead() && stringreader.peek() == '/') {
|
||||
stringreader.skip();
|
||||
}
|
||||
+ final String command = packet.getCommand();
|
||||
+ final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(), command, true, null);
|
||||
+ event.callEvent();
|
||||
+ final java.util.List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.completions();
|
||||
+ // If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
|
||||
+ if (!event.isHandled()) {
|
||||
+ if (!event.isCancelled()) {
|
||||
|
||||
- ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
||||
+ // Paper start - async tab completion
|
||||
+ com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
||||
+ java.util.List<String> completions = new java.util.ArrayList<>();
|
||||
+ String buffer = packet.getCommand();
|
||||
+ event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(this.getCraftPlayer(), completions,
|
||||
+ buffer, true, null);
|
||||
+ event.callEvent();
|
||||
+ completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
||||
+ // If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
|
||||
+ if (!event.isHandled()) {
|
||||
+ if (!event.isCancelled()) {
|
||||
+ this.server.scheduleOnMain(() -> { // This needs to be on main
|
||||
+ ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
||||
|
||||
- this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
||||
- if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
||||
- this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
||||
- });
|
||||
+ this.server.scheduleOnMain(() -> { // Paper - This needs to be on main
|
||||
+ ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
||||
+
|
||||
+ this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
||||
+ if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
||||
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
||||
+ this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
||||
+ if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
||||
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
||||
+ });
|
||||
+ });
|
||||
+ }
|
||||
+ } else if (!completions.isEmpty()) {
|
||||
+ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder0 = new com.mojang.brigadier.suggestion.SuggestionsBuilder(command, stringreader.getTotalLength());
|
||||
+ final com.mojang.brigadier.suggestion.SuggestionsBuilder builder = builder0.createOffset(builder0.getInput().lastIndexOf(' ') + 1);
|
||||
+ completions.forEach(completion -> {
|
||||
+ final Integer intSuggestion = com.google.common.primitives.Ints.tryParse(completion.suggestion());
|
||||
+ if (intSuggestion != null) {
|
||||
+ builder.suggest(intSuggestion, PaperAdventure.asVanilla(completion.tooltip()));
|
||||
+ } else {
|
||||
+ builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip()));
|
||||
+ }
|
||||
+ });
|
||||
+ player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
|
||||
+ }
|
||||
+ } else if (!completions.isEmpty()) {
|
||||
+ com.mojang.brigadier.suggestion.SuggestionsBuilder builder = new com.mojang.brigadier.suggestion.SuggestionsBuilder(packet.getCommand(), stringreader.getTotalLength());
|
||||
+
|
||||
+ builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
|
||||
+ completions.forEach(builder::suggest);
|
||||
+ player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
|
||||
+ }
|
||||
});
|
||||
+ // Paper end - async tab completion
|
||||
}
|
||||
|
||||
|
@ -93,34 +106,56 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
final CraftServer server = this.server.server;
|
||||
final String buffer = line.line();
|
||||
+ // Async Tab Complete
|
||||
+ com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event;
|
||||
+ java.util.List<String> completions = new java.util.ArrayList<>();
|
||||
+ event = new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), completions,
|
||||
+ buffer, true, null);
|
||||
+ final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent event =
|
||||
+ new com.destroystokyo.paper.event.server.AsyncTabCompleteEvent(server.getConsoleSender(), buffer, true, null);
|
||||
+ event.callEvent();
|
||||
+ completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
||||
+ final List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.completions();
|
||||
+
|
||||
+ if (event.isCancelled() || event.isHandled()) {
|
||||
+ // Still fire sync event with the provided completions, if someone is listening
|
||||
+ if (!event.isCancelled() && TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||
+ List<String> finalCompletions = completions;
|
||||
+ List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> finalCompletions = new java.util.ArrayList<>(completions);
|
||||
+ Waitable<List<String>> syncCompletions = new Waitable<List<String>>() {
|
||||
+ @Override
|
||||
+ protected List<String> evaluate() {
|
||||
+ org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer, finalCompletions);
|
||||
+ org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(server.getConsoleSender(), buffer,
|
||||
+ finalCompletions.stream()
|
||||
+ .map(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion::suggestion)
|
||||
+ .collect(java.util.stream.Collectors.toList()));
|
||||
+ return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
|
||||
+ }
|
||||
+ };
|
||||
+ server.getServer().processQueue.add(syncCompletions);
|
||||
+ try {
|
||||
+ completions = syncCompletions.get();
|
||||
+ final List<String> legacyCompletions = syncCompletions.get();
|
||||
+ completions.removeIf(it -> !legacyCompletions.contains(it.suggestion())); // remove any suggestions that were removed
|
||||
+ // add any new suggestions
|
||||
+ for (final String completion : legacyCompletions) {
|
||||
+ if (notNewSuggestion(completions, completion)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ completions.add(com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion.completion(completion));
|
||||
+ }
|
||||
+ } catch (InterruptedException | ExecutionException e1) {
|
||||
+ e1.printStackTrace();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!completions.isEmpty()) {
|
||||
+ candidates.addAll(completions.stream().map(Candidate::new).collect(java.util.stream.Collectors.toList()));
|
||||
+ for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion completion : completions) {
|
||||
+ if (completion.suggestion().isEmpty()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ candidates.add(new Candidate(
|
||||
+ completion.suggestion(),
|
||||
+ completion.suggestion(),
|
||||
+ null,
|
||||
+ io.papermc.paper.adventure.PaperAdventure.PLAIN.serializeOr(completion.tooltip(), null),
|
||||
+ null,
|
||||
+ null,
|
||||
+ false
|
||||
+ ));
|
||||
+ }
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
|
@ -128,3 +163,19 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
// Paper end
|
||||
Waitable<List<String>> waitable = new Waitable<List<String>>() {
|
||||
@Override
|
||||
@@ -0,0 +0,0 @@ public class ConsoleCommandCompleter implements Completer {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ private boolean notNewSuggestion(final List<com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion> completions, final String completion) {
|
||||
+ for (final com.destroystokyo.paper.event.server.AsyncTabCompleteEvent.Completion it : completions) {
|
||||
+ if (it.suggestion().equals(completion)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
|
|
@ -2835,8 +2835,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ // Paper end
|
||||
// CraftBukkit end
|
||||
StringReader stringreader = new StringReader(packet.getCommand());
|
||||
|
||||
// Paper start - async tab completion
|
||||
TAB_COMPLETE_EXECUTOR.execute(() -> {
|
||||
diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java
|
||||
|
|
|
@ -17,7 +17,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ if (org.spigotmc.SpigotConfig.tabComplete < 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ // Paper end - Don't suggest if tab-complete is disabled
|
||||
// Paper start - async tab completion
|
||||
TAB_COMPLETE_EXECUTOR.execute(() -> {
|
||||
StringReader stringreader = new StringReader(packet.getCommand());
|
||||
|
||||
if (stringreader.canRead() && stringreader.peek() == '/') {
|
||||
|
|
|
@ -86,33 +86,35 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
||||
ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
|
||||
|
||||
this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
||||
- if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
||||
- this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
||||
+ // Paper start
|
||||
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, buffer);
|
||||
+ suggestEvent.setCancelled(suggestions.isEmpty());
|
||||
+ if (!suggestEvent.callEvent()) return;
|
||||
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), (com.mojang.brigadier.suggestion.Suggestions) suggestEvent.getSuggestions())); // CraftBukkit - decompile error // Paper
|
||||
+ // Paper end
|
||||
this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
|
||||
- if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
|
||||
- this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
|
||||
+ // Paper start - Brigadier API
|
||||
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, command);
|
||||
+ suggestEvent.setCancelled(suggestions.isEmpty());
|
||||
+ if (!suggestEvent.callEvent()) return;
|
||||
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
|
||||
+ // Paper end - Brigadier API
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
|
||||
builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
|
||||
completions.forEach(builder::suggest);
|
||||
- player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
|
||||
+ com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join();
|
||||
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, buffer);
|
||||
+ suggestEvent.setCancelled(suggestions.isEmpty());
|
||||
+ if (!suggestEvent.callEvent()) return;
|
||||
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
|
||||
}
|
||||
builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip()));
|
||||
}
|
||||
});
|
||||
- player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
|
||||
+ // Paper start - Brigadier API
|
||||
+ com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join();
|
||||
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, command);
|
||||
+ suggestEvent.setCancelled(suggestions.isEmpty());
|
||||
+ if (!suggestEvent.callEvent()) return;
|
||||
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
|
||||
+ // Paper end - Brigadier API
|
||||
}
|
||||
});
|
||||
// Paper end - async tab completion
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
|
||||
|
|
Loading…
Reference in a new issue