diff --git a/CHANGELOG.md b/CHANGELOG.md index 036fe420..2b61b1d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) - 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] ### Fixed diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 7098eed1..40ee528f 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -8,6 +8,8 @@ repositories { } 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.indraPublishingSonatype) implementation(libs.gradleTestLogger) diff --git a/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/argument/WrappedBrigadierParser.java b/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/argument/WrappedBrigadierParser.java index 4107b4ce..71900510 100644 --- a/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/argument/WrappedBrigadierParser.java +++ b/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/argument/WrappedBrigadierParser.java @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.List; import java.util.Queue; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.NonNull; import static java.util.Objects.requireNonNull; @@ -53,7 +54,7 @@ public final class WrappedBrigadierParser implements ArgumentParser public static final String COMMAND_CONTEXT_BRIGADIER_NATIVE_SENDER = "_cloud_brigadier_native_sender"; - private final ArgumentType nativeType; + private final Supplier> nativeType; private final int expectedArgumentCount; /** @@ -63,6 +64,16 @@ public final class WrappedBrigadierParser implements ArgumentParser * @since 1.5.0 */ public WrappedBrigadierParser(final ArgumentType 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> nativeType) { this(nativeType, DEFAULT_ARGUMENT_COUNT); } @@ -74,10 +85,25 @@ public final class WrappedBrigadierParser implements ArgumentParser * @since 1.5.0 */ public WrappedBrigadierParser( - final ArgumentType nativeType, + final ArgumentType 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> nativeType, final int expectedArgumentCount ) { - this.nativeType = requireNonNull(nativeType, "brigadierType"); + requireNonNull(nativeType, "brigadierType"); + this.nativeType = nativeType; this.expectedArgumentCount = expectedArgumentCount; } @@ -88,7 +114,7 @@ public final class WrappedBrigadierParser implements ArgumentParser * @since 1.5.0 */ public ArgumentType getNativeArgument() { - return this.nativeType; + return this.nativeType.get(); } @Override @@ -109,7 +135,7 @@ public final class WrappedBrigadierParser implements ArgumentParser // Then try to parse try { - return ArgumentParseResult.success(this.nativeType.parse(reader)); + return ArgumentParseResult.success(this.nativeType.get().parse(reader)); } catch (final CommandSyntaxException ex) { return ArgumentParseResult.failure(ex); } finally { @@ -142,7 +168,7 @@ public final class WrappedBrigadierParser implements ArgumentParser false ); - final CompletableFuture result = this.nativeType.listSuggestions( + final CompletableFuture result = this.nativeType.get().listSuggestions( reverseMappedContext, new SuggestionsBuilder(input, 0) ); diff --git a/cloud-minecraft/cloud-fabric/build.gradle.kts b/cloud-minecraft/cloud-fabric/build.gradle.kts index 429a9877..ba7e0ec6 100644 --- a/cloud-minecraft/cloud-fabric/build.gradle.kts +++ b/cloud-minecraft/cloud-fabric/build.gradle.kts @@ -2,7 +2,7 @@ import net.fabricmc.loom.task.AbstractRunTask import net.ltgt.gradle.errorprone.errorprone plugins { - id("quiet-fabric-loom") version "0.11-SNAPSHOT" + id("quiet-fabric-loom") version "0.12-SNAPSHOT" id("cloud.base-conventions") } @@ -44,7 +44,8 @@ dependencies { minecraft(libs.fabricMinecraft) mappings(loom.officialMojangMappings()) 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(libs.fabricPermissionsApi) diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricClientCommandManager.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricClientCommandManager.java index 16571c47..04bfe0ee 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricClientCommandManager.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricClientCommandManager.java @@ -28,8 +28,8 @@ import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator; import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.permission.PredicatePermission; import java.util.function.Function; -import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; -import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientSuggestionProvider; import org.checkerframework.checker.nullness.qual.NonNull; @@ -165,7 +165,8 @@ public final class FabricClientCommandManager extends FabricCommandManager extends FabricCommandManager>() { - }, SwizzleArgument.swizzle()); + this.registerConstantNativeParserSupplier(new TypeToken>() {}, SwizzleArgument.swizzle()); this.registerConstantNativeParserSupplier(ResourceLocation.class, ResourceLocationArgument.id()); - this.registerConstantNativeParserSupplier( - EntityAnchorArgument.Anchor.class, - EntityAnchorArgument.anchor() - ); + this.registerConstantNativeParserSupplier(EntityAnchorArgument.Anchor.class, EntityAnchorArgument.anchor()); this.registerConstantNativeParserSupplier(MinMaxBounds.Ints.class, RangeArgument.intRange()); - this.registerConstantNativeParserSupplier(MinMaxBounds.Floats.class, RangeArgument.floatRange()); - this.registerConstantNativeParserSupplier(ItemInput.class, ItemArgument.item()); + this.registerConstantNativeParserSupplier(MinMaxBounds.Doubles.class, RangeArgument.floatRange()); + this.registerContextualNativeParserSupplier(ItemInput.class, ItemArgument::item); + this.registerContextualNativeParserSupplier(BlockPredicateArgument.Result.class, BlockPredicateArgument::blockPredicate); /* Wrapped/Constant Brigadier types, mapped value type */ - this.registerConstantNativeParserSupplier( - BlockPredicateArgument.Result.class, - BlockPredicateArgument.blockPredicate() - ); this.registerConstantNativeParserSupplier(MessageArgument.Message.class, MessageArgument.message()); - this.getParserRegistry().registerParserSupplier( - TypeToken.get(MinecraftTime.class), - params -> FabricArgumentParsers.time() - ); + this.getParserRegistry().registerParserSupplier(TypeToken.get(MinecraftTime.class), params -> FabricArgumentParsers.time()); } @SuppressWarnings({"unchecked", "rawtypes"}) private void registerRegistryEntryMappings() { this.brigadierManager.registerMapping( - new TypeToken>() { - }, - builder -> builder.to(argument -> { - /* several registries have specialized argument types, so let's use those where possible */ - final ResourceKey> 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> registry = argument.registryKey(); - if (registry.equals(Registry.SOUND_EVENT_REGISTRY)) { - return (SuggestionProvider) SuggestionProviders.AVAILABLE_SOUNDS; - } else if (registry.equals(Registry.BIOME_REGISTRY)) { - return (SuggestionProvider) 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 */ - }) + new TypeToken>() {}, + builder -> { + builder.to(argument -> ResourceOrTagLocationArgument.resourceOrTag((ResourceKey) argument.registryKey())); + } ); /* Find all fields of RegistryKey> and register those */ @@ -292,6 +250,21 @@ public abstract class FabricCommandManager value type + * @since 1.7.0 + */ + final void registerContextualNativeParserSupplier( + final @NonNull Class type, + final @NonNull Function> 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. * diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java index c1cb0b9b..ef048b88 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricCommandRegistrationHandler.java @@ -25,6 +25,7 @@ package cloud.commandframework.fabric; import cloud.commandframework.Command; import cloud.commandframework.arguments.StaticArgument; +import cloud.commandframework.fabric.argument.FabricArgumentParsers; import cloud.commandframework.internal.CommandRegistrationHandler; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -33,11 +34,16 @@ import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.RootCommandNode; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource; -import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +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.Commands.CommandSelection; +import net.minecraft.commands.Commands; import net.minecraft.commands.SharedSuggestionProvider; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.NonNull; @@ -51,6 +57,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; * @param native sender type */ abstract class FabricCommandRegistrationHandler implements CommandRegistrationHandler { + private @MonotonicNonNull FabricCommandManager commandManager; void initialize(final FabricCommandManager manager) { @@ -71,7 +78,7 @@ abstract class FabricCommandRegistrationHandler brig sender type + * @param brig sender type * @return the built node */ private static LiteralCommandNode buildRedirect( @@ -81,7 +88,7 @@ abstract class FabricCommandRegistrationHandler builder = LiteralArgumentBuilder + final LiteralArgumentBuilder builder = LiteralArgumentBuilder .literal(alias) .requires(destination.getRequirement()) .forward( @@ -97,19 +104,65 @@ abstract class FabricCommandRegistrationHandler extends FabricCommandRegistrationHandler { + + private final Set> registeredCommands = ConcurrentHashMap.newKeySet(); + private volatile boolean registerEventFired = false; + @Override void initialize(final FabricCommandManager manager) { super.initialize(manager); + ClientCommandRegistrationCallback.EVENT.register(this::registerCommands); + ClientPlayConnectionEvents.DISCONNECT.register(($, $$) -> this.registerEventFired = false); } @Override @SuppressWarnings("unchecked") - public boolean registerCommand(final @NonNull Command cmd) { - final Command command = (Command) cmd; - final RootCommandNode rootNode = ClientCommandManager.DISPATCHER.getRoot(); + public boolean registerCommand(final @NonNull Command command) { + this.registeredCommands.add((Command) command); + 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 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) command) + ); + } + return true; + } + + public void registerCommands( + final CommandDispatcher dispatcher, + final CommandBuildContext commandBuildContext + ) { + this.registerEventFired = true; + FabricArgumentParsers.ContextualArgumentTypeProvider.withBuildContext( + this.commandManager(), + commandBuildContext, + true, + () -> { + for (final Command command : this.registeredCommands) { + this.registerClientCommand(dispatcher, command); + } + } + ); + } + + @SuppressWarnings("unchecked") + private void registerClientCommand( + final CommandDispatcher dispatcher, + final Command command + ) { + final RootCommandNode rootNode = dispatcher.getRoot(); final StaticArgument first = ((StaticArgument) command.getArguments().get(0)); - final CommandNode baseNode = this - .commandManager() + final CommandNode baseNode = this.commandManager() .brigadierManager() .createLiteralCommandNode( first.getName(), @@ -131,11 +184,11 @@ abstract class FabricCommandRegistrationHandler extends FabricCommandRegistrationHandler { + private final Set> registeredCommands = ConcurrentHashMap.newKeySet(); @Override @@ -150,21 +203,32 @@ abstract class FabricCommandRegistrationHandler) command); } - private void registerAllCommands(final CommandDispatcher dispatcher, final boolean isDedicated) { + private void registerAllCommands( + final CommandDispatcher dispatcher, + final CommandBuildContext access, + final Commands.CommandSelection side + ) { this.commandManager().registrationCalled(); - for (final Command command : this.registeredCommands) { - /* Only register commands in the declared environment */ - final CommandSelection env = command.getCommandMeta().getOrDefault( - FabricServerCommandManager.META_REGISTRATION_ENVIRONMENT, - CommandSelection.ALL - ); + FabricArgumentParsers.ContextualArgumentTypeProvider.withBuildContext( + this.commandManager(), + access, + true, + () -> { + for (final Command 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) - || (env == CommandSelection.DEDICATED && !isDedicated)) { - continue; + if ((env == Commands.CommandSelection.INTEGRATED && !side.includeIntegrated) + || (env == Commands.CommandSelection.DEDICATED && !side.includeDedicated)) { + continue; + } + this.registerCommand(dispatcher.getRoot(), command); + } } - this.registerCommand(dispatcher.getRoot(), command); - } + ); } private void registerCommand(final RootCommandNode dispatcher, final Command command) { @@ -178,7 +242,8 @@ abstract class FabricCommandRegistrationHandler(this.commandManager(), CommandSourceStack::getTextName, CommandSourceStack::sendFailure)); + new FabricExecutor<>(this.commandManager(), CommandSourceStack::getTextName, CommandSourceStack::sendFailure) + ); dispatcher.addChild(baseNode); @@ -186,5 +251,7 @@ abstract class FabricCommandRegistrationHandler implements Command { + private static final Logger LOGGER = LogUtils.getLogger(); - private static final Logger LOGGER = LogManager.getLogger(); - - private static final Component NEWLINE = new TextComponent("\n"); + private static final Component NEWLINE = Component.literal("\n"); private static final String MESSAGE_INTERNAL_ERROR = "An internal error occurred while attempting to perform this command."; private static final String MESSAGE_NO_PERMS = "I'm sorry, but you do not have permission to perform this command. " @@ -79,6 +77,7 @@ final class FabricExecutor implements Com final S source = ctx.getSource(); final String input = ctx.getInput().substring(ctx.getLastChild().getNodes().get(0).getRange().getStart()); final C sender = this.manager.commandSourceMapper().apply(source); + this.manager.executeCommand(sender, input).whenComplete((result, throwable) -> { if (throwable == null) { return; @@ -99,8 +98,8 @@ final class FabricExecutor implements Com (InvalidSyntaxException) throwable, (c, e) -> this.sendError.accept( source, - new TextComponent("Invalid Command Syntax. Correct command syntax is: ") - .append(new TextComponent(String.format("/%s", e.getCorrectSyntax())) + Component.literal("Invalid Command Syntax. Correct command syntax is: ") + .append(Component.literal(String.format("/%s", e.getCorrectSyntax())) .withStyle(style -> style.withColor(ChatFormatting.GRAY))) ) ); @@ -109,21 +108,21 @@ final class FabricExecutor implements Com sender, InvalidCommandSenderException.class, (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) { this.manager.handleException( sender, NoPermissionException.class, (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) { this.manager.handleException( sender, NoSuchCommandException.class, (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) { this.manager.handleException( @@ -132,14 +131,14 @@ final class FabricExecutor implements Com (ArgumentParseException) throwable, (c, e) -> { if (throwable.getCause() instanceof CommandSyntaxException) { - this.sendError.accept(source, new TextComponent("Invalid Command Argument: ") - .append(new TextComponent("") + this.sendError.accept(source, Component.literal("Invalid Command Argument: ") + .append(Component.literal("") .append(ComponentUtils .fromMessage(((CommandSyntaxException) throwable.getCause()).getRawMessage())) .withStyle(ChatFormatting.GRAY))); } else { - this.sendError.accept(source, new TextComponent("Invalid Command Argument: ") - .append(new TextComponent(throwable.getCause().getMessage()) + this.sendError.accept(source, Component.literal("Invalid Command Argument: ") + .append(Component.literal(throwable.getCause().getMessage()) .withStyle(ChatFormatting.GRAY))); } } @@ -151,7 +150,7 @@ final class FabricExecutor implements Com (CommandExecutionException) throwable, (c, e) -> { this.sendError.accept(source, this.decorateHoverStacktrace( - new TextComponent(MESSAGE_INTERNAL_ERROR), + Component.literal(MESSAGE_INTERNAL_ERROR), throwable.getCause(), sender )); @@ -164,7 +163,7 @@ final class FabricExecutor implements Com ); } else { this.sendError.accept(source, this.decorateHoverStacktrace( - new TextComponent(MESSAGE_INTERNAL_ERROR), + Component.literal(MESSAGE_INTERNAL_ERROR), throwable, sender )); @@ -183,9 +182,9 @@ final class FabricExecutor implements Com return input.withStyle(style -> style .withHoverEvent(new HoverEvent( HoverEvent.Action.SHOW_TEXT, - new TextComponent(stackTrace) + Component.literal(stackTrace) .append(NEWLINE) - .append(new TextComponent(" Click to copy") + .append(Component.literal(" Click to copy") .withStyle(s2 -> s2.withColor(ChatFormatting.GRAY).withItalic(true))) )) .withClickEvent(new ClickEvent( diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java index d2c874ba..305b1bef 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/FabricServerCommandManager.java @@ -40,11 +40,11 @@ import cloud.commandframework.meta.CommandMeta; import io.leangen.geantyref.TypeToken; import java.util.function.Function; 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.CommandSourceStack; 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.Vec3; import org.checkerframework.checker.nullness.qual.NonNull; @@ -121,7 +121,7 @@ public final class FabricServerCommandManager extends FabricCommandManager sender type + * @param argument value type + * @param factory factory that creates these arguments + * @return the parser + */ + public static @NonNull ArgumentParser contextual(final @NonNull Function> factory) { + return new WrappedBrigadierParser<>(new ContextualArgumentTypeProvider<>(factory)); + } + /** * A parser for in-game time, in ticks. * @@ -505,4 +524,94 @@ public final class FabricArgumentParsers { } + @ApiStatus.Internal + public static final class ContextualArgumentTypeProvider implements Supplier> { + private static final ThreadLocal CONTEXT = new ThreadLocal<>(); + private static final Map, Set>> INSTANCES = + new WeakHashMap<>(); + + private final Function> provider; + private volatile ArgumentType 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> instances() { + return INSTANCES.computeIfAbsent(this.commandManager, $ -> Collections.newSetFromMap(new WeakHashMap<>())); + } + } + + ContextualArgumentTypeProvider(final @NonNull Function> provider) { + this.provider = provider; + } + + @Override + public ArgumentType get() { + final ThreadLocalContext ctx = CONTEXT.get(); + + if (ctx != null) { + synchronized (INSTANCES) { + ctx.instances().add(this); + } + } + + ArgumentType 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; + } + + } + } diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FloatRangeArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FloatRangeArgument.java index 00df362b..cd285416 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FloatRangeArgument.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/FloatRangeArgument.java @@ -35,13 +35,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; 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. * * @param the sender type * @since 1.5.0 */ -public final class FloatRangeArgument extends CommandArgument { +public final class FloatRangeArgument extends CommandArgument { FloatRangeArgument( final boolean required, @@ -55,7 +55,7 @@ public final class FloatRangeArgument extends CommandArgument(RangeArgument.floatRange()), defaultValue, - MinMaxBounds.Floats.class, + MinMaxBounds.Doubles.class, suggestionsProvider, defaultDescription ); @@ -108,7 +108,7 @@ public final class FloatRangeArgument extends CommandArgument @NonNull FloatRangeArgument optional( final @NonNull String name, - final MinMaxBounds.@NonNull Floats defaultValue + final MinMaxBounds.@NonNull Doubles defaultValue ) { return FloatRangeArgument.builder(name).asOptionalWithDefault(defaultValue).build(); } @@ -120,10 +120,10 @@ public final class FloatRangeArgument extends CommandArgument sender type * @since 1.5.0 */ - public static final class Builder extends TypedBuilder> { + public static final class Builder extends TypedBuilder> { Builder(final @NonNull String name) { - super(MinMaxBounds.Floats.class, name); + super(MinMaxBounds.Doubles.class, name); } /** @@ -151,7 +151,7 @@ public final class FloatRangeArgument extends CommandArgument asOptionalWithDefault(final MinMaxBounds.@NonNull Floats defaultValue) { + public @NonNull Builder asOptionalWithDefault(final MinMaxBounds.@NonNull Doubles defaultValue) { final StringBuilder value = new StringBuilder(6); if (defaultValue.getMin() != null) { value.append(defaultValue.getMin()); diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/ItemInputArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/ItemInputArgument.java index e0f1eb0b..a6651c2f 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/ItemInputArgument.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/ItemInputArgument.java @@ -25,7 +25,6 @@ package cloud.commandframework.fabric.argument; import cloud.commandframework.ArgumentDescription; import cloud.commandframework.arguments.CommandArgument; -import cloud.commandframework.brigadier.argument.WrappedBrigadierParser; import cloud.commandframework.context.CommandContext; import java.util.List; import java.util.function.BiFunction; @@ -54,7 +53,7 @@ public final class ItemInputArgument extends CommandArgument { super( required, name, - new WrappedBrigadierParser<>(ItemArgument.item()), + FabricArgumentParsers.contextual(ItemArgument::item), defaultValue, ItemInput.class, suggestionsProvider, diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SidedArgumentParser.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SidedArgumentParser.java index 2ec43dd1..f193d6e5 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SidedArgumentParser.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/SidedArgumentParser.java @@ -28,7 +28,7 @@ import cloud.commandframework.arguments.parser.ArgumentParser; import cloud.commandframework.context.CommandContext; import cloud.commandframework.fabric.FabricCommandContextKeys; 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.SharedSuggestionProvider; import org.checkerframework.checker.nullness.qual.NonNull; diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/TeamArgument.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/TeamArgument.java index d20f4343..cecb830f 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/TeamArgument.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/TeamArgument.java @@ -36,7 +36,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Queue; 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.world.scores.PlayerTeam; import org.checkerframework.checker.nullness.qual.NonNull; diff --git a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/package-info.java b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/package-info.java index 0fc3f1cc..f7b29d2a 100644 --- a/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/package-info.java +++ b/cloud-minecraft/cloud-fabric/src/main/java/cloud/commandframework/fabric/argument/package-info.java @@ -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. * diff --git a/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json b/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json index 59ba843c..01845fb5 100644 --- a/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json +++ b/cloud-minecraft/cloud-fabric/src/main/resources/fabric.mod.json @@ -25,9 +25,10 @@ "depends": { "fabricloader": ">=0.7.4", - "fabric-command-api-v1": "*", + "fabric-command-api-v2": "*", + "fabric-networking-api-v1": "*", "fabric-lifecycle-events-v1": "*", "fabric-permissions-api-v0": "*", - "minecraft": ">=1.14" + "minecraft": ">1.18.2" } } diff --git a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java index cbbe3cdd..6848c335 100644 --- a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java +++ b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricClientExample.java @@ -27,29 +27,32 @@ import cloud.commandframework.Command; import cloud.commandframework.arguments.standard.StringArgument; import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.fabric.FabricClientCommandManager; +import cloud.commandframework.fabric.argument.ItemInputArgument; import cloud.commandframework.meta.CommandMeta; import com.google.gson.JsonObject; import com.google.gson.internal.Streams; import com.google.gson.stream.JsonWriter; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.realmsclient.RealmsMainScreen; import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; 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.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.GenericDirtMessageScreen; import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; 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.TextComponent; -import net.minecraft.network.chat.TranslatableComponent; -import net.minecraft.realms.RealmsBridge; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentUtils; import org.checkerframework.checker.nullness.qual.NonNull; public final class FabricClientExample implements ClientModInitializer { @@ -68,8 +71,8 @@ public final class FabricClientExample implements ClientModInitializer { "cloud-dump-" + Instant.now().toString().replace(':', '-') + ".json" ); ctx.getSender().sendFeedback( - new TextComponent("Dumping command output to ") - .append(new TextComponent(target.toString()) + Component.literal("Dumping command output to ") + .append(Component.literal(target.toString()) .withStyle(s -> s.withClickEvent(new ClickEvent( ClickEvent.Action.OPEN_FILE, target.toAbsolutePath().toString() @@ -80,11 +83,11 @@ public final class FabricClientExample implements ClientModInitializer { final CommandDispatcher dispatcher = Minecraft.getInstance() .getConnection() .getCommands(); - final JsonObject object = ArgumentTypes.serializeNodeToJson(dispatcher, dispatcher.getRoot()); + final JsonObject object = ArgumentUtils.serializeNodeToJson(dispatcher, dispatcher.getRoot()); json.setIndent(" "); Streams.write(object, json); } catch (final IOException ex) { - ctx.getSender().sendError(new TextComponent( + ctx.getSender().sendError(Component.literal( "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") .argument(StringArgument.greedy("message")) .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") @@ -108,21 +111,34 @@ public final class FabricClientExample implements ClientModInitializer { commandManager.command(base.literal("requires_cheats") .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.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) { - boolean singlePlayer = client.hasSingleplayerServer(); + final boolean singlePlayer = client.hasSingleplayerServer(); client.level.disconnect(); if (singlePlayer) { - client.clearLevel(new GenericDirtMessageScreen(new TranslatableComponent("menu.savingLevel"))); + client.clearLevel(new GenericDirtMessageScreen(Component.translatable("menu.savingLevel"))); } else { client.clearLevel(); } if (singlePlayer) { client.setScreen(new TitleScreen()); } else if (client.isConnectedToRealms()) { - new RealmsBridge().switchToRealms(new TitleScreen()); + client.setScreen(new RealmsMainScreen(new TitleScreen())); } else { client.setScreen(new JoinMultiplayerScreen(new TitleScreen())); } diff --git a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java index fc876a1c..1ed476cc 100644 --- a/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java +++ b/cloud-minecraft/cloud-fabric/src/testmod/java/cloud/commandframework/fabric/testmod/FabricExample.java @@ -52,16 +52,14 @@ import net.fabricmc.loader.api.ModContainer; import net.fabricmc.loader.api.metadata.ModMetadata; import net.fabricmc.loader.api.metadata.Person; import net.minecraft.ChatFormatting; -import net.minecraft.Util; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.item.ItemInput; -import net.minecraft.network.chat.ChatType; import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentUtils; import net.minecraft.network.chat.HoverEvent; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextColor; -import net.minecraft.network.chat.TextComponent; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.phys.Vec3; @@ -86,13 +84,13 @@ public final class FabricExample implements ModInitializer { .argument(name) .argument(hugs) .handler(ctx -> { - ctx.getSender().sendSuccess(new TextComponent("Hello, ") + ctx.getSender().sendSuccess(Component.literal("Hello, ") .append(ctx.get(name)) .append(", hope you're doing well!") .withStyle(style -> style.withColor(TextColor.fromRgb(0xAA22BB))), false); - ctx.getSender().sendSuccess(new TextComponent("Cloud would like to give you ") - .append(new TextComponent(String.valueOf(ctx.get(hugs))) + ctx.getSender().sendSuccess(Component.literal("Cloud would like to give you ") + .append(Component.literal(String.valueOf(ctx.get(hugs))) .withStyle(style -> style.withColor(TextColor.fromRgb(0xFAB3DA)))) .append(" hug(s) <3") .withStyle(style -> style.withBold(true)), false); @@ -109,15 +107,13 @@ public final class FabricExample implements ModInitializer { final MultiplePlayerSelector selector = ctx.get(playerSelector); final Collection selected = selector.get(); selected.forEach(selectedPlayer -> - selectedPlayer.sendMessage( - new TextComponent("Wave from ") - .withStyle(style -> style.withColor(ctx.get(textColor))) - .append(ctx.getSender().getDisplayName()), - ChatType.SYSTEM, - Util.NIL_UUID + selectedPlayer.sendSystemMessage( + Component.literal("Wave from ") + .withStyle(style -> style.withColor(ctx.get(textColor))) + .append(ctx.getSender().getDisplayName()) )); 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() )), false @@ -150,14 +146,14 @@ public final class FabricExample implements ModInitializer { .map(ModContainer::getMetadata) .sorted(Comparator.comparing(ModMetadata::getId)) .collect(Collectors.toList()); - final TextComponent text = new TextComponent(""); - text.append(new TextComponent("Loaded Mods") + final MutableComponent text = Component.literal(""); + text.append(Component.literal("Loaded Mods") .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))); for (final ModMetadata mod : modList) { text.append( - new TextComponent("") + Component.literal("") .withStyle(style -> style.withColor(ChatFormatting.WHITE) .withClickEvent(new ClickEvent( ClickEvent.Action.SUGGEST_COMMAND, @@ -165,17 +161,17 @@ public final class FabricExample implements ModInitializer { )) .withHoverEvent(new HoverEvent( 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(new TextComponent(String.format(" (%s) ", mod.getId())) + .append(Component.literal(mod.getName()).withStyle(style -> style.withColor(ChatFormatting.GREEN))) + .append(Component.literal(String.format(" (%s) ", mod.getId())) .withStyle(style -> style .withColor(ChatFormatting.GRAY) .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) { - 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); @@ -204,23 +200,23 @@ public final class FabricExample implements ModInitializer { manager.command(mods.argument(modMetadata) .handler(ctx -> { final ModMetadata meta = ctx.get(modMetadata); - final MutableComponent text = new TextComponent("") - .append(new TextComponent(meta.getName()) + final MutableComponent text = Component.literal("") + .append(Component.literal(meta.getName()) .withStyle(style -> style.withColor(ChatFormatting.BLUE).applyFormat(ChatFormatting.BOLD))) - .append(new TextComponent("\n modid: " + meta.getId())) - .append(new TextComponent("\n version: " + meta.getVersion())) - .append(new TextComponent("\n type: " + meta.getType())); + .append(Component.literal("\n modid: " + meta.getId())) + .append(Component.literal("\n version: " + meta.getVersion())) + .append(Component.literal("\n type: " + meta.getType())); if (!meta.getDescription().isEmpty()) { - text.append(new TextComponent("\n description: " + meta.getDescription())); + text.append(Component.literal("\n description: " + meta.getDescription())); } 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) .collect(Collectors.joining(", ")))); } 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( text, diff --git a/cloud-minecraft/cloud-fabric/src/testmod/resources/fabric.mod.json b/cloud-minecraft/cloud-fabric/src/testmod/resources/fabric.mod.json index fc0ab379..2c0a6f88 100644 --- a/cloud-minecraft/cloud-fabric/src/testmod/resources/fabric.mod.json +++ b/cloud-minecraft/cloud-fabric/src/testmod/resources/fabric.mod.json @@ -28,8 +28,8 @@ "depends": { "fabricloader": ">=0.7.4", - "fabric-command-api-v1": "*", - "minecraft": ">=1.14", + "fabric-command-api-v2": "*", + "minecraft": ">1.18.2", "cloud": "*" } } diff --git a/gradle/libs.versions.yml b/gradle/libs.versions.yml index 3d14d44c..636c23ca 100644 --- a/gradle/libs.versions.yml +++ b/gradle/libs.versions.yml @@ -44,9 +44,9 @@ versions: velocityApi: 3.1.0 spongeApi7: 7.3.0 jetbrainsAnnotations: 23.0.0 - fabricMinecraft: 1.16.5 - fabricLoader: 0.13.3 - fabricApi: 0.31.0+1.16 + fabricMinecraft: 1.19 + fabricLoader: 0.14.6 + fabricApi: 0.55.2+1.19 fabricPermissionsApi: 0.1-SNAPSHOT # testing diff --git a/settings.gradle.kts b/settings.gradle.kts index 9772e34d..db10437a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,7 +3,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") pluginManagement { repositories { gradlePluginPortal() - maven("https://maven.fabricmc.net") + maven("https://maven.fabricmc.net/") maven("https://maven.quiltmc.org/repository/release/") maven("https://repo.jpenilla.xyz/snapshots/") }