fabric: Support 1.19 (#356)

Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
This commit is contained in:
zml 2022-06-08 14:42:44 -07:00 committed by Jason
parent 28ff5d3003
commit 63f2c9299f
21 changed files with 370 additions and 199 deletions

View file

@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Paper: Improved KeyedWorldArgument suggestions ([#334](https://github.com/Incendo/cloud/pull/334)) - Paper: Improved KeyedWorldArgument suggestions ([#334](https://github.com/Incendo/cloud/pull/334))
- Minecraft: Support sender-aware description decorators in MinecraftHelp ([#354](https://github.com/Incendo/cloud/pull/354)) - Minecraft: Support sender-aware description decorators in MinecraftHelp ([#354](https://github.com/Incendo/cloud/pull/354))
### Changed
- Fabric: Updated for Minecraft 1.19 (no longer supports older versions)
## [1.6.2] ## [1.6.2]
### Fixed ### Fixed

View file

@ -8,6 +8,8 @@ repositories {
} }
dependencies { dependencies {
// loom needs this version of asm, for some reason we have an older one on the classpath without this
implementation("org.ow2.asm:asm:9.3")
implementation(libs.indraCommon) implementation(libs.indraCommon)
implementation(libs.indraPublishingSonatype) implementation(libs.indraPublishingSonatype)
implementation(libs.gradleTestLogger) implementation(libs.gradleTestLogger)

View file

@ -38,6 +38,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
@ -53,7 +54,7 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
public static final String COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER = "_cloud_brigadier_native_sender"; public static final String COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER = "_cloud_brigadier_native_sender";
private final ArgumentType<T> nativeType; private final Supplier<ArgumentType<T>> nativeType;
private final int expectedArgumentCount; private final int expectedArgumentCount;
/** /**
@ -63,6 +64,16 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
* @since 1.5.0 * @since 1.5.0
*/ */
public WrappedBrigadierParser(final ArgumentType<T> nativeType) { public WrappedBrigadierParser(final ArgumentType<T> nativeType) {
this(() -> nativeType, DEFAULT_ARGUMENT_COUNT);
}
/**
* Create an argument parser based on a brigadier command.
*
* @param nativeType the native command type, computed lazily
* @since 1.7.0
*/
public WrappedBrigadierParser(final Supplier<ArgumentType<T>> nativeType) {
this(nativeType, DEFAULT_ARGUMENT_COUNT); this(nativeType, DEFAULT_ARGUMENT_COUNT);
} }
@ -74,10 +85,25 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
* @since 1.5.0 * @since 1.5.0
*/ */
public WrappedBrigadierParser( public WrappedBrigadierParser(
final ArgumentType<T> nativeType, final ArgumentType<T> nativeType,
final int expectedArgumentCount
) {
this(() -> nativeType, expectedArgumentCount);
}
/**
* Create an argument parser based on a brigadier command.
*
* @param nativeType the native command type provider, calculated lazily
* @param expectedArgumentCount the number of arguments the brigadier type is expected to consume
* @since 1.7.0
*/
public WrappedBrigadierParser(
final Supplier<ArgumentType<T>> nativeType,
final int expectedArgumentCount final int expectedArgumentCount
) { ) {
this.nativeType = requireNonNull(nativeType, "brigadierType"); requireNonNull(nativeType, "brigadierType");
this.nativeType = nativeType;
this.expectedArgumentCount = expectedArgumentCount; this.expectedArgumentCount = expectedArgumentCount;
} }
@ -88,7 +114,7 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
* @since 1.5.0 * @since 1.5.0
*/ */
public ArgumentType<T> getNativeArgument() { public ArgumentType<T> getNativeArgument() {
return this.nativeType; return this.nativeType.get();
} }
@Override @Override
@ -109,7 +135,7 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
// Then try to parse // Then try to parse
try { try {
return ArgumentParseResult.success(this.nativeType.parse(reader)); return ArgumentParseResult.success(this.nativeType.get().parse(reader));
} catch (final CommandSyntaxException ex) { } catch (final CommandSyntaxException ex) {
return ArgumentParseResult.failure(ex); return ArgumentParseResult.failure(ex);
} finally { } finally {
@ -142,7 +168,7 @@ public final class WrappedBrigadierParser<C, T> implements ArgumentParser<C, T>
false false
); );
final CompletableFuture<Suggestions> result = this.nativeType.listSuggestions( final CompletableFuture<Suggestions> result = this.nativeType.get().listSuggestions(
reverseMappedContext, reverseMappedContext,
new SuggestionsBuilder(input, 0) new SuggestionsBuilder(input, 0)
); );

View file

@ -2,7 +2,7 @@ import net.fabricmc.loom.task.AbstractRunTask
import net.ltgt.gradle.errorprone.errorprone import net.ltgt.gradle.errorprone.errorprone
plugins { plugins {
id("quiet-fabric-loom") version "0.11-SNAPSHOT" id("quiet-fabric-loom") version "0.12-SNAPSHOT"
id("cloud.base-conventions") id("cloud.base-conventions")
} }
@ -44,7 +44,8 @@ dependencies {
minecraft(libs.fabricMinecraft) minecraft(libs.fabricMinecraft)
mappings(loom.officialMojangMappings()) mappings(loom.officialMojangMappings())
modImplementation(libs.fabricLoader) modImplementation(libs.fabricLoader)
modImplementation(fabricApi.module("fabric-command-api-v1", libs.versions.fabricApi.get())) modImplementation(fabricApi.module("fabric-command-api-v2", libs.versions.fabricApi.get()))
modImplementation(fabricApi.module("fabric-networking-api-v1", libs.versions.fabricApi.get()))
modImplementation(fabricApi.module("fabric-lifecycle-events-v1", libs.versions.fabricApi.get())) modImplementation(fabricApi.module("fabric-lifecycle-events-v1", libs.versions.fabricApi.get()))
modImplementation(libs.fabricPermissionsApi) modImplementation(libs.fabricPermissionsApi)

View file

@ -28,8 +28,8 @@ import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator;
import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.permission.PredicatePermission; import cloud.commandframework.permission.PredicatePermission;
import java.util.function.Function; import java.util.function.Function;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientSuggestionProvider; import net.minecraft.client.multiplayer.ClientSuggestionProvider;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -165,7 +165,8 @@ public final class FabricClientCommandManager<C> extends FabricCommandManager<C,
if (!Minecraft.getInstance().hasSingleplayerServer()) { if (!Minecraft.getInstance().hasSingleplayerServer()) {
return allowOnMultiplayer; return allowOnMultiplayer;
} }
return Minecraft.getInstance().getSingleplayerServer().getPlayerList().isAllowCheatsForAllPlayers(); return Minecraft.getInstance().getSingleplayerServer().getPlayerList().isAllowCheatsForAllPlayers()
|| Minecraft.getInstance().getSingleplayerServer().getWorldData().getAllowCommands();
}; };
} }
@ -198,7 +199,8 @@ public final class FabricClientCommandManager<C> extends FabricCommandManager<C,
if (!Minecraft.getInstance().hasSingleplayerServer()) { if (!Minecraft.getInstance().hasSingleplayerServer()) {
return allowOnMultiplayer; return allowOnMultiplayer;
} }
return !Minecraft.getInstance().getSingleplayerServer().getPlayerList().isAllowCheatsForAllPlayers(); return !Minecraft.getInstance().getSingleplayerServer().getPlayerList().isAllowCheatsForAllPlayers()
&& !Minecraft.getInstance().getSingleplayerServer().getWorldData().getAllowCommands();
}; };
} }

View file

@ -40,7 +40,6 @@ import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta; import cloud.commandframework.meta.SimpleCommandMeta;
import cloud.commandframework.permission.PredicatePermission; import cloud.commandframework.permission.PredicatePermission;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import io.leangen.geantyref.GenericTypeReflector; import io.leangen.geantyref.GenericTypeReflector;
import io.leangen.geantyref.TypeToken; import io.leangen.geantyref.TypeToken;
@ -56,16 +55,13 @@ import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.advancements.critereon.MinMaxBounds; import net.minecraft.advancements.critereon.MinMaxBounds;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.AngleArgument; import net.minecraft.commands.arguments.AngleArgument;
import net.minecraft.commands.arguments.ColorArgument; import net.minecraft.commands.arguments.ColorArgument;
import net.minecraft.commands.arguments.CompoundTagArgument; import net.minecraft.commands.arguments.CompoundTagArgument;
import net.minecraft.commands.arguments.DimensionArgument;
import net.minecraft.commands.arguments.EntityAnchorArgument; import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.commands.arguments.EntitySummonArgument;
import net.minecraft.commands.arguments.ItemEnchantmentArgument;
import net.minecraft.commands.arguments.MessageArgument; import net.minecraft.commands.arguments.MessageArgument;
import net.minecraft.commands.arguments.MobEffectArgument;
import net.minecraft.commands.arguments.NbtPathArgument; import net.minecraft.commands.arguments.NbtPathArgument;
import net.minecraft.commands.arguments.NbtTagArgument; import net.minecraft.commands.arguments.NbtTagArgument;
import net.minecraft.commands.arguments.ObjectiveCriteriaArgument; import net.minecraft.commands.arguments.ObjectiveCriteriaArgument;
@ -73,12 +69,12 @@ import net.minecraft.commands.arguments.OperationArgument;
import net.minecraft.commands.arguments.ParticleArgument; import net.minecraft.commands.arguments.ParticleArgument;
import net.minecraft.commands.arguments.RangeArgument; import net.minecraft.commands.arguments.RangeArgument;
import net.minecraft.commands.arguments.ResourceLocationArgument; import net.minecraft.commands.arguments.ResourceLocationArgument;
import net.minecraft.commands.arguments.ResourceOrTagLocationArgument;
import net.minecraft.commands.arguments.UuidArgument; import net.minecraft.commands.arguments.UuidArgument;
import net.minecraft.commands.arguments.blocks.BlockPredicateArgument; import net.minecraft.commands.arguments.blocks.BlockPredicateArgument;
import net.minecraft.commands.arguments.coordinates.SwizzleArgument; import net.minecraft.commands.arguments.coordinates.SwizzleArgument;
import net.minecraft.commands.arguments.item.ItemArgument; import net.minecraft.commands.arguments.item.ItemArgument;
import net.minecraft.commands.arguments.item.ItemInput; import net.minecraft.commands.arguments.item.ItemInput;
import net.minecraft.commands.synchronization.SuggestionProviders;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.particles.ParticleOptions; import net.minecraft.core.particles.ParticleOptions;
@ -180,64 +176,26 @@ public abstract class FabricCommandManager<C, S extends SharedSuggestionProvider
this.registerConstantNativeParserSupplier(OperationArgument.Operation.class, OperationArgument.operation()); this.registerConstantNativeParserSupplier(OperationArgument.Operation.class, OperationArgument.operation());
this.registerConstantNativeParserSupplier(ParticleOptions.class, ParticleArgument.particle()); this.registerConstantNativeParserSupplier(ParticleOptions.class, ParticleArgument.particle());
this.registerConstantNativeParserSupplier(AngleArgument.SingleAngle.class, AngleArgument.angle()); this.registerConstantNativeParserSupplier(AngleArgument.SingleAngle.class, AngleArgument.angle());
this.registerConstantNativeParserSupplier(new TypeToken<EnumSet<Direction.Axis>>() { this.registerConstantNativeParserSupplier(new TypeToken<EnumSet<Direction.Axis>>() {}, SwizzleArgument.swizzle());
}, SwizzleArgument.swizzle());
this.registerConstantNativeParserSupplier(ResourceLocation.class, ResourceLocationArgument.id()); this.registerConstantNativeParserSupplier(ResourceLocation.class, ResourceLocationArgument.id());
this.registerConstantNativeParserSupplier( this.registerConstantNativeParserSupplier(EntityAnchorArgument.Anchor.class, EntityAnchorArgument.anchor());
EntityAnchorArgument.Anchor.class,
EntityAnchorArgument.anchor()
);
this.registerConstantNativeParserSupplier(MinMaxBounds.Ints.class, RangeArgument.intRange()); this.registerConstantNativeParserSupplier(MinMaxBounds.Ints.class, RangeArgument.intRange());
this.registerConstantNativeParserSupplier(MinMaxBounds.Floats.class, RangeArgument.floatRange()); this.registerConstantNativeParserSupplier(MinMaxBounds.Doubles.class, RangeArgument.floatRange());
this.registerConstantNativeParserSupplier(ItemInput.class, ItemArgument.item()); this.registerContextualNativeParserSupplier(ItemInput.class, ItemArgument::item);
this.registerContextualNativeParserSupplier(BlockPredicateArgument.Result.class, BlockPredicateArgument::blockPredicate);
/* Wrapped/Constant Brigadier types, mapped value type */ /* Wrapped/Constant Brigadier types, mapped value type */
this.registerConstantNativeParserSupplier(
BlockPredicateArgument.Result.class,
BlockPredicateArgument.blockPredicate()
);
this.registerConstantNativeParserSupplier(MessageArgument.Message.class, MessageArgument.message()); this.registerConstantNativeParserSupplier(MessageArgument.Message.class, MessageArgument.message());
this.getParserRegistry().registerParserSupplier( this.getParserRegistry().registerParserSupplier(TypeToken.get(MinecraftTime.class), params -> FabricArgumentParsers.time());
TypeToken.get(MinecraftTime.class),
params -> FabricArgumentParsers.time()
);
} }
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
private void registerRegistryEntryMappings() { private void registerRegistryEntryMappings() {
this.brigadierManager.registerMapping( this.brigadierManager.registerMapping(
new TypeToken<RegistryEntryArgument.Parser<C, ?>>() { new TypeToken<RegistryEntryArgument.Parser<C, ?>>() {},
}, builder -> {
builder -> builder.to(argument -> { builder.to(argument -> ResourceOrTagLocationArgument.<Object>resourceOrTag((ResourceKey) argument.registryKey()));
/* several registries have specialized argument types, so let's use those where possible */ }
final ResourceKey<? extends Registry<?>> registry = argument.registryKey();
if (registry.equals(Registry.ENTITY_TYPE_REGISTRY)) {
return EntitySummonArgument.id();
} else if (registry.equals(Registry.ENCHANTMENT_REGISTRY)) {
return ItemEnchantmentArgument.enchantment();
} else if (registry.equals(Registry.MOB_EFFECT_REGISTRY)) {
return MobEffectArgument.effect();
} else if (registry.equals(Registry.DIMENSION_REGISTRY)) {
return DimensionArgument.dimension();
}
return ResourceLocationArgument.id();
}
).suggestedBy((argument, useCloud) -> {
/* A few other registries have client-side suggestion providers but no argument type */
/* Type parameters are messed up here for some reason */
final ResourceKey<? extends Registry<?>> registry = argument.registryKey();
if (registry.equals(Registry.SOUND_EVENT_REGISTRY)) {
return (SuggestionProvider<S>) SuggestionProviders.AVAILABLE_SOUNDS;
} else if (registry.equals(Registry.BIOME_REGISTRY)) {
return (SuggestionProvider<S>) SuggestionProviders.AVAILABLE_BIOMES;
} else if (registry.equals(Registry.ENTITY_TYPE_REGISTRY)
|| registry.equals(Registry.ENCHANTMENT_REGISTRY)
|| registry.equals(Registry.MOB_EFFECT_REGISTRY)
|| registry.equals(Registry.DIMENSION_REGISTRY)) {
return null; /* for types with their own argument type, use Brigadier */
}
return useCloud; /* use cloud suggestions for anything else */
})
); );
/* Find all fields of RegistryKey<? extends Registry<?>> and register those */ /* Find all fields of RegistryKey<? extends Registry<?>> and register those */
@ -292,6 +250,21 @@ public abstract class FabricCommandManager<C, S extends SharedSuggestionProvider
} }
} }
/**
* Register a parser supplier for a brigadier type that has no options and whose output can be directly used.
*
* @param type the Java type to map
* @param argument a function providing the Brigadier parser given a build context
* @param <T> value type
* @since 1.7.0
*/
final <T> void registerContextualNativeParserSupplier(
final @NonNull Class<T> type,
final @NonNull Function<CommandBuildContext, @NonNull ArgumentType<T>> argument
) {
this.getParserRegistry().registerParserSupplier(TypeToken.get(type), params -> FabricArgumentParsers.contextual(argument));
}
/** /**
* Register a parser supplier for a brigadier type that has no options and whose output can be directly used. * Register a parser supplier for a brigadier type that has no options and whose output can be directly used.
* *

View file

@ -25,6 +25,7 @@ package cloud.commandframework.fabric;
import cloud.commandframework.Command; import cloud.commandframework.Command;
import cloud.commandframework.arguments.StaticArgument; import cloud.commandframework.arguments.StaticArgument;
import cloud.commandframework.fabric.argument.FabricArgumentParsers;
import cloud.commandframework.internal.CommandRegistrationHandler; import cloud.commandframework.internal.CommandRegistrationHandler;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@ -33,11 +34,16 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode; import com.mojang.brigadier.tree.RootCommandNode;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands.CommandSelection; import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -51,6 +57,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
* @param <S> native sender type * @param <S> native sender type
*/ */
abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionProvider> implements CommandRegistrationHandler { abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionProvider> implements CommandRegistrationHandler {
private @MonotonicNonNull FabricCommandManager<C, S> commandManager; private @MonotonicNonNull FabricCommandManager<C, S> commandManager;
void initialize(final FabricCommandManager<C, S> manager) { void initialize(final FabricCommandManager<C, S> manager) {
@ -71,7 +78,7 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
* *
* @param alias the command alias * @param alias the command alias
* @param destination the destination node * @param destination the destination node
* @param <S> brig sender type * @param <S> brig sender type
* @return the built node * @return the built node
*/ */
private static <S> LiteralCommandNode<S> buildRedirect( private static <S> LiteralCommandNode<S> buildRedirect(
@ -81,7 +88,7 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
// Redirects only work for nodes with children, but break the top argument-less command. // Redirects only work for nodes with children, but break the top argument-less command.
// Manually adding the root command after setting the redirect doesn't fix it. // Manually adding the root command after setting the redirect doesn't fix it.
// (See https://github.com/Mojang/brigadier/issues/46) Manually clone the node instead. // (See https://github.com/Mojang/brigadier/issues/46) Manually clone the node instead.
LiteralArgumentBuilder<S> builder = LiteralArgumentBuilder final LiteralArgumentBuilder<S> builder = LiteralArgumentBuilder
.<S>literal(alias) .<S>literal(alias)
.requires(destination.getRequirement()) .requires(destination.getRequirement())
.forward( .forward(
@ -97,19 +104,65 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
} }
static class Client<C> extends FabricCommandRegistrationHandler<C, FabricClientCommandSource> { static class Client<C> extends FabricCommandRegistrationHandler<C, FabricClientCommandSource> {
private final Set<Command<C>> registeredCommands = ConcurrentHashMap.newKeySet();
private volatile boolean registerEventFired = false;
@Override @Override
void initialize(final FabricCommandManager<C, FabricClientCommandSource> manager) { void initialize(final FabricCommandManager<C, FabricClientCommandSource> manager) {
super.initialize(manager); super.initialize(manager);
ClientCommandRegistrationCallback.EVENT.register(this::registerCommands);
ClientPlayConnectionEvents.DISCONNECT.register(($, $$) -> this.registerEventFired = false);
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public boolean registerCommand(final @NonNull Command<?> cmd) { public boolean registerCommand(final @NonNull Command<?> command) {
final Command<C> command = (Command<C>) cmd; this.registeredCommands.add((Command<C>) command);
final RootCommandNode<FabricClientCommandSource> rootNode = ClientCommandManager.DISPATCHER.getRoot(); if (this.registerEventFired) {
final ClientPacketListener connection = Minecraft.getInstance().getConnection();
if (connection == null) {
throw new IllegalStateException("Expected connection to be present but it wasn't!");
}
final CommandDispatcher<FabricClientCommandSource> dispatcher = ClientCommandManager.getActiveDispatcher();
if (dispatcher == null) {
throw new IllegalStateException("Expected an active dispatcher!");
}
FabricArgumentParsers.ContextualArgumentTypeProvider.withBuildContext(
this.commandManager(),
new CommandBuildContext(connection.registryAccess()),
false,
() -> this.registerClientCommand(dispatcher, (Command<C>) command)
);
}
return true;
}
public void registerCommands(
final CommandDispatcher<FabricClientCommandSource> dispatcher,
final CommandBuildContext commandBuildContext
) {
this.registerEventFired = true;
FabricArgumentParsers.ContextualArgumentTypeProvider.withBuildContext(
this.commandManager(),
commandBuildContext,
true,
() -> {
for (final Command<C> command : this.registeredCommands) {
this.registerClientCommand(dispatcher, command);
}
}
);
}
@SuppressWarnings("unchecked")
private void registerClientCommand(
final CommandDispatcher<FabricClientCommandSource> dispatcher,
final Command<C> command
) {
final RootCommandNode<FabricClientCommandSource> rootNode = dispatcher.getRoot();
final StaticArgument<C> first = ((StaticArgument<C>) command.getArguments().get(0)); final StaticArgument<C> first = ((StaticArgument<C>) command.getArguments().get(0));
final CommandNode<FabricClientCommandSource> baseNode = this final CommandNode<FabricClientCommandSource> baseNode = this.commandManager()
.commandManager()
.brigadierManager() .brigadierManager()
.createLiteralCommandNode( .createLiteralCommandNode(
first.getName(), first.getName(),
@ -131,11 +184,11 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
for (final String alias : first.getAlternativeAliases()) { for (final String alias : first.getAlternativeAliases()) {
rootNode.addChild(buildRedirect(alias, baseNode)); rootNode.addChild(buildRedirect(alias, baseNode));
} }
return true;
} }
} }
static class Server<C> extends FabricCommandRegistrationHandler<C, CommandSourceStack> { static class Server<C> extends FabricCommandRegistrationHandler<C, CommandSourceStack> {
private final Set<Command<C>> registeredCommands = ConcurrentHashMap.newKeySet(); private final Set<Command<C>> registeredCommands = ConcurrentHashMap.newKeySet();
@Override @Override
@ -150,21 +203,32 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
return this.registeredCommands.add((Command<C>) command); return this.registeredCommands.add((Command<C>) command);
} }
private void registerAllCommands(final CommandDispatcher<CommandSourceStack> dispatcher, final boolean isDedicated) { private void registerAllCommands(
final CommandDispatcher<CommandSourceStack> dispatcher,
final CommandBuildContext access,
final Commands.CommandSelection side
) {
this.commandManager().registrationCalled(); this.commandManager().registrationCalled();
for (final Command<C> command : this.registeredCommands) { FabricArgumentParsers.ContextualArgumentTypeProvider.withBuildContext(
/* Only register commands in the declared environment */ this.commandManager(),
final CommandSelection env = command.getCommandMeta().getOrDefault( access,
FabricServerCommandManager.META_REGISTRATION_ENVIRONMENT, true,
CommandSelection.ALL () -> {
); for (final Command<C> command : this.registeredCommands) {
/* Only register commands in the declared environment */
final Commands.CommandSelection env = command.getCommandMeta().getOrDefault(
FabricServerCommandManager.META_REGISTRATION_ENVIRONMENT,
Commands.CommandSelection.ALL
);
if ((env == CommandSelection.INTEGRATED && isDedicated) if ((env == Commands.CommandSelection.INTEGRATED && !side.includeIntegrated)
|| (env == CommandSelection.DEDICATED && !isDedicated)) { || (env == Commands.CommandSelection.DEDICATED && !side.includeDedicated)) {
continue; continue;
}
this.registerCommand(dispatcher.getRoot(), command);
}
} }
this.registerCommand(dispatcher.getRoot(), command); );
}
} }
private void registerCommand(final RootCommandNode<CommandSourceStack> dispatcher, final Command<C> command) { private void registerCommand(final RootCommandNode<CommandSourceStack> dispatcher, final Command<C> command) {
@ -178,7 +242,8 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
perm perm
), ),
true, true,
new FabricExecutor<>(this.commandManager(), CommandSourceStack::getTextName, CommandSourceStack::sendFailure)); new FabricExecutor<>(this.commandManager(), CommandSourceStack::getTextName, CommandSourceStack::sendFailure)
);
dispatcher.addChild(baseNode); dispatcher.addChild(baseNode);
@ -186,5 +251,7 @@ abstract class FabricCommandRegistrationHandler<C, S extends SharedSuggestionPro
dispatcher.addChild(buildRedirect(alias, baseNode)); dispatcher.addChild(buildRedirect(alias, baseNode));
} }
} }
} }
} }

View file

@ -32,6 +32,7 @@ import cloud.commandframework.exceptions.NoSuchCommandException;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.logging.LogUtils;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
@ -44,16 +45,13 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Command<S> { final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Command<S> {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Logger LOGGER = LogManager.getLogger(); private static final Component NEWLINE = Component.literal("\n");
private static final Component NEWLINE = new TextComponent("\n");
private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command."; private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command.";
private static final String MESSAGE_NO_PERMS = private static final String MESSAGE_NO_PERMS =
"I'm sorry, but you do not have permission to perform this command. " "I'm sorry, but you do not have permission to perform this command. "
@ -79,6 +77,7 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
final S source = ctx.getSource(); final S source = ctx.getSource();
final String input = ctx.getInput().substring(ctx.getLastChild().getNodes().get(0).getRange().getStart()); final String input = ctx.getInput().substring(ctx.getLastChild().getNodes().get(0).getRange().getStart());
final C sender = this.manager.commandSourceMapper().apply(source); final C sender = this.manager.commandSourceMapper().apply(source);
this.manager.executeCommand(sender, input).whenComplete((result, throwable) -> { this.manager.executeCommand(sender, input).whenComplete((result, throwable) -> {
if (throwable == null) { if (throwable == null) {
return; return;
@ -99,8 +98,8 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
(InvalidSyntaxException) throwable, (InvalidSyntaxException) throwable,
(c, e) -> this.sendError.accept( (c, e) -> this.sendError.accept(
source, source,
new TextComponent("Invalid Command Syntax. Correct command syntax is: ") Component.literal("Invalid Command Syntax. Correct command syntax is: ")
.append(new TextComponent(String.format("/%s", e.getCorrectSyntax())) .append(Component.literal(String.format("/%s", e.getCorrectSyntax()))
.withStyle(style -> style.withColor(ChatFormatting.GRAY))) .withStyle(style -> style.withColor(ChatFormatting.GRAY)))
) )
); );
@ -109,21 +108,21 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
sender, sender,
InvalidCommandSenderException.class, InvalidCommandSenderException.class,
(InvalidCommandSenderException) throwable, (InvalidCommandSenderException) throwable,
(c, e) -> this.sendError.accept(source, new TextComponent(throwable.getMessage())) (c, e) -> this.sendError.accept(source, Component.literal(throwable.getMessage()))
); );
} else if (throwable instanceof NoPermissionException) { } else if (throwable instanceof NoPermissionException) {
this.manager.handleException( this.manager.handleException(
sender, sender,
NoPermissionException.class, NoPermissionException.class,
(NoPermissionException) throwable, (NoPermissionException) throwable,
(c, e) -> this.sendError.accept(source, new TextComponent(MESSAGE_NO_PERMS)) (c, e) -> this.sendError.accept(source, Component.literal(MESSAGE_NO_PERMS))
); );
} else if (throwable instanceof NoSuchCommandException) { } else if (throwable instanceof NoSuchCommandException) {
this.manager.handleException( this.manager.handleException(
sender, sender,
NoSuchCommandException.class, NoSuchCommandException.class,
(NoSuchCommandException) throwable, (NoSuchCommandException) throwable,
(c, e) -> this.sendError.accept(source, new TextComponent(MESSAGE_UNKNOWN_COMMAND)) (c, e) -> this.sendError.accept(source, Component.literal(MESSAGE_UNKNOWN_COMMAND))
); );
} else if (throwable instanceof ArgumentParseException) { } else if (throwable instanceof ArgumentParseException) {
this.manager.handleException( this.manager.handleException(
@ -132,14 +131,14 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
(ArgumentParseException) throwable, (ArgumentParseException) throwable,
(c, e) -> { (c, e) -> {
if (throwable.getCause() instanceof CommandSyntaxException) { if (throwable.getCause() instanceof CommandSyntaxException) {
this.sendError.accept(source, new TextComponent("Invalid Command Argument: ") this.sendError.accept(source, Component.literal("Invalid Command Argument: ")
.append(new TextComponent("") .append(Component.literal("")
.append(ComponentUtils .append(ComponentUtils
.fromMessage(((CommandSyntaxException) throwable.getCause()).getRawMessage())) .fromMessage(((CommandSyntaxException) throwable.getCause()).getRawMessage()))
.withStyle(ChatFormatting.GRAY))); .withStyle(ChatFormatting.GRAY)));
} else { } else {
this.sendError.accept(source, new TextComponent("Invalid Command Argument: ") this.sendError.accept(source, Component.literal("Invalid Command Argument: ")
.append(new TextComponent(throwable.getCause().getMessage()) .append(Component.literal(throwable.getCause().getMessage())
.withStyle(ChatFormatting.GRAY))); .withStyle(ChatFormatting.GRAY)));
} }
} }
@ -151,7 +150,7 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
(CommandExecutionException) throwable, (CommandExecutionException) throwable,
(c, e) -> { (c, e) -> {
this.sendError.accept(source, this.decorateHoverStacktrace( this.sendError.accept(source, this.decorateHoverStacktrace(
new TextComponent(MESSAGE_INTERNAL_ERROR), Component.literal(MESSAGE_INTERNAL_ERROR),
throwable.getCause(), throwable.getCause(),
sender sender
)); ));
@ -164,7 +163,7 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
); );
} else { } else {
this.sendError.accept(source, this.decorateHoverStacktrace( this.sendError.accept(source, this.decorateHoverStacktrace(
new TextComponent(MESSAGE_INTERNAL_ERROR), Component.literal(MESSAGE_INTERNAL_ERROR),
throwable, throwable,
sender sender
)); ));
@ -183,9 +182,9 @@ final class FabricExecutor<C, S extends SharedSuggestionProvider> implements Com
return input.withStyle(style -> style return input.withStyle(style -> style
.withHoverEvent(new HoverEvent( .withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, HoverEvent.Action.SHOW_TEXT,
new TextComponent(stackTrace) Component.literal(stackTrace)
.append(NEWLINE) .append(NEWLINE)
.append(new TextComponent(" Click to copy") .append(Component.literal(" Click to copy")
.withStyle(s2 -> s2.withColor(ChatFormatting.GRAY).withItalic(true))) .withStyle(s2 -> s2.withColor(ChatFormatting.GRAY).withItalic(true)))
)) ))
.withClickEvent(new ClickEvent( .withClickEvent(new ClickEvent(

View file

@ -40,11 +40,11 @@ import cloud.commandframework.meta.CommandMeta;
import io.leangen.geantyref.TypeToken; import io.leangen.geantyref.TypeToken;
import java.util.function.Function; import java.util.function.Function;
import me.lucko.fabric.api.permissions.v0.Permissions; import me.lucko.fabric.api.permissions.v0.Permissions;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.commands.CommandSource; import net.minecraft.commands.CommandSource;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands; import net.minecraft.commands.Commands;
import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.Component;
import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -121,7 +121,7 @@ public final class FabricServerCommandManager<C> extends FabricCommandManager<C,
null, null,
4, 4,
"", "",
TextComponent.EMPTY, Component.empty(),
null, null,
null null
) )

View file

@ -28,6 +28,7 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.fabric.FabricCommandContextKeys; import cloud.commandframework.fabric.FabricCommandContextKeys;
import cloud.commandframework.fabric.FabricCommandManager;
import cloud.commandframework.fabric.data.Coordinates; import cloud.commandframework.fabric.data.Coordinates;
import cloud.commandframework.fabric.data.Message; import cloud.commandframework.fabric.data.Message;
import cloud.commandframework.fabric.data.MinecraftTime; import cloud.commandframework.fabric.data.MinecraftTime;
@ -38,11 +39,17 @@ import cloud.commandframework.fabric.data.SinglePlayerSelector;
import cloud.commandframework.fabric.internal.EntitySelectorAccess; import cloud.commandframework.fabric.internal.EntitySelectorAccess;
import cloud.commandframework.fabric.mixin.MessageArgumentMessageAccess; import cloud.commandframework.fabric.mixin.MessageArgumentMessageAccess;
import cloud.commandframework.fabric.mixin.MessageArgumentPartAccess; import cloud.commandframework.fabric.mixin.MessageArgumentPartAccess;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.EntityArgument;
@ -59,6 +66,7 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.jetbrains.annotations.ApiStatus;
/** /**
* Parsers for Vanilla command argument types. * Parsers for Vanilla command argument types.
@ -70,6 +78,17 @@ public final class FabricArgumentParsers {
private FabricArgumentParsers() { private FabricArgumentParsers() {
} }
/**
* A parser that wraps Brigadier argument types which need a {@link CommandBuildContext}
* @param <C> sender type
* @param <V> argument value type
* @param factory factory that creates these arguments
* @return the parser
*/
public static <C, V> @NonNull ArgumentParser<C, V> contextual(final @NonNull Function<CommandBuildContext, ArgumentType<V>> factory) {
return new WrappedBrigadierParser<>(new ContextualArgumentTypeProvider<>(factory));
}
/** /**
* A parser for in-game time, in ticks. * A parser for in-game time, in ticks.
* *
@ -505,4 +524,94 @@ public final class FabricArgumentParsers {
} }
@ApiStatus.Internal
public static final class ContextualArgumentTypeProvider<V> implements Supplier<ArgumentType<V>> {
private static final ThreadLocal<ThreadLocalContext> CONTEXT = new ThreadLocal<>();
private static final Map<FabricCommandManager<?, ?>, Set<ContextualArgumentTypeProvider<?>>> INSTANCES =
new WeakHashMap<>();
private final Function<CommandBuildContext, ArgumentType<V>> provider;
private volatile ArgumentType<V> provided;
/**
* Temporarily expose a command build context to providers called from this thread.
*
* @param ctx the context
* @param commandManager command manager to use
* @param resetExisting whether to clear cached state from existing provider instances for this command type
* @param action an action to perform while the context is exposed
* @since 1.7.0
*/
public static void withBuildContext(
final FabricCommandManager<?, ?> commandManager,
final CommandBuildContext ctx,
final boolean resetExisting,
final Runnable action
) {
final ThreadLocalContext context = new ThreadLocalContext(commandManager, ctx);
CONTEXT.set(context);
try {
if (resetExisting) {
synchronized (INSTANCES) {
for (final ContextualArgumentTypeProvider<?> contextualArgumentTypeProvider : context.instances()) {
contextualArgumentTypeProvider.provided = null;
}
}
}
action.run();
} finally {
CONTEXT.remove();
}
}
private static final class ThreadLocalContext {
private final FabricCommandManager<?, ?> commandManager;
private final CommandBuildContext commandBuildContext;
private ThreadLocalContext(
final FabricCommandManager<?, ?> commandManager,
final CommandBuildContext commandBuildContext
) {
this.commandManager = commandManager;
this.commandBuildContext = commandBuildContext;
}
private Set<ContextualArgumentTypeProvider<?>> instances() {
return INSTANCES.computeIfAbsent(this.commandManager, $ -> Collections.newSetFromMap(new WeakHashMap<>()));
}
}
ContextualArgumentTypeProvider(final @NonNull Function<CommandBuildContext, ArgumentType<V>> provider) {
this.provider = provider;
}
@Override
public ArgumentType<V> get() {
final ThreadLocalContext ctx = CONTEXT.get();
if (ctx != null) {
synchronized (INSTANCES) {
ctx.instances().add(this);
}
}
ArgumentType<V> provided = this.provided;
if (provided == null) {
synchronized (this) {
if (this.provided == null) {
if (ctx == null) {
throw new IllegalStateException("No build context was available while trying to compute an argument type");
}
provided = this.provider.apply(ctx.commandBuildContext);
this.provided = provided;
}
}
}
return provided;
}
}
} }

View file

@ -35,13 +35,13 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
/** /**
* An argument parsing an unbounded {@link net.minecraft.advancements.critereon.MinMaxBounds.Floats float range}, in the form * An argument parsing an unbounded {@link net.minecraft.advancements.critereon.MinMaxBounds.Doubles double range}, in the form
* {@code [min]..[max]}, where both lower and upper bounds are optional. * {@code [min]..[max]}, where both lower and upper bounds are optional.
* *
* @param <C> the sender type * @param <C> the sender type
* @since 1.5.0 * @since 1.5.0
*/ */
public final class FloatRangeArgument<C> extends CommandArgument<C, MinMaxBounds.Floats> { public final class FloatRangeArgument<C> extends CommandArgument<C, MinMaxBounds.Doubles> {
FloatRangeArgument( FloatRangeArgument(
final boolean required, final boolean required,
@ -55,7 +55,7 @@ public final class FloatRangeArgument<C> extends CommandArgument<C, MinMaxBounds
name, name,
new WrappedBrigadierParser<>(RangeArgument.floatRange()), new WrappedBrigadierParser<>(RangeArgument.floatRange()),
defaultValue, defaultValue,
MinMaxBounds.Floats.class, MinMaxBounds.Doubles.class,
suggestionsProvider, suggestionsProvider,
defaultDescription defaultDescription
); );
@ -108,7 +108,7 @@ public final class FloatRangeArgument<C> extends CommandArgument<C, MinMaxBounds
*/ */
public static <C> @NonNull FloatRangeArgument<C> optional( public static <C> @NonNull FloatRangeArgument<C> optional(
final @NonNull String name, final @NonNull String name,
final MinMaxBounds.@NonNull Floats defaultValue final MinMaxBounds.@NonNull Doubles defaultValue
) { ) {
return FloatRangeArgument.<C>builder(name).asOptionalWithDefault(defaultValue).build(); return FloatRangeArgument.<C>builder(name).asOptionalWithDefault(defaultValue).build();
} }
@ -120,10 +120,10 @@ public final class FloatRangeArgument<C> extends CommandArgument<C, MinMaxBounds
* @param <C> sender type * @param <C> sender type
* @since 1.5.0 * @since 1.5.0
*/ */
public static final class Builder<C> extends TypedBuilder<C, MinMaxBounds.Floats, Builder<C>> { public static final class Builder<C> extends TypedBuilder<C, MinMaxBounds.Doubles, Builder<C>> {
Builder(final @NonNull String name) { Builder(final @NonNull String name) {
super(MinMaxBounds.Floats.class, name); super(MinMaxBounds.Doubles.class, name);
} }
/** /**
@ -151,7 +151,7 @@ public final class FloatRangeArgument<C> extends CommandArgument<C, MinMaxBounds
* @see CommandArgument.Builder#asOptionalWithDefault(String) * @see CommandArgument.Builder#asOptionalWithDefault(String)
* @since 1.5.0 * @since 1.5.0
*/ */
public @NonNull Builder<C> asOptionalWithDefault(final MinMaxBounds.@NonNull Floats defaultValue) { public @NonNull Builder<C> asOptionalWithDefault(final MinMaxBounds.@NonNull Doubles defaultValue) {
final StringBuilder value = new StringBuilder(6); final StringBuilder value = new StringBuilder(6);
if (defaultValue.getMin() != null) { if (defaultValue.getMin() != null) {
value.append(defaultValue.getMin()); value.append(defaultValue.getMin());

View file

@ -25,7 +25,6 @@ package cloud.commandframework.fabric.argument;
import cloud.commandframework.ArgumentDescription; import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.arguments.CommandArgument; import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.brigadier.argument.WrappedBrigadierParser;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import java.util.List; import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -54,7 +53,7 @@ public final class ItemInputArgument<C> extends CommandArgument<C, ItemInput> {
super( super(
required, required,
name, name,
new WrappedBrigadierParser<>(ItemArgument.item()), FabricArgumentParsers.contextual(ItemArgument::item),
defaultValue, defaultValue,
ItemInput.class, ItemInput.class,
suggestionsProvider, suggestionsProvider,

View file

@ -28,7 +28,7 @@ import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.context.CommandContext; import cloud.commandframework.context.CommandContext;
import cloud.commandframework.fabric.FabricCommandContextKeys; import cloud.commandframework.fabric.FabricCommandContextKeys;
import java.util.Queue; import java.util.Queue;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;

View file

@ -36,7 +36,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.PlayerTeam;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;

View file

@ -1,26 +1,3 @@
//
// MIT License
//
// Copyright (c) 2021 Alexander Söderberg & Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
/** /**
* Arguments for the Fabric environment. * Arguments for the Fabric environment.
* *

View file

@ -25,9 +25,10 @@
"depends": { "depends": {
"fabricloader": ">=0.7.4", "fabricloader": ">=0.7.4",
"fabric-command-api-v1": "*", "fabric-command-api-v2": "*",
"fabric-networking-api-v1": "*",
"fabric-lifecycle-events-v1": "*", "fabric-lifecycle-events-v1": "*",
"fabric-permissions-api-v0": "*", "fabric-permissions-api-v0": "*",
"minecraft": ">=1.14" "minecraft": ">1.18.2"
} }
} }

View file

@ -27,29 +27,32 @@ import cloud.commandframework.Command;
import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.fabric.FabricClientCommandManager; import cloud.commandframework.fabric.FabricClientCommandManager;
import cloud.commandframework.fabric.argument.ItemInputArgument;
import cloud.commandframework.meta.CommandMeta; import cloud.commandframework.meta.CommandMeta;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.internal.Streams; import com.google.gson.internal.Streams;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.realmsclient.RealmsMainScreen;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.GenericDirtMessageScreen; import net.minecraft.client.gui.screens.GenericDirtMessageScreen;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.commands.synchronization.ArgumentTypes; import net.minecraft.commands.arguments.item.ItemInput;
import net.minecraft.commands.synchronization.ArgumentUtils;
import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.realms.RealmsBridge;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
public final class FabricClientExample implements ClientModInitializer { public final class FabricClientExample implements ClientModInitializer {
@ -68,8 +71,8 @@ public final class FabricClientExample implements ClientModInitializer {
"cloud-dump-" + Instant.now().toString().replace(':', '-') + ".json" "cloud-dump-" + Instant.now().toString().replace(':', '-') + ".json"
); );
ctx.getSender().sendFeedback( ctx.getSender().sendFeedback(
new TextComponent("Dumping command output to ") Component.literal("Dumping command output to ")
.append(new TextComponent(target.toString()) .append(Component.literal(target.toString())
.withStyle(s -> s.withClickEvent(new ClickEvent( .withStyle(s -> s.withClickEvent(new ClickEvent(
ClickEvent.Action.OPEN_FILE, ClickEvent.Action.OPEN_FILE,
target.toAbsolutePath().toString() target.toAbsolutePath().toString()
@ -80,11 +83,11 @@ public final class FabricClientExample implements ClientModInitializer {
final CommandDispatcher<SharedSuggestionProvider> dispatcher = Minecraft.getInstance() final CommandDispatcher<SharedSuggestionProvider> dispatcher = Minecraft.getInstance()
.getConnection() .getConnection()
.getCommands(); .getCommands();
final JsonObject object = ArgumentTypes.serializeNodeToJson(dispatcher, dispatcher.getRoot()); final JsonObject object = ArgumentUtils.serializeNodeToJson(dispatcher, dispatcher.getRoot());
json.setIndent(" "); json.setIndent(" ");
Streams.write(object, json); Streams.write(object, json);
} catch (final IOException ex) { } catch (final IOException ex) {
ctx.getSender().sendError(new TextComponent( ctx.getSender().sendError(Component.literal(
"Unable to write file, see console for details: " + ex.getMessage() "Unable to write file, see console for details: " + ex.getMessage()
)); ));
} }
@ -93,7 +96,7 @@ public final class FabricClientExample implements ClientModInitializer {
commandManager.command(base.literal("say") commandManager.command(base.literal("say")
.argument(StringArgument.greedy("message")) .argument(StringArgument.greedy("message"))
.handler(ctx -> ctx.getSender().sendFeedback( .handler(ctx -> ctx.getSender().sendFeedback(
new TextComponent("Cloud client commands says: " + ctx.get("message")) Component.literal("Cloud client commands says: " + ctx.get("message"))
))); )));
commandManager.command(base.literal("quit") commandManager.command(base.literal("quit")
@ -108,21 +111,34 @@ public final class FabricClientExample implements ClientModInitializer {
commandManager.command(base.literal("requires_cheats") commandManager.command(base.literal("requires_cheats")
.permission(FabricClientCommandManager.cheatsAllowed(false)) .permission(FabricClientCommandManager.cheatsAllowed(false))
.handler(ctx -> ctx.getSender().sendFeedback(new TextComponent("Cheats are enabled!")))); .handler(ctx -> ctx.getSender().sendFeedback(Component.literal("Cheats are enabled!"))));
// Test argument which requires CommandBuildContext/RegistryAccess
commandManager.command(base.literal("show_item")
.argument(ItemInputArgument.of("item"))
.handler(ctx -> {
try {
ctx.getSender().sendFeedback(
ctx.<ItemInput>get("item").createItemStack(1, false).getDisplayName()
);
} catch (final CommandSyntaxException ex) {
ctx.getSender().sendError(ComponentUtils.fromMessage(ex.getRawMessage()));
}
}));
} }
private static void disconnectClient(final @NonNull Minecraft client) { private static void disconnectClient(final @NonNull Minecraft client) {
boolean singlePlayer = client.hasSingleplayerServer(); final boolean singlePlayer = client.hasSingleplayerServer();
client.level.disconnect(); client.level.disconnect();
if (singlePlayer) { if (singlePlayer) {
client.clearLevel(new GenericDirtMessageScreen(new TranslatableComponent("menu.savingLevel"))); client.clearLevel(new GenericDirtMessageScreen(Component.translatable("menu.savingLevel")));
} else { } else {
client.clearLevel(); client.clearLevel();
} }
if (singlePlayer) { if (singlePlayer) {
client.setScreen(new TitleScreen()); client.setScreen(new TitleScreen());
} else if (client.isConnectedToRealms()) { } else if (client.isConnectedToRealms()) {
new RealmsBridge().switchToRealms(new TitleScreen()); client.setScreen(new RealmsMainScreen(new TitleScreen()));
} else { } else {
client.setScreen(new JoinMultiplayerScreen(new TitleScreen())); client.setScreen(new JoinMultiplayerScreen(new TitleScreen()));
} }

View file

@ -52,16 +52,14 @@ import net.fabricmc.loader.api.ModContainer;
import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.ModMetadata;
import net.fabricmc.loader.api.metadata.Person; import net.fabricmc.loader.api.metadata.Person;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.item.ItemInput; import net.minecraft.commands.arguments.item.ItemInput;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.TextColor; import net.minecraft.network.chat.TextColor;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -86,13 +84,13 @@ public final class FabricExample implements ModInitializer {
.argument(name) .argument(name)
.argument(hugs) .argument(hugs)
.handler(ctx -> { .handler(ctx -> {
ctx.getSender().sendSuccess(new TextComponent("Hello, ") ctx.getSender().sendSuccess(Component.literal("Hello, ")
.append(ctx.get(name)) .append(ctx.get(name))
.append(", hope you're doing well!") .append(", hope you're doing well!")
.withStyle(style -> style.withColor(TextColor.fromRgb(0xAA22BB))), false); .withStyle(style -> style.withColor(TextColor.fromRgb(0xAA22BB))), false);
ctx.getSender().sendSuccess(new TextComponent("Cloud would like to give you ") ctx.getSender().sendSuccess(Component.literal("Cloud would like to give you ")
.append(new TextComponent(String.valueOf(ctx.get(hugs))) .append(Component.literal(String.valueOf(ctx.get(hugs)))
.withStyle(style -> style.withColor(TextColor.fromRgb(0xFAB3DA)))) .withStyle(style -> style.withColor(TextColor.fromRgb(0xFAB3DA))))
.append(" hug(s) <3") .append(" hug(s) <3")
.withStyle(style -> style.withBold(true)), false); .withStyle(style -> style.withBold(true)), false);
@ -109,15 +107,13 @@ public final class FabricExample implements ModInitializer {
final MultiplePlayerSelector selector = ctx.get(playerSelector); final MultiplePlayerSelector selector = ctx.get(playerSelector);
final Collection<ServerPlayer> selected = selector.get(); final Collection<ServerPlayer> selected = selector.get();
selected.forEach(selectedPlayer -> selected.forEach(selectedPlayer ->
selectedPlayer.sendMessage( selectedPlayer.sendSystemMessage(
new TextComponent("Wave from ") Component.literal("Wave from ")
.withStyle(style -> style.withColor(ctx.get(textColor))) .withStyle(style -> style.withColor(ctx.get(textColor)))
.append(ctx.getSender().getDisplayName()), .append(ctx.getSender().getDisplayName())
ChatType.SYSTEM,
Util.NIL_UUID
)); ));
ctx.getSender().sendSuccess( ctx.getSender().sendSuccess(
new TextComponent(String.format("Waved at %d players (%s)", selected.size(), Component.literal(String.format("Waved at %d players (%s)", selected.size(),
selector.inputString() selector.inputString()
)), )),
false false
@ -150,14 +146,14 @@ public final class FabricExample implements ModInitializer {
.map(ModContainer::getMetadata) .map(ModContainer::getMetadata)
.sorted(Comparator.comparing(ModMetadata::getId)) .sorted(Comparator.comparing(ModMetadata::getId))
.collect(Collectors.toList()); .collect(Collectors.toList());
final TextComponent text = new TextComponent(""); final MutableComponent text = Component.literal("");
text.append(new TextComponent("Loaded Mods") text.append(Component.literal("Loaded Mods")
.withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.BOLD))); .withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.BOLD)));
text.append(new TextComponent(String.format(" (%s)\n", modList.size())) text.append(Component.literal(String.format(" (%s)\n", modList.size()))
.withStyle(style -> style.withColor(ChatFormatting.GRAY).applyFormat(ChatFormatting.ITALIC))); .withStyle(style -> style.withColor(ChatFormatting.GRAY).applyFormat(ChatFormatting.ITALIC)));
for (final ModMetadata mod : modList) { for (final ModMetadata mod : modList) {
text.append( text.append(
new TextComponent("") Component.literal("")
.withStyle(style -> style.withColor(ChatFormatting.WHITE) .withStyle(style -> style.withColor(ChatFormatting.WHITE)
.withClickEvent(new ClickEvent( .withClickEvent(new ClickEvent(
ClickEvent.Action.SUGGEST_COMMAND, ClickEvent.Action.SUGGEST_COMMAND,
@ -165,17 +161,17 @@ public final class FabricExample implements ModInitializer {
)) ))
.withHoverEvent(new HoverEvent( .withHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT, HoverEvent.Action.SHOW_TEXT,
new TextComponent("Click for more info") Component.literal("Click for more info")
))) )))
.append(new TextComponent(mod.getName()).withStyle(style -> style.withColor(ChatFormatting.GREEN))) .append(Component.literal(mod.getName()).withStyle(style -> style.withColor(ChatFormatting.GREEN)))
.append(new TextComponent(String.format(" (%s) ", mod.getId())) .append(Component.literal(String.format(" (%s) ", mod.getId()))
.withStyle(style -> style .withStyle(style -> style
.withColor(ChatFormatting.GRAY) .withColor(ChatFormatting.GRAY)
.applyFormat(ChatFormatting.ITALIC))) .applyFormat(ChatFormatting.ITALIC)))
.append(new TextComponent(String.format("v%s", mod.getVersion()))) .append(Component.literal(String.format("v%s", mod.getVersion())))
); );
if (modList.indexOf(mod) != modList.size() - 1) { if (modList.indexOf(mod) != modList.size() - 1) {
text.append(new TextComponent(", ").withStyle(style -> style.withColor(ChatFormatting.GRAY))); text.append(Component.literal(", ").withStyle(style -> style.withColor(ChatFormatting.GRAY)));
} }
} }
ctx.getSender().sendSuccess(text, false); ctx.getSender().sendSuccess(text, false);
@ -204,23 +200,23 @@ public final class FabricExample implements ModInitializer {
manager.command(mods.argument(modMetadata) manager.command(mods.argument(modMetadata)
.handler(ctx -> { .handler(ctx -> {
final ModMetadata meta = ctx.get(modMetadata); final ModMetadata meta = ctx.get(modMetadata);
final MutableComponent text = new TextComponent("") final MutableComponent text = Component.literal("")
.append(new TextComponent(meta.getName()) .append(Component.literal(meta.getName())
.withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.BOLD))) .withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.BOLD)))
.append(new TextComponent("\n modid: " + meta.getId())) .append(Component.literal("\n modid: " + meta.getId()))
.append(new TextComponent("\n version: " + meta.getVersion())) .append(Component.literal("\n version: " + meta.getVersion()))
.append(new TextComponent("\n type: " + meta.getType())); .append(Component.literal("\n type: " + meta.getType()));
if (!meta.getDescription().isEmpty()) { if (!meta.getDescription().isEmpty()) {
text.append(new TextComponent("\n description: " + meta.getDescription())); text.append(Component.literal("\n description: " + meta.getDescription()));
} }
if (!meta.getAuthors().isEmpty()) { if (!meta.getAuthors().isEmpty()) {
text.append(new TextComponent("\n authors: " + meta.getAuthors().stream() text.append(Component.literal("\n authors: " + meta.getAuthors().stream()
.map(Person::getName) .map(Person::getName)
.collect(Collectors.joining(", ")))); .collect(Collectors.joining(", "))));
} }
if (!meta.getLicense().isEmpty()) { if (!meta.getLicense().isEmpty()) {
text.append(new TextComponent("\n license: " + String.join(", ", meta.getLicense()))); text.append(Component.literal("\n license: " + String.join(", ", meta.getLicense())));
} }
ctx.getSender().sendSuccess( ctx.getSender().sendSuccess(
text, text,

View file

@ -28,8 +28,8 @@
"depends": { "depends": {
"fabricloader": ">=0.7.4", "fabricloader": ">=0.7.4",
"fabric-command-api-v1": "*", "fabric-command-api-v2": "*",
"minecraft": ">=1.14", "minecraft": ">1.18.2",
"cloud": "*" "cloud": "*"
} }
} }

View file

@ -44,9 +44,9 @@ versions:
velocityApi: 3.1.0 velocityApi: 3.1.0
spongeApi7: 7.3.0 spongeApi7: 7.3.0
jetbrainsAnnotations: 23.0.0 jetbrainsAnnotations: 23.0.0
fabricMinecraft: 1.16.5 fabricMinecraft: 1.19
fabricLoader: 0.13.3 fabricLoader: 0.14.6
fabricApi: 0.31.0+1.16 fabricApi: 0.55.2+1.19
fabricPermissionsApi: 0.1-SNAPSHOT fabricPermissionsApi: 0.1-SNAPSHOT
# testing # testing

View file

@ -3,7 +3,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement { pluginManagement {
repositories { repositories {
gradlePluginPortal() gradlePluginPortal()
maven("https://maven.fabricmc.net") maven("https://maven.fabricmc.net/")
maven("https://maven.quiltmc.org/repository/release/") maven("https://maven.quiltmc.org/repository/release/")
maven("https://repo.jpenilla.xyz/snapshots/") maven("https://repo.jpenilla.xyz/snapshots/")
} }