From 46cc80888910b1eede01790dc5251a454ef04768 Mon Sep 17 00:00:00 2001 From: Byte Dice Date: Fri, 13 Dec 2024 20:21:43 +0100 Subject: [PATCH] added args --- .../client/Bde_particlesClient.kt | 3 +- .../bytedice/bde_particles/Bde_particles.kt | 3 +- .../bde_particles/commands/ConfigArgs.kt | 298 ++++++++++++++++++ .../bde_particles/commands/ManageEmitters.kt | 79 +++-- .../particles/ParticleIdRegister.kt | 32 +- 5 files changed, 371 insertions(+), 44 deletions(-) create mode 100644 src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt diff --git a/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt b/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt index beb0108..6153ffe 100644 --- a/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt +++ b/src/client/kotlin/com/bytedice/bde_particles/client/Bde_particlesClient.kt @@ -2,6 +2,7 @@ package com.bytedice.bde_particles.client import com.bytedice.bde_particles.commands.GiveEmitterTool import com.bytedice.bde_particles.commands.KillAllEmitters +import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.commands.SpawnEmitter //import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.init @@ -39,7 +40,7 @@ class Bde_particlesClient : ClientModInitializer { CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> GiveEmitterTool.register(dispatcher, registryAccess) - //ManageEmitters .register(dispatcher) + ManageEmitters .register(dispatcher) KillAllEmitters.register(dispatcher) SpawnEmitter .register(dispatcher) } diff --git a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt index a6c1c0c..cfabaab 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/Bde_particles.kt @@ -2,6 +2,7 @@ package com.bytedice.bde_particles import com.bytedice.bde_particles.commands.GiveEmitterTool import com.bytedice.bde_particles.commands.KillAllEmitters +import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.commands.SpawnEmitter //import com.bytedice.bde_particles.commands.ManageEmitters import com.bytedice.bde_particles.items.ParticleEmitterTool @@ -90,7 +91,7 @@ class Bde_particles : ModInitializer { CommandRegistrationCallback.EVENT.register { dispatcher, registryAccess, _ -> GiveEmitterTool.register(dispatcher, registryAccess) - //ManageEmitters .register(dispatcher) + ManageEmitters .register(dispatcher) KillAllEmitters.register(dispatcher) SpawnEmitter .register(dispatcher) } diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt new file mode 100644 index 0000000..1f8a829 --- /dev/null +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ConfigArgs.kt @@ -0,0 +1,298 @@ +package com.bytedice.bde_particles.commands + +import com.bytedice.bde_particles.particles.EmitterParams +import com.bytedice.bde_particles.particles.ParamClasses +import com.bytedice.bde_particles.particles.SpawningShape +import com.bytedice.bde_particles.particles.updateParam +import com.bytedice.bde_particles.positiveFeedback +import com.mojang.brigadier.Command +import com.mojang.brigadier.arguments.BoolArgumentType +import com.mojang.brigadier.arguments.FloatArgumentType +import com.mojang.brigadier.arguments.IntegerArgumentType +import com.mojang.brigadier.arguments.StringArgumentType +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 net.minecraft.server.command.CommandManager +import net.minecraft.server.command.ServerCommandSource +import org.joml.Vector2f +import org.joml.Vector3f +import java.util.concurrent.CompletableFuture +import kotlin.reflect.* +import kotlin.reflect.full.memberProperties + + +typealias CommandArgumentBuilder = KFunction> + + +// TODO: doesn't apply shape & duration. Error applying shape other than circle + +fun listConfigArgs(configArgs: Array, rootArg: RequiredArgumentBuilder) : RequiredArgumentBuilder { + configArgs.forEach { arg -> + var configArg = CommandManager.literal(arg) + + val argAccess = parseArgNameToAccess(arg)!! + val func = parseArgTypeToFunc(argAccess.returnType) + + if (func != null) { + if (func.parameters.lastIndex == 0) { configArg.then(func.call(argAccess)) } + else { configArg = func.call(argAccess, configArg) as LiteralArgumentBuilder? } + } + + // Add the configArg to the root command builder + rootArg.then(configArg) + } + + return rootArg +} + + +fun dataClassToArray(dataClass: KClass<*>): Array { + return dataClass.memberProperties.map { it.name }.toTypedArray() +} + + +fun parseArgNameToAccess(name: String) : KProperty1? { + val access = EmitterParams::class.memberProperties + .firstOrNull { it.name == name } + + return access +} + + +fun parseArgTypeToFunc(type: KType) : CommandArgumentBuilder? { + when (type.classifier) { + Int::class -> return ::intArg + Float::class -> return ::floatArg + String::class -> return ::stringArg + ParamClasses.Duration::class -> return ::durArg + Vector3f::class -> return ::vec3fArg + ParamClasses.PairInt::class -> return ::pairIntArg + SpawningShape::class -> return ::shapeArg + ParamClasses.PairVec3f::class -> println("Type is PairVec3f") + ParamClasses.PairFloat::class -> println("Type is PairFloat") + ParamClasses.TransformWithVel::class -> println("Type is TransformWithVel") + Array::class -> println("Type is Array") + ParamClasses.LerpVal::class -> println("Type is LerpVal") + Pair::class -> println("Type is Pair") + else -> return null + } + + return null +} + + +fun successText(access: KProperty1, value: String, id: String, context: CommandContext) { + positiveFeedback("Successfully updated parameter \"${access.name}\" to \"$value\" of Emitter ID \"$id\"", context) +} + + +fun intArg(access: KProperty1) : RequiredArgumentBuilder { + return CommandManager.argument("Int", IntegerArgumentType.integer()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val value = IntegerArgumentType.getInteger(context, "Int") + + updateParam(id, access, value) + + successText(access, value.toString(), id, context) + Command.SINGLE_SUCCESS + } +} + +fun floatArg(access: KProperty1) : RequiredArgumentBuilder { + return CommandManager.argument("Float", FloatArgumentType.floatArg()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val value = FloatArgumentType.getFloat(context, "Float") + + updateParam(id, access, value) + + successText(access, value.toString(), id, context) + Command.SINGLE_SUCCESS + } +} + +fun stringArg(access: KProperty1) : RequiredArgumentBuilder { + return CommandManager.argument("String", StringArgumentType.string()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val value = StringArgumentType.getString(context, "String") + + updateParam(id, access, value) + + successText(access, value.toString(), id, context) + Command.SINGLE_SUCCESS + } +} + +fun durArg(access: KProperty1, + configArg: LiteralArgumentBuilder + ) : LiteralArgumentBuilder +{ + return configArg + .then(CommandManager.literal("SingleBurst") + .then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) + .executes { context -> durArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + .then(CommandManager.literal("MultiBurst") + .then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) + .then(CommandManager.argument("loopDelay", IntegerArgumentType.integer()) + .then(CommandManager.argument("loopCount", IntegerArgumentType.integer()) + .executes { context -> durArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + ) + ) + .then(CommandManager.literal("InfiniteLoop") + .then(CommandManager.argument("loopDur", IntegerArgumentType.integer()) + .then(CommandManager.argument("loopDelay", IntegerArgumentType.integer()) + .executes { context -> durArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + ) +} + +fun durArgExec(access: KProperty1, context: CommandContext) { + val id = StringArgumentType.getString(context, "Emitter ID") + val loopDur = IntegerArgumentType.getInteger(context, "loopDur") + + val eParams = EmitterParams.DEFAULT + + when (access.get(eParams)) { + is ParamClasses.Duration.SingleBurst -> { + updateParam(id, access, ParamClasses.Duration.SingleBurst(loopDur)) + } + is ParamClasses.Duration.MultiBurst -> { + val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay") + val loopCount = IntegerArgumentType.getInteger(context, "loopCount") + updateParam(id, access, ParamClasses.Duration.MultiBurst(loopDur, loopDelay, loopCount)) + } + is ParamClasses.Duration.InfiniteLoop -> { + val loopDelay = IntegerArgumentType.getInteger(context, "loopDelay") + updateParam(id, access, ParamClasses.Duration.InfiniteLoop(loopDur, loopDelay)) + } + } + + successText(access, access.get(eParams)::class.simpleName ?: "UNKNOWN", id, context) +} + +fun vec3fArg(access: KProperty1) : RequiredArgumentBuilder { + return CommandManager.argument("X", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Y", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Z", FloatArgumentType.floatArg()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val valueX = FloatArgumentType.getFloat(context, "X") + val valueY = FloatArgumentType.getFloat(context, "Y") + val valueZ = FloatArgumentType.getFloat(context, "Z") + + updateParam(id, access, Vector3f(valueX, valueY, valueX)) + + successText(access, "[$valueX, $valueY, $valueZ]", id, context) + Command.SINGLE_SUCCESS + } + ) + ) +} + +fun pairIntArg(access: KProperty1) : RequiredArgumentBuilder { + return CommandManager.argument("First Int", IntegerArgumentType.integer()) + .then(CommandManager.argument("Second Int", IntegerArgumentType.integer()) + .executes { context -> + val id = StringArgumentType.getString(context, "Emitter ID") + val valueX = IntegerArgumentType.getInteger(context, "First Int") + val valueY = IntegerArgumentType.getInteger(context, "Second Int") + + updateParam(id, access, ParamClasses.PairInt(valueX, valueY)) + successText(access, "[$valueX, $valueY]", id, context) + Command.SINGLE_SUCCESS + } + ) +} + +fun shapeArg(access: KProperty1, + configArg: LiteralArgumentBuilder + ) : LiteralArgumentBuilder +{ + return configArg + .then(CommandManager.literal("Circle") + .then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) + .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + ) + .then(CommandManager.literal("Rect") + .then(CommandManager.argument("X", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Y", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) + .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + ) + ) + .then(CommandManager.literal("Cube") + .then(CommandManager.argument("X", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Y", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Z", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) + .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + ) + ) + ) + .then(CommandManager.literal("Sphere") + .then(CommandManager.argument("Radius", FloatArgumentType.floatArg()) + .then(CommandManager.argument("Spawn On Edge", BoolArgumentType.bool()) + .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + ) + ) + ) + .then(CommandManager.literal("Point") + .executes { context -> shapeArgExec(access, context); Command.SINGLE_SUCCESS } + ) +} + +fun shapeArgExec(access: KProperty1, context: CommandContext) { + val id = StringArgumentType.getString(context, "Emitter ID") + + val eParams = EmitterParams.DEFAULT + + println(access.get(eParams)::class) + + when (access.get(eParams)) { + is SpawningShape.Circle -> { + val radius = FloatArgumentType.getFloat(context, "Radius") + val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") + updateParam(id, access, SpawningShape.Circle(radius, onEdge)) + } + is SpawningShape.Rect -> { + val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") + val x = FloatArgumentType.getFloat(context, "X") + val y = FloatArgumentType.getFloat(context, "Y") + updateParam(id, access, SpawningShape.Rect(Vector2f(x, y), onEdge)) + } + is SpawningShape.Cube -> { + val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") + val x = FloatArgumentType.getFloat(context, "X") + val y = FloatArgumentType.getFloat(context, "Y") + val z = FloatArgumentType.getFloat(context, "Z") + updateParam(id, access, SpawningShape.Cube(Vector3f(x, y, z), onEdge)) + } + is SpawningShape.Sphere -> { + val radius = FloatArgumentType.getFloat(context, "Radius") + val onEdge = BoolArgumentType.getBool(context, "Spawn On Edge") + updateParam(id, access, SpawningShape.Sphere(radius, onEdge)) + println("$id, ${access.name}, $radius, $onEdge") + } + is SpawningShape.Point -> { + updateParam(id, access, SpawningShape.Point) + } + } + + successText(access, access.get(eParams)::class.simpleName ?: "UNKNOWN", id, context) +} \ No newline at end of file diff --git a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt index 19611f7..879ae5a 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/commands/ManageEmitters.kt @@ -8,6 +8,7 @@ import com.mojang.brigadier.Command import com.mojang.brigadier.CommandDispatcher import com.mojang.brigadier.arguments.StringArgumentType import com.mojang.brigadier.builder.RequiredArgumentBuilder +import com.mojang.brigadier.context.CommandContext import net.minecraft.server.command.CommandManager import net.minecraft.server.command.ServerCommandSource import net.minecraft.text.Style @@ -84,12 +85,12 @@ object ManageEmitters { command .then(CommandManager.literal("create").then(create())) .then(CommandManager.literal("remove").then(remove())) - .then(CommandManager.literal("config")) + .then(CommandManager.literal("config").then(config())) .then(CommandManager.literal("list") .executes { context -> val emitterIdList = idRegister.keys.sorted().joinToString(" ") - val feedback = Text.literal("List of all registered Emitter IDs $emitterIdList") + val feedback = Text.literal("List of all registered Emitter IDs:\n$emitterIdList") .setStyle(Style.EMPTY.withColor(Color(0, 200, 0).rgb)) context.source.sendFeedback({ feedback }, false) @@ -107,36 +108,45 @@ object ManageEmitters { val newEmitterId = StringArgumentType.getString(context, "New Emitter ID") val presetEmitterId = StringArgumentType.getString(context, "Preset Emitter ID") - val params = getEmitterDataById(presetEmitterId) - val usedDefault = params == null - - val result = addToRegister( - newEmitterId, - if (usedDefault) { EmitterParams.DEFAULT } else { params!! }, - ) - - if (usedDefault && result.second) { - positiveFeedback( - "Successfully added new Emitter with ID \"${result.first}\" to register.\n(DEFAULT params were used as a preset because the specified Preset Emitter ID wasn't valid.)", - context - ) - } - else if (!usedDefault && result.second) { - positiveFeedback( - "Successfully added new Emitter with ID \"${result.first}\" and Preset Emitter ID \"$presetEmitterId\" to register.", - context - ) - } - else { - negativeFeedback( - "Failed to add new Emitter with ID \"${result.first}\" to register.\nThe provided Emitter ID is either reserved for system use or already in use.", - context - ) - } - + createExec(context, newEmitterId, presetEmitterId) Command.SINGLE_SUCCESS } ) + .executes { context -> + val newEmitterId = StringArgumentType.getString(context, "New Emitter ID") + + createExec(context, newEmitterId, "") + Command.SINGLE_SUCCESS + } + } + + private fun createExec(context: CommandContext, newEmitterId: String, presetEmitterId: String) { + val params = getEmitterDataById(presetEmitterId) + val usedDefault = params == null + + val result = addToRegister( + newEmitterId, + if (usedDefault) { EmitterParams.DEFAULT } else { params!! }, + ) + + if (usedDefault && result.second) { + positiveFeedback( + "Successfully added new Emitter with ID \"${result.first}\" to register.\n(DEFAULT params were used as a preset because the specified Preset Emitter ID wasn't valid.)", + context + ) + } + else if (!usedDefault && result.second) { + positiveFeedback( + "Successfully added new Emitter with ID \"${result.first}\" and Preset Emitter ID \"$presetEmitterId\" to register.", + context + ) + } + else { + negativeFeedback( + "Failed to add new Emitter with ID \"${result.first}\" to register.\nThe provided Emitter ID is either reserved for system use or already in use.", + context + ) + } } private fun remove() : RequiredArgumentBuilder { @@ -163,12 +173,13 @@ object ManageEmitters { } } - /*private fun config() : RequiredArgumentBuilder { - return emitterList("Emitter ID") - .then( + private fun config() : RequiredArgumentBuilder { + var emitterList = emitterListArg("Emitter ID") - ) - }*/ + emitterList = listConfigArgs(dataClassToArray(EmitterParams::class), emitterList) as RequiredArgumentBuilder + + return emitterList + } // I am not going to torture myself by converting this to a json. /* diff --git a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt index efa44e1..7caade6 100644 --- a/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt +++ b/src/main/kotlin/com/bytedice/bde_particles/particles/ParticleIdRegister.kt @@ -1,5 +1,10 @@ package com.bytedice.bde_particles.particles +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.memberProperties +import kotlin.reflect.jvm.isAccessible + val idRegister: MutableMap = mutableMapOf() val forbiddenIds = arrayOf("", "NULL") @@ -37,10 +42,17 @@ fun addToRegister(id: String, params: EmitterParams) : Pair { fun removeFromRegister(id: String) : Boolean { - if (id in forbiddenIds) { return false } - if (!idRegister.containsKey(id)) { return false } + if (!idRegister.containsKey(id)) { + println("BPS - Failed to remove Emitter ID \"$id\" as it doesn't exist!") + return false + } + if (id in forbiddenIds) { + println("BPS - Failed to remove Emitter ID \"$id\" as it's been reserved for system use!") + return false + } idRegister.remove(id) + println("BPS - Removed Emitter ID \"$id\" from register.") return true } @@ -55,22 +67,26 @@ fun updateRegistered(id: String, newParams: EmitterParams) { } -fun updateParam(id: String, paramName: String, paramValue: Any) : Boolean { +fun updateParam(id: String, paramAccess: KProperty1, paramValue: Any) : Boolean { val emitter = getEmitterDataById(id) ?: return false - val updatedValues = updateParams(emitter, paramName, paramValue) + val updatedValues = updateParams(emitter, paramAccess as KProperty1, paramValue) + + updateRegistered(id, updatedValues.first) return updatedValues.second } -fun updateParams(params: EmitterParams, paramName: String, paramValue: Any) : Pair { // TODO: map to new params +fun updateParams(params: EmitterParams, paramAccess: KProperty1, paramValue: Any): Pair { val newParams = params.copy() - when (paramName) { - "maxCount" -> newParams.maxCount = paramValue as Int + if (paramAccess.returnType.classifier == paramValue::class) { + paramAccess as KMutableProperty1 + paramAccess.set(newParams, paramValue) + return Pair(newParams, true) } - return Pair(newParams, true) + return Pair(params, false) }