Skip to content

Commit

Permalink
Updated Command Base
Browse files Browse the repository at this point in the history
Signed-off-by: Lyzev <[email protected]>
  • Loading branch information
Lyzev committed Nov 11, 2023
1 parent 8388f49 commit 4559bfa
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 22 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ dependencies {
implementation(libs.reflections)
implementation(libs.bundles.imgui)
implementation(libs.fuzzywuzzy)
implementation(libs.adventure)
}

loom {
Expand Down
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ reflections = "0.10.2"
imgui = "1.86.11"
# https://github.com/intuit/fuzzy-matcher
fuzzywuzzy = "1.4.0"
# https://github.com/KyoriPowered/adventure
adventure = "4.14.0"

[libraries]
minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" }
Expand All @@ -43,6 +45,7 @@ imgui_natives_windows = { module = "io.github.spair:imgui-java-natives-windows",
imgui_natives_linux = { module = "io.github.spair:imgui-java-natives-linux", version.ref = "imgui" }
imgui_natives_macos = { module = "io.github.spair:imgui-java-natives-macos", version.ref = "imgui" }
fuzzywuzzy = { module = "me.xdrop:fuzzywuzzy", version.ref = "fuzzywuzzy" }
adventure = { module = "net.kyori:adventure-api", version.ref = "adventure" }

[plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
Expand Down
33 changes: 19 additions & 14 deletions src/main/kotlin/dev/lyzev/schizoid/Schizoid.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ package dev.lyzev.schizoid
import dev.lyzev.api.events.EventShutdown
import dev.lyzev.api.events.EventStartup
import dev.lyzev.api.setting.SettingInitializer
import dev.lyzev.schizoid.Schizoid.CI
import dev.lyzev.schizoid.Schizoid.METADATA
import dev.lyzev.schizoid.Schizoid.MOD_AUTHORS
import dev.lyzev.schizoid.Schizoid.MOD_ID
import dev.lyzev.schizoid.Schizoid.MOD_NAME
import dev.lyzev.schizoid.Schizoid.MOD_VERSION
import dev.lyzev.schizoid.Schizoid.logger
import dev.lyzev.schizoid.Schizoid.mc
import dev.lyzev.schizoid.Schizoid.root
import dev.lyzev.schizoid.feature.FeatureManager
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.loader.api.FabricLoader
Expand All @@ -18,37 +27,33 @@ import java.io.File

/**
* Elevate your Minecraft gameplay with this free and feature-rich client built with Fabric API and utilizing mixin-based injection techniques.
*
* @property MOD_ID The unique identifier of the mod.
* @property METADATA The metadata of the mod.
* @property MOD_NAME The name of the mod.
* @property MOD_VERSION The version of the mod.
* @property MOD_AUTHORS The list of authors contributing to the mod.
* @property CI Whether the mod is running in a continuous integration environment.
* @property root The root directory of the Schizoid mod, used for storing mod-specific data.
* @property logger The logger for the Schizoid mod.
* @property mc The Minecraft client instance.
*/
object Schizoid : ClientModInitializer {

// The unique identifier of the mod.
const val MOD_ID = "schizoid"

// The metadata of the mod.
val METADATA = FabricLoader.getInstance().getModContainer(MOD_ID).get().metadata

Check notice on line 44 in src/main/kotlin/dev/lyzev/schizoid/Schizoid.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Function or property has platform type

Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.

// The name of the mod.
val MOD_NAME = METADATA.name

Check notice on line 45 in src/main/kotlin/dev/lyzev/schizoid/Schizoid.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Function or property has platform type

Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.

// The version of the mod.
val MOD_VERSION = METADATA.version.friendlyString

Check notice on line 46 in src/main/kotlin/dev/lyzev/schizoid/Schizoid.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Function or property has platform type

Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.

// The list of authors contributing to the mod.
@Suppress("SpellCheckingInspection")
val MOD_AUTHORS = METADATA.authors

Check notice on line 48 in src/main/kotlin/dev/lyzev/schizoid/Schizoid.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Function or property has platform type

Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.

// Whether the mod is running in a continuous integration environment.
val CI = System.getProperty("CI").toBooleanStrict()

// The root directory of the Schizoid mod, used for storing mod-specific data.
val root = File(
MinecraftClient.getInstance().runDirectory, MOD_ID
).also { if (!it.exists()) it.mkdir() }

// The logger for the Schizoid mod.
val logger: Logger = LogManager.getLogger(MOD_ID)

// The Minecraft client instance.
val mc = MinecraftClient.getInstance()

Check notice on line 57 in src/main/kotlin/dev/lyzev/schizoid/Schizoid.kt

View workflow job for this annotation

GitHub Actions / Qodana for JVM

Function or property has platform type

Declaration has type inferred from a platform call, which can lead to unchecked nullability issues. Specify type explicitly as nullable or non-nullable.

/**
Expand Down
19 changes: 15 additions & 4 deletions src/main/kotlin/dev/lyzev/schizoid/feature/FeatureManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ import org.reflections.Reflections
import java.lang.reflect.Modifier
import kotlin.jvm.internal.Reflection


/**
* Singleton object responsible for initializing and managing features.
*
* @property PREFIX The prefix used for commands.
* @property features The list of features.
*/
object FeatureManager : EventListener {

Expand Down Expand Up @@ -79,10 +83,17 @@ object FeatureManager : EventListener {
if (message.startsWith(PREFIX)) {
event.isCancelled = true
val args = message.substring(PREFIX.length).split(" ")
val feature = find<Command>(args[0])
if (feature != null)
feature(args.drop(1))
else FuzzySearch.extractOne(args[0], features.flatMap { it.aliases.toList() }).let { result ->
val cmd = find<Command>(args[0])
if (cmd != null) {
cmd.args.reset()
val (success, error) = cmd.args.parse(*args.drop(1).toTypedArray())
if (success)
cmd()
else {
sendChatMessage("§c$error")
sendChatMessage("§cUsage: ${cmd.usage}")
}
} else FuzzySearch.extractOne(args[0], features.flatMap { it.aliases.toList() }).let { result ->
val response = StringBuilder("Unknown command.")
if (result.score > 80 && args[0].isNotBlank()) response.append(" Did you mean ${PREFIX + result.string}?")
else response.append(" Try using ${PREFIX}help for a list of commands.")
Expand Down
163 changes: 161 additions & 2 deletions src/main/kotlin/dev/lyzev/schizoid/feature/features/command/Command.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,164 @@ import dev.lyzev.schizoid.feature.Feature
* @param key The keybind of the command.
* @property category The category of the command.
*/
abstract class Command(name: String, desc: String, vararg aliases: String, key: Int, category: Category) :
Feature(name, desc, aliases = aliases, key, category), (List<String>) -> Unit
abstract class Command(
name: String, desc: String, val args: Arguments, vararg aliases: String, key: Int, category: Category
) : Feature(name, desc, aliases = aliases, key, category), () -> Unit {

val usage = aliases.first() + " " + args.usage

/**
* The arguments of a command.
*
* @property args The arguments of the command.
* @property usage The usage of the command.
* @property size The size of the command.
*/
class Arguments(vararg val args: Argument<*>) {

val usage = args.joinToString(" ") { if (it.isRequired) "<$it>" else "[$it]" }
val size = args.filter { it.isRequired }.size..args.size

fun parse(vararg inputs: String): Pair<Boolean, String> {
if (size.last <= 0) return true to ""
if (inputs.size !in size) return false to "Invalid number of arguments: ${inputs.size} (expected: $size)"

var required = 0
var i = 0
while (i < inputs.size && i + required < args.size) {
val arg = args[i + required]
val input = inputs[i]

val (success, error) = arg.parse(input)
if (!success) {
if (arg.isRequired || inputs.size == args.size) return false to error
else {
required++
continue
}
}
i++
}

return !(args.any { it.isRequired && it.value == null }) to "Not all required arguments were provided"
}

fun reset() = args.forEach { it.value = null }
}

/**
* An argument for a command.
*
* @property name The name of the argument.
* @property desc The description of the argument.
* @property isRequired Whether the argument is required.
* @property value The value of the argument.
* @param T The type of the argument.
*/
abstract class Argument<T>(val name: String, val desc: String? = null, val isRequired: Boolean = true) {

var value: T? = null

/**
* Parses the input.
*
* @param input The input to parse.
*/
abstract fun parse(input: String): Pair<Boolean, String>

override fun toString(): String = "$name${desc?.let { " : $it" } ?: ""}"
}

/**
* A string argument.
*
* @property name The name of the argument.
* @property desc The description of the argument.
* @property isRequired Whether the argument is required.
* @property value The value of the argument.
* @param T The type of the argument.
*/
class StringArgument(name: String, desc: String? = null, isRequired: Boolean = true) :
Argument<String>(name, desc, isRequired) {

override fun parse(input: String): Pair<Boolean, String> {
value = input
return value!!.isNotBlank() to "Invalid string: $input"
}
}

/**
* A long argument.
*
* @property name The name of the argument.
* @property desc The description of the argument.
* @property isRequired Whether the argument is required.
* @property value The value of the argument.
* @param T The type of the argument.
*/
class LongArgument(name: String, desc: String? = null, isRequired: Boolean = true) :
Argument<Long>(name, desc, isRequired) {

override fun parse(input: String): Pair<Boolean, String> {
value = input.toLongOrNull()
return (value != null) to "Invalid integer: $input"
}
}

/**
* A double argument.
*
* @property name The name of the argument.
* @property desc The description of the argument.
* @property isRequired Whether the argument is required.
* @property value The value of the argument.
* @param T The type of the argument.
*/
class DoubleArgument(name: String, desc: String? = null, isRequired: Boolean = true) :
Argument<Double>(name, desc, isRequired) {

override fun parse(input: String): Pair<Boolean, String> {
value = input.toDoubleOrNull()
return (value != null) to "Invalid float: $input"
}
}

/**
* A boolean argument.
*
* @property name The name of the argument.
* @property desc The description of the argument.
* @property isRequired Whether the argument is required.
* @property value The value of the argument.
* @param T The type of the argument.
*/
class BooleanArgument(name: String, desc: String? = null, isRequired: Boolean = true) :
Argument<Boolean>(name, desc, isRequired) {

override fun parse(input: String): Pair<Boolean, String> {
value = input.toBooleanStrictOrNull()
return (value != null) to "Invalid boolean: $input"
}
}

/**
* An enum argument.
*
* @property name The name of the argument.
* @property desc The description of the argument.
* @property isRequired Whether the argument is required.
* @property value The value of the argument.
* @param T The type of the argument.
*/
class EnumArgument<T : Enum<T>>(
name: String, desc: String? = null, isRequired: Boolean = true, val enum: Class<T>
) : Argument<T>(name, desc, isRequired) {

override fun parse(input: String): Pair<Boolean, String> {
value = enum.enumConstants.firstOrNull { it.name.equals(input, true) }
return (value != null) to "Invalid enum: $input"
}

override fun toString(): String = super.toString() + enum.enumConstants.joinToString(" | ", " (", ")")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,21 @@ import dev.lyzev.schizoid.feature.features.command.Command
/**
* A test command.
*/
object TestCommand : Command("Test", "This is a test command.", "test", key = -1, category = Category.MISC) {
object TestCommand : Command(
"Test",
"This is a test command.",
Arguments(
StringArgument("Test1", "This is a test argument"),
DoubleArgument("Test2", isRequired = false),
BooleanArgument("Test3")
),
"test",
key = -1,
category = Category.MISC
) {

override fun invoke(args: List<String>) {
override fun invoke() {
sendChatMessage("This is a test command.")
args.args.forEach { sendChatMessage(if (it.value != null) it.value!!.toString() else it.name + " wasn't provided") }
}
}

0 comments on commit 4559bfa

Please sign in to comment.