minecraft-src/net/minecraft/client/multiplayer/ClientSuggestionProvider.java
2025-07-04 03:15:13 +03:00

188 lines
6.3 KiB
Java

package net.minecraft.client.multiplayer;
import com.google.common.collect.Lists;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.protocol.game.ClientboundCustomChatCompletionsPacket;
import net.minecraft.network.protocol.game.ServerboundCommandSuggestionPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.HitResult.Type;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class ClientSuggestionProvider implements SharedSuggestionProvider {
private final ClientPacketListener connection;
private final Minecraft minecraft;
private int pendingSuggestionsId = -1;
@Nullable
private CompletableFuture<Suggestions> pendingSuggestionsFuture;
private final Set<String> customCompletionSuggestions = new HashSet();
public ClientSuggestionProvider(ClientPacketListener connection, Minecraft minecraft) {
this.connection = connection;
this.minecraft = minecraft;
}
@Override
public Collection<String> getOnlinePlayerNames() {
List<String> list = Lists.<String>newArrayList();
for (PlayerInfo playerInfo : this.connection.getOnlinePlayers()) {
list.add(playerInfo.getProfile().getName());
}
return list;
}
@Override
public Collection<String> getCustomTabSugggestions() {
if (this.customCompletionSuggestions.isEmpty()) {
return this.getOnlinePlayerNames();
} else {
Set<String> set = new HashSet(this.getOnlinePlayerNames());
set.addAll(this.customCompletionSuggestions);
return set;
}
}
@Override
public Collection<String> getSelectedEntities() {
return (Collection<String>)(this.minecraft.hitResult != null && this.minecraft.hitResult.getType() == Type.ENTITY
? Collections.singleton(((EntityHitResult)this.minecraft.hitResult).getEntity().getStringUUID())
: Collections.emptyList());
}
@Override
public Collection<String> getAllTeams() {
return this.connection.scoreboard().getTeamNames();
}
@Override
public Stream<ResourceLocation> getAvailableSounds() {
return this.minecraft.getSoundManager().getAvailableSounds().stream();
}
@Override
public boolean hasPermission(int permissionLevel) {
LocalPlayer localPlayer = this.minecraft.player;
return localPlayer != null ? localPlayer.hasPermissions(permissionLevel) : permissionLevel == 0;
}
@Override
public CompletableFuture<Suggestions> suggestRegistryElements(
ResourceKey<? extends Registry<?>> resourceKey,
SharedSuggestionProvider.ElementSuggestionType registryKey,
SuggestionsBuilder builder,
CommandContext<?> context
) {
return (CompletableFuture<Suggestions>)this.registryAccess().lookup(resourceKey).map(registry -> {
this.suggestRegistryElements(registry, registryKey, builder);
return builder.buildFuture();
}).orElseGet(() -> this.customSuggestion(context));
}
@Override
public CompletableFuture<Suggestions> customSuggestion(CommandContext<?> context) {
if (this.pendingSuggestionsFuture != null) {
this.pendingSuggestionsFuture.cancel(false);
}
this.pendingSuggestionsFuture = new CompletableFuture();
int i = ++this.pendingSuggestionsId;
this.connection.send(new ServerboundCommandSuggestionPacket(i, context.getInput()));
return this.pendingSuggestionsFuture;
}
private static String prettyPrint(double doubleValue) {
return String.format(Locale.ROOT, "%.2f", doubleValue);
}
private static String prettyPrint(int intValue) {
return Integer.toString(intValue);
}
@Override
public Collection<SharedSuggestionProvider.TextCoordinates> getRelevantCoordinates() {
HitResult hitResult = this.minecraft.hitResult;
if (hitResult != null && hitResult.getType() == Type.BLOCK) {
BlockPos blockPos = ((BlockHitResult)hitResult).getBlockPos();
return Collections.singleton(
new SharedSuggestionProvider.TextCoordinates(prettyPrint(blockPos.getX()), prettyPrint(blockPos.getY()), prettyPrint(blockPos.getZ()))
);
} else {
return SharedSuggestionProvider.super.getRelevantCoordinates();
}
}
@Override
public Collection<SharedSuggestionProvider.TextCoordinates> getAbsoluteCoordinates() {
HitResult hitResult = this.minecraft.hitResult;
if (hitResult != null && hitResult.getType() == Type.BLOCK) {
Vec3 vec3 = hitResult.getLocation();
return Collections.singleton(new SharedSuggestionProvider.TextCoordinates(prettyPrint(vec3.x), prettyPrint(vec3.y), prettyPrint(vec3.z)));
} else {
return SharedSuggestionProvider.super.getAbsoluteCoordinates();
}
}
@Override
public Set<ResourceKey<Level>> levels() {
return this.connection.levels();
}
@Override
public RegistryAccess registryAccess() {
return this.connection.registryAccess();
}
@Override
public FeatureFlagSet enabledFeatures() {
return this.connection.enabledFeatures();
}
public void completeCustomSuggestions(int transaction, Suggestions result) {
if (transaction == this.pendingSuggestionsId) {
this.pendingSuggestionsFuture.complete(result);
this.pendingSuggestionsFuture = null;
this.pendingSuggestionsId = -1;
}
}
public void modifyCustomCompletions(ClientboundCustomChatCompletionsPacket.Action action, List<String> entries) {
switch (action) {
case ADD:
this.customCompletionSuggestions.addAll(entries);
break;
case REMOVE:
entries.forEach(this.customCompletionSuggestions::remove);
break;
case SET:
this.customCompletionSuggestions.clear();
this.customCompletionSuggestions.addAll(entries);
}
}
}