mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-18 20:05:02 -04:00
command execution
This commit is contained in:
parent
5135fa4d27
commit
3ec82ad51f
@ -46,6 +46,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||
import de.bixilon.minosoft.terminal.AutoConnect
|
||||
import de.bixilon.minosoft.terminal.CommandLineArguments
|
||||
import de.bixilon.minosoft.terminal.RunConfiguration
|
||||
import de.bixilon.minosoft.terminal.cli.CLI
|
||||
import de.bixilon.minosoft.util.GitInfo
|
||||
import de.bixilon.minosoft.util.KUtil
|
||||
import de.bixilon.minosoft.util.RenderPolling
|
||||
@ -75,6 +76,8 @@ object Minosoft {
|
||||
|
||||
val taskWorker = TaskWorker(criticalErrorHandler = { _, exception -> exception.crash() })
|
||||
|
||||
taskWorker += Task(identifier = BootTasks.CLI, priority = ThreadPool.HIGH, executor = CLI::startThread)
|
||||
|
||||
taskWorker += Task(identifier = BootTasks.PACKETS, priority = ThreadPool.HIGH, executor = PacketTypeRegistry::init)
|
||||
taskWorker += Task(identifier = BootTasks.VERSIONS, priority = ThreadPool.HIGH, dependencies = arrayOf(BootTasks.PACKETS), executor = Versions::load)
|
||||
taskWorker += Task(identifier = BootTasks.PROFILES, priority = ThreadPool.HIGH, dependencies = arrayOf(BootTasks.VERSIONS), executor = GlobalProfileManager::initialize)
|
||||
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.commands.errors
|
||||
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
class ExpectedArgumentError(
|
||||
reader: CommandReader,
|
||||
) : ReaderError(reader, reader.pointer, reader.pointer)
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.commands.errors.literal
|
||||
|
||||
import de.bixilon.minosoft.commands.errors.ReaderError
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
class ExpectedLiteralArgument(
|
||||
reader: CommandReader,
|
||||
) : ReaderError(reader, reader.pointer, reader.pointer)
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.commands.errors.literal
|
||||
|
||||
import de.bixilon.minosoft.commands.errors.ReaderError
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
class InvalidLiteralArgumentError(
|
||||
reader: CommandReader,
|
||||
val argument: String,
|
||||
) : ReaderError(reader, reader.pointer - argument.length, reader.pointer)
|
@ -26,7 +26,7 @@ class ArgumentNode : ExecutableNode {
|
||||
suggestion: SuggestionType<*>? = null,
|
||||
executable: Boolean = false,
|
||||
redirect: CommandNode? = null,
|
||||
) : super(name, suggestion, false, null, executable, redirect) {
|
||||
) : super(name, setOf(), suggestion, false, null, executable, redirect) {
|
||||
this.parser = parser
|
||||
}
|
||||
|
||||
@ -35,4 +35,9 @@ class ArgumentNode : ExecutableNode {
|
||||
this.executor = executor
|
||||
this.parser = parser
|
||||
}
|
||||
|
||||
override fun addChild(node: CommandNode): ArgumentNode {
|
||||
super.addChild(node)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,37 @@
|
||||
|
||||
package de.bixilon.minosoft.commands.nodes
|
||||
|
||||
import de.bixilon.minosoft.commands.stack.CommandStack
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
abstract class CommandNode(
|
||||
val executable: Boolean,
|
||||
val redirect: CommandNode?,
|
||||
)
|
||||
) {
|
||||
protected val children: MutableList<CommandNode> = mutableListOf()
|
||||
|
||||
open fun addChild(node: CommandNode): CommandNode {
|
||||
children += node
|
||||
return this
|
||||
}
|
||||
|
||||
protected open fun executeChild(child: CommandNode, reader: CommandReader, stack: CommandStack) {
|
||||
child.execute(reader, stack)
|
||||
}
|
||||
|
||||
open fun execute(reader: CommandReader, stack: CommandStack) {
|
||||
val pointer = reader.pointer
|
||||
val stackSize = stack.size
|
||||
var lastError: Throwable? = null
|
||||
for (child in children) {
|
||||
try {
|
||||
return executeChild(child, reader, stack)
|
||||
} catch (error: Throwable) {
|
||||
lastError = error
|
||||
}
|
||||
reader.pointer = pointer
|
||||
stack.reset(stackSize)
|
||||
}
|
||||
throw lastError ?: return
|
||||
}
|
||||
}
|
||||
|
@ -13,14 +13,47 @@
|
||||
|
||||
package de.bixilon.minosoft.commands.nodes
|
||||
|
||||
import de.bixilon.minosoft.commands.errors.ExpectedArgumentError
|
||||
import de.bixilon.minosoft.commands.stack.CommandExecutor
|
||||
import de.bixilon.minosoft.commands.stack.CommandStack
|
||||
import de.bixilon.minosoft.commands.suggestion.types.SuggestionType
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
abstract class ExecutableNode(
|
||||
val name: String,
|
||||
val aliases: Set<String> = setOf(),
|
||||
val suggestion: SuggestionType<*>? = null,
|
||||
var onlyDirectExecution: Boolean = true,
|
||||
var executor: CommandExecutor? = null,
|
||||
executable: Boolean = executor != null,
|
||||
redirect: CommandNode? = null,
|
||||
) : CommandNode(executable, redirect)
|
||||
) : CommandNode(executable, redirect) {
|
||||
|
||||
private fun execute(stack: CommandStack) {
|
||||
try {
|
||||
executor?.invoke(stack)
|
||||
} catch (exception: Throwable) {
|
||||
exception.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(reader: CommandReader, stack: CommandStack) {
|
||||
reader.skipWhitespaces()
|
||||
if (!reader.canPeekNext()) {
|
||||
// empty string
|
||||
if (executable) {
|
||||
return execute(stack)
|
||||
} else {
|
||||
throw ExpectedArgumentError(reader)
|
||||
}
|
||||
}
|
||||
super.execute(reader, stack)
|
||||
}
|
||||
|
||||
override fun executeChild(child: CommandNode, reader: CommandReader, stack: CommandStack) {
|
||||
super.executeChild(child, reader, stack)
|
||||
if (!onlyDirectExecution) {
|
||||
execute(stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,18 +13,38 @@
|
||||
|
||||
package de.bixilon.minosoft.commands.nodes
|
||||
|
||||
import de.bixilon.minosoft.commands.errors.literal.ExpectedLiteralArgument
|
||||
import de.bixilon.minosoft.commands.errors.literal.InvalidLiteralArgumentError
|
||||
import de.bixilon.minosoft.commands.stack.CommandExecutor
|
||||
import de.bixilon.minosoft.commands.stack.CommandStack
|
||||
import de.bixilon.minosoft.commands.suggestion.types.SuggestionType
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
class LiteralNode : ExecutableNode {
|
||||
|
||||
constructor(
|
||||
name: String,
|
||||
aliases: Set<String> = setOf(),
|
||||
suggestion: SuggestionType<*>? = null,
|
||||
executable: Boolean = false,
|
||||
redirect: CommandNode? = null,
|
||||
) : super(name, suggestion, false, null, executable, redirect)
|
||||
) : super(name, aliases, suggestion, false, null, executable, redirect)
|
||||
|
||||
|
||||
constructor(name: String, onlyDirectExecution: Boolean = true, executor: CommandExecutor) : super(name, onlyDirectExecution = onlyDirectExecution, executor = executor, executable = true)
|
||||
constructor(name: String, aliases: Set<String> = setOf(), onlyDirectExecution: Boolean = true, executor: CommandExecutor) : super(name, aliases, onlyDirectExecution = onlyDirectExecution, executor = executor, executable = true)
|
||||
|
||||
override fun addChild(node: CommandNode): LiteralNode {
|
||||
super.addChild(node)
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
override fun execute(reader: CommandReader, stack: CommandStack) {
|
||||
val literalName = reader.readUnquotedString() ?: throw ExpectedLiteralArgument(reader)
|
||||
if (literalName != name && literalName !in aliases) {
|
||||
throw InvalidLiteralArgumentError(reader, literalName)
|
||||
}
|
||||
stack.push(name, name)
|
||||
return super.execute(reader, stack)
|
||||
}
|
||||
}
|
||||
|
@ -13,4 +13,12 @@
|
||||
|
||||
package de.bixilon.minosoft.commands.nodes
|
||||
|
||||
class RootNode : CommandNode(false, null)
|
||||
import de.bixilon.minosoft.commands.stack.CommandStack
|
||||
import de.bixilon.minosoft.commands.util.CommandReader
|
||||
|
||||
class RootNode : CommandNode(false, null) {
|
||||
|
||||
fun execute(command: String) {
|
||||
execute(CommandReader(command), CommandStack())
|
||||
}
|
||||
}
|
||||
|
@ -13,4 +13,32 @@
|
||||
|
||||
package de.bixilon.minosoft.commands.stack
|
||||
|
||||
class CommandStack
|
||||
import de.bixilon.kutil.cast.CastUtil.nullCast
|
||||
|
||||
class CommandStack {
|
||||
private val stack: MutableList<StackEntry> = mutableListOf()
|
||||
val size: Int get() = stack.size
|
||||
|
||||
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
|
||||
inline operator fun <reified T> get(name: String): T? {
|
||||
return getAny(name).nullCast()
|
||||
}
|
||||
|
||||
fun getAny(name: String): Any? {
|
||||
return stack.find { it.name == name }?.data
|
||||
}
|
||||
|
||||
fun reset(size: Int) {
|
||||
var index = 0
|
||||
stack.removeAll { index++ >= size }
|
||||
}
|
||||
|
||||
fun push(name: String, data: Any) {
|
||||
stack.add(StackEntry(name, data))
|
||||
}
|
||||
|
||||
private data class StackEntry(
|
||||
val name: String,
|
||||
val data: Any?,
|
||||
)
|
||||
}
|
||||
|
@ -27,5 +27,6 @@ enum class BootTasks {
|
||||
YGGDRASIL,
|
||||
STARTUP_PROGRESS,
|
||||
ASSETS_OVERRIDE,
|
||||
CLI,
|
||||
;
|
||||
}
|
||||
|
64
src/main/java/de/bixilon/minosoft/terminal/cli/CLI.kt
Normal file
64
src/main/java/de/bixilon/minosoft/terminal/cli/CLI.kt
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.terminal.cli
|
||||
|
||||
import de.bixilon.kutil.latch.CountUpAndDownLatch
|
||||
import de.bixilon.minosoft.ShutdownReasons
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.terminal.commands.Commands
|
||||
import de.bixilon.minosoft.util.ShutdownManager
|
||||
import org.jline.reader.LineReader
|
||||
import org.jline.reader.LineReaderBuilder
|
||||
import org.jline.reader.UserInterruptException
|
||||
import org.jline.terminal.Terminal
|
||||
import org.jline.terminal.TerminalBuilder
|
||||
|
||||
object CLI {
|
||||
var connection: PlayConnection? = null
|
||||
|
||||
|
||||
fun startThread(latch: CountUpAndDownLatch) {
|
||||
latch.inc()
|
||||
Thread({ latch.dec(); startLoop() }, "CLI").start()
|
||||
}
|
||||
|
||||
private fun startLoop() {
|
||||
val builder = TerminalBuilder.builder()
|
||||
|
||||
val terminal: Terminal = builder.build()
|
||||
val reader: LineReader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
// .completer() // ToDo
|
||||
.build()
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
val line: String = reader.readLine().removeDuplicatedWhitespaces()
|
||||
if (line.isBlank()) {
|
||||
continue
|
||||
}
|
||||
terminal.flush()
|
||||
Commands.ROOT_NODE.execute(line)
|
||||
} catch (exception: UserInterruptException) {
|
||||
ShutdownManager.shutdown(reason = ShutdownReasons.ALL_FINE)
|
||||
} catch (exception: Throwable) {
|
||||
exception.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.removeDuplicatedWhitespaces(): String {
|
||||
return this.replace("\\s{2,}".toRegex(), "")
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.terminal.commands
|
||||
|
||||
import de.bixilon.minosoft.commands.nodes.RootNode
|
||||
|
||||
object Commands {
|
||||
val ROOT_NODE = RootNode()
|
||||
.register(HelpCommand)
|
||||
|
||||
fun RootNode.register(command: Command): RootNode {
|
||||
this.addChild(command.build())
|
||||
return this
|
||||
}
|
||||
}
|
@ -15,9 +15,17 @@ package de.bixilon.minosoft.terminal.commands
|
||||
|
||||
import de.bixilon.minosoft.commands.nodes.LiteralNode
|
||||
|
||||
class HelpCommand : Command {
|
||||
object HelpCommand : Command {
|
||||
|
||||
override fun build(): LiteralNode {
|
||||
TODO("Not yet implemented")
|
||||
return LiteralNode("help", setOf("?"), onlyDirectExecution = false, executor = {
|
||||
printHelp(it["general"])
|
||||
})
|
||||
.addChild(LiteralNode("general", executable = true))
|
||||
}
|
||||
|
||||
fun printHelp(subcommand: String?) {
|
||||
println("-------------- Minosoft help --------------")
|
||||
println("Subcommand: $subcommand")
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user