added args

This commit is contained in:
2024-12-13 20:21:43 +01:00
parent 0b17f8688c
commit 46cc808889
5 changed files with 371 additions and 44 deletions
@@ -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)
}
@@ -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<ArgumentBuilder<ServerCommandSource, *>>
// TODO: doesn't apply shape & duration. Error applying shape other than circle
fun listConfigArgs(configArgs: Array<String>, rootArg: RequiredArgumentBuilder<ServerCommandSource, *>) : RequiredArgumentBuilder<ServerCommandSource, *> {
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<ServerCommandSource>? }
}
// Add the configArg to the root command builder
rootArg.then(configArg)
}
return rootArg
}
fun dataClassToArray(dataClass: KClass<*>): Array<String> {
return dataClass.memberProperties.map { it.name }.toTypedArray()
}
fun parseArgNameToAccess(name: String) : KProperty1<EmitterParams, *>? {
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<EmitterParams, *>, value: String, id: String, context: CommandContext<ServerCommandSource>) {
positiveFeedback("Successfully updated parameter \"${access.name}\" to \"$value\" of Emitter ID \"$id\"", context)
}
fun intArg(access: KProperty1<EmitterParams, Int>) : RequiredArgumentBuilder<ServerCommandSource, Int> {
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<EmitterParams, Float>) : RequiredArgumentBuilder<ServerCommandSource, Float> {
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<EmitterParams, String>) : RequiredArgumentBuilder<ServerCommandSource, String> {
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<EmitterParams, ParamClasses.Duration>,
configArg: LiteralArgumentBuilder<ServerCommandSource>
) : LiteralArgumentBuilder<ServerCommandSource>
{
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<EmitterParams, ParamClasses.Duration>, context: CommandContext<ServerCommandSource>) {
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<EmitterParams, Vector3f>) : RequiredArgumentBuilder<ServerCommandSource, Float> {
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<EmitterParams, ParamClasses.PairInt>) : RequiredArgumentBuilder<ServerCommandSource, Int> {
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<EmitterParams, SpawningShape>,
configArg: LiteralArgumentBuilder<ServerCommandSource>
) : LiteralArgumentBuilder<ServerCommandSource>
{
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<EmitterParams, SpawningShape>, context: CommandContext<ServerCommandSource>) {
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)
}
@@ -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<ServerCommandSource>, 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<ServerCommandSource, String> {
@@ -163,12 +173,13 @@ object ManageEmitters {
}
}
/*private fun config() : RequiredArgumentBuilder<ServerCommandSource, String> {
return emitterList("Emitter ID")
.then(
private fun config() : RequiredArgumentBuilder<ServerCommandSource, String> {
var emitterList = emitterListArg("Emitter ID")
)
}*/
emitterList = listConfigArgs(dataClassToArray(EmitterParams::class), emitterList) as RequiredArgumentBuilder<ServerCommandSource, String>
return emitterList
}
// I am not going to torture myself by converting this to a json.
/*
@@ -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<String, EmitterParams> = mutableMapOf()
val forbiddenIds = arrayOf("", "NULL")
@@ -37,10 +42,17 @@ fun addToRegister(id: String, params: EmitterParams) : Pair<String, Boolean> {
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<EmitterParams, *>, paramValue: Any) : Boolean {
val emitter = getEmitterDataById(id) ?: return false
val updatedValues = updateParams(emitter, paramName, paramValue)
val updatedValues = updateParams(emitter, paramAccess as KProperty1<EmitterParams, Any>, paramValue)
updateRegistered(id, updatedValues.first)
return updatedValues.second
}
fun updateParams(params: EmitterParams, paramName: String, paramValue: Any) : Pair<EmitterParams, Boolean> { // TODO: map to new params
fun updateParams(params: EmitterParams, paramAccess: KProperty1<EmitterParams, Any>, paramValue: Any): Pair<EmitterParams, Boolean> {
val newParams = params.copy()
when (paramName) {
"maxCount" -> newParams.maxCount = paramValue as Int
if (paramAccess.returnType.classifier == paramValue::class) {
paramAccess as KMutableProperty1<EmitterParams, Any>
paramAccess.set(newParams, paramValue)
return Pair(newParams, true)
}
return Pair(newParams, true)
return Pair(params, false)
}