From ee5591684dc6bcdea6c44a62806d537bbb7e0cee Mon Sep 17 00:00:00 2001 From: nea Date: Mon, 4 Sep 2023 17:44:57 +0200 Subject: Add support for case insensitive commands (/fIrM) --- .../moe/nea/firmament/mixins/MixinCommandNode.java | 29 ++++++++ .../commands/CaseInsensitiveLiteralCommandNode.kt | 79 ++++++++++++++++++++++ src/main/kotlin/moe/nea/firmament/commands/dsl.kt | 9 ++- 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 src/main/java/moe/nea/firmament/mixins/MixinCommandNode.java create mode 100644 src/main/kotlin/moe/nea/firmament/commands/CaseInsensitiveLiteralCommandNode.kt (limited to 'src') diff --git a/src/main/java/moe/nea/firmament/mixins/MixinCommandNode.java b/src/main/java/moe/nea/firmament/mixins/MixinCommandNode.java new file mode 100644 index 0000000..aa8e584 --- /dev/null +++ b/src/main/java/moe/nea/firmament/mixins/MixinCommandNode.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.mixins; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.brigadier.tree.CommandNode; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.Locale; +import java.util.Map; + +@Mixin(value = CommandNode.class, remap = false) +public class MixinCommandNode { + @WrapOperation(method = "getRelevantNodes", at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;"), remap = false) + public Object modify(Map map, Object text, Operation op) { + var original = op.call(map, text); + if (original == null) { + return map.get(((String) text).toLowerCase(Locale.ROOT)); + } + return original; + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/commands/CaseInsensitiveLiteralCommandNode.kt b/src/main/kotlin/moe/nea/firmament/commands/CaseInsensitiveLiteralCommandNode.kt new file mode 100644 index 0000000..b819a5a --- /dev/null +++ b/src/main/kotlin/moe/nea/firmament/commands/CaseInsensitiveLiteralCommandNode.kt @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2023 Linnea Gräf + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package moe.nea.firmament.commands + +import com.mojang.brigadier.Command +import com.mojang.brigadier.RedirectModifier +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.builder.LiteralArgumentBuilder +import com.mojang.brigadier.context.CommandContextBuilder +import com.mojang.brigadier.context.StringRange +import com.mojang.brigadier.exceptions.CommandSyntaxException +import com.mojang.brigadier.tree.CommandNode +import com.mojang.brigadier.tree.LiteralCommandNode +import java.util.function.Predicate + +class CaseInsensitiveLiteralCommandNode( + literal: String, command: Command?, requirement: Predicate?, + redirect: CommandNode?, modifier: RedirectModifier?, forks: Boolean +) : LiteralCommandNode( + literal.lowercase(), command, requirement, redirect, modifier, forks +) { + class Builder(literal: String) : LiteralArgumentBuilder(literal) { + override fun build(): LiteralCommandNode { + val result = CaseInsensitiveLiteralCommandNode( + literal, + command, requirement, redirect, redirectModifier, isFork + ) + for (argument in arguments) { + result.addChild(argument) + } + return result + } + } + + override fun createBuilder(): LiteralArgumentBuilder { + return Builder(literal).also { + it.requires(requirement) + it.forward(redirect, redirectModifier, isFork) + if (command != null) + it.executes(command) + } + } + + override fun parse(reader: StringReader, contextBuilder: CommandContextBuilder) { + val start = reader.cursor + val end = parse0(reader) + if (end > -1) { + contextBuilder.withNode(this, StringRange.between(start, end)) + return + } + + throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.literalIncorrect().createWithContext(reader, literal) + } + + override fun toString(): String { + return "" + } + + private fun parse0(reader: StringReader): Int { + val start = reader.cursor + if (reader.canRead(literal.length)) { + val end = start + literal.length + if (reader.string.substring(start, end).equals(literal, true)) { + reader.cursor = end + if (!reader.canRead() || reader.peek() == ' ') { + return end + } else { + reader.cursor = start + } + } + } + return -1 + } + +} diff --git a/src/main/kotlin/moe/nea/firmament/commands/dsl.kt b/src/main/kotlin/moe/nea/firmament/commands/dsl.kt index d6eaf85..551f560 100644 --- a/src/main/kotlin/moe/nea/firmament/commands/dsl.kt +++ b/src/main/kotlin/moe/nea/firmament/commands/dsl.kt @@ -8,7 +8,6 @@ package moe.nea.firmament.commands import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.builder.ArgumentBuilder -import com.mojang.brigadier.builder.LiteralArgumentBuilder import com.mojang.brigadier.builder.RequiredArgumentBuilder import com.mojang.brigadier.context.CommandContext import com.mojang.brigadier.suggestion.SuggestionProvider @@ -32,9 +31,9 @@ operator fun > C.get(arg: TypeSafeArg): T { fun literal( name: String, - block: LiteralArgumentBuilder.() -> Unit -): LiteralArgumentBuilder = - LiteralArgumentBuilder.literal(name).also(block) + block: CaseInsensitiveLiteralCommandNode.Builder.() -> Unit +): CaseInsensitiveLiteralCommandNode.Builder = + CaseInsensitiveLiteralCommandNode.Builder(name).also(block) private fun normalizeGeneric(argument: Type): Class<*> { @@ -101,7 +100,7 @@ fun > T.suggestsList(provider fun > T.thenLiteral( name: String, - block: LiteralArgumentBuilder.() -> Unit + block: CaseInsensitiveLiteralCommandNode.Builder.() -> Unit ): T = then(literal(name, block)) -- cgit