connection commands

This commit is contained in:
Bixilon 2022-05-19 18:57:13 +02:00
parent 30bfd53466
commit f3dc3219a0
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
13 changed files with 100 additions and 23 deletions

View File

@ -19,4 +19,4 @@ abstract class ReaderError(
val reader: CommandReader, val reader: CommandReader,
val start: Int, val start: Int,
val end: Int, val end: Int,
) : Exception("Error at $start-$end: ${reader.string} (at ${reader.string.substring(start, end)}") ) : Exception("Error at $start-$end: ${reader.string} (at ${reader.string.substring(start, end)})")

View File

@ -83,4 +83,8 @@ abstract class CommandNode(
} }
return suggestions return suggestions
} }
fun clear() {
children.clear()
}
} }

View File

@ -16,14 +16,19 @@ package de.bixilon.minosoft.commands.nodes
import de.bixilon.minosoft.commands.nodes.builder.CommandNodeBuilder import de.bixilon.minosoft.commands.nodes.builder.CommandNodeBuilder
import de.bixilon.minosoft.commands.stack.CommandStack import de.bixilon.minosoft.commands.stack.CommandStack
import de.bixilon.minosoft.commands.util.CommandReader import de.bixilon.minosoft.commands.util.CommandReader
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
class RootNode : CommandNode { class RootNode : CommandNode {
constructor() : super(false, null) constructor() : super(false, null)
constructor(builder: CommandNodeBuilder) : super(builder.executable, null) constructor(builder: CommandNodeBuilder) : super(builder.executable, null)
fun execute(command: String) { fun execute(command: String, connection: PlayConnection? = null) {
execute(CommandReader(command), CommandStack()) val stack = CommandStack()
if (connection != null) {
stack.connection = connection
}
execute(CommandReader(command), stack)
} }
fun getSuggestions(command: String): List<Any?> { fun getSuggestions(command: String): List<Any?> {

View File

@ -21,6 +21,7 @@ import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.pro
import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.rotation.YawRotation import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.rotation.YawRotation
import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.sort.SortProperty import de.bixilon.minosoft.commands.parser.minecraft.target.targets.selector.properties.sort.SortProperty
// See https://minecraft.fandom.com/wiki/Target_selectors
object TargetProperties { object TargetProperties {
val properties: MutableMap<String, TargetPropertyFactory<*>> = mutableMapOf() val properties: MutableMap<String, TargetPropertyFactory<*>> = mutableMapOf()

View File

@ -15,12 +15,14 @@ package de.bixilon.minosoft.commands.stack
import de.bixilon.kutil.cast.CastUtil.nullCast import de.bixilon.kutil.cast.CastUtil.nullCast
import de.bixilon.minosoft.data.entities.entities.Entity import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
class CommandStack { class CommandStack {
private val stack: MutableList<StackEntry> = mutableListOf() private val stack: MutableList<StackEntry> = mutableListOf()
val size: Int get() = stack.size val size: Int get() = stack.size
var executor: Entity? = null var executor: Entity? = null
lateinit var connection: PlayConnection
@Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE")
inline operator fun <reified T> get(name: String): T? { inline operator fun <reified T> get(name: String): T? {

View File

@ -209,7 +209,7 @@ open class CommandReader(val string: String) {
return null return null
} }
val string = string.substring(pointer, string.length) val string = string.substring(pointer, string.length)
pointer = string.length pointer = this.string.length
return string return string
} }

View File

@ -52,6 +52,7 @@ import de.bixilon.minosoft.protocol.packets.c2s.handshaking.HandshakeC2SP
import de.bixilon.minosoft.protocol.packets.c2s.login.StartC2SP import de.bixilon.minosoft.protocol.packets.c2s.login.StartC2SP
import de.bixilon.minosoft.protocol.protocol.ProtocolStates import de.bixilon.minosoft.protocol.protocol.ProtocolStates
import de.bixilon.minosoft.terminal.RunConfiguration import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.terminal.cli.CLI
import de.bixilon.minosoft.util.ServerAddress import de.bixilon.minosoft.util.ServerAddress
import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogLevels
@ -119,6 +120,9 @@ class PlayConnection(
assetsManager.unload() assetsManager.unload()
state = PlayConnectionStates.DISCONNECTED state = PlayConnectionStates.DISCONNECTED
ACTIVE_CONNECTIONS -= this ACTIVE_CONNECTIONS -= this
if (CLI.connection === this) {
CLI.connection = null
}
} }
} }
network::state.observe(this) { network::state.observe(this) {
@ -131,6 +135,10 @@ class PlayConnection(
ProtocolStates.PLAY -> { ProtocolStates.PLAY -> {
state = PlayConnectionStates.JOINING state = PlayConnectionStates.JOINING
if (CLI.connection == null) {
CLI.connection = this
}
registerEvent(CallbackEventInvoker.of<ChatMessageReceiveEvent> { registerEvent(CallbackEventInvoker.of<ChatMessageReceiveEvent> {
val additionalPrefix = when (it.type.position) { val additionalPrefix = when (it.type.position) {
ChatTextPositions.SYSTEM -> "[SYSTEM] " ChatTextPositions.SYSTEM -> "[SYSTEM] "

View File

@ -14,16 +14,38 @@
package de.bixilon.minosoft.terminal.cli package de.bixilon.minosoft.terminal.cli
import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.kutil.latch.CountUpAndDownLatch
import de.bixilon.kutil.watcher.DataWatcher.Companion.observe
import de.bixilon.kutil.watcher.DataWatcher.Companion.watched
import de.bixilon.minosoft.ShutdownReasons import de.bixilon.minosoft.ShutdownReasons
import de.bixilon.minosoft.commands.nodes.RootNode
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.terminal.commands.Commands import de.bixilon.minosoft.terminal.commands.Commands
import de.bixilon.minosoft.terminal.commands.ConnectionCommand
import de.bixilon.minosoft.util.ShutdownManager import de.bixilon.minosoft.util.ShutdownManager
import org.jline.reader.* import org.jline.reader.*
import org.jline.terminal.Terminal import org.jline.terminal.Terminal
import org.jline.terminal.TerminalBuilder import org.jline.terminal.TerminalBuilder
object CLI { object CLI {
var connection: PlayConnection? = null var connection: PlayConnection? by watched(null)
private val ROOT_NODE = RootNode()
init {
register()
}
@Synchronized
private fun register() {
ROOT_NODE.clear()
val connection = this.connection
for (command in Commands.COMMANDS) {
if (command is ConnectionCommand && connection == null) {
continue
}
ROOT_NODE.addChild(command.node)
}
}
fun startThread(latch: CountUpAndDownLatch) { fun startThread(latch: CountUpAndDownLatch) {
@ -41,6 +63,9 @@ object CLI {
.completer(NodeCompleter) .completer(NodeCompleter)
.build() .build()
this::connection.observe(this) { register() }
while (true) { while (true) {
try { try {
val line: String = reader.readLine().removeDuplicatedWhitespaces() val line: String = reader.readLine().removeDuplicatedWhitespaces()
@ -48,7 +73,7 @@ object CLI {
continue continue
} }
terminal.flush() terminal.flush()
Commands.ROOT_NODE.execute(line) ROOT_NODE.execute(line, connection)
} catch (exception: UserInterruptException) { } catch (exception: UserInterruptException) {
ShutdownManager.shutdown(reason = ShutdownReasons.ALL_FINE) ShutdownManager.shutdown(reason = ShutdownReasons.ALL_FINE)
} catch (exception: Throwable) { } catch (exception: Throwable) {
@ -64,7 +89,7 @@ object CLI {
object NodeCompleter : Completer { object NodeCompleter : Completer {
override fun complete(reader: LineReader, line: ParsedLine, candidates: MutableList<Candidate>) { override fun complete(reader: LineReader, line: ParsedLine, candidates: MutableList<Candidate>) {
val suggestions = Commands.ROOT_NODE.getSuggestions(line.line()) val suggestions = ROOT_NODE.getSuggestions(line.line())
for (suggestion in suggestions) { for (suggestion in suggestions) {
candidates += Candidate(suggestion.toString()) candidates += Candidate(suggestion.toString())
} }

View File

@ -16,6 +16,5 @@ package de.bixilon.minosoft.terminal.commands
import de.bixilon.minosoft.commands.nodes.LiteralNode import de.bixilon.minosoft.commands.nodes.LiteralNode
interface Command { interface Command {
var node: LiteralNode
fun build(): LiteralNode
} }

View File

@ -13,14 +13,9 @@
package de.bixilon.minosoft.terminal.commands package de.bixilon.minosoft.terminal.commands
import de.bixilon.minosoft.commands.nodes.RootNode
object Commands { object Commands {
val ROOT_NODE = RootNode() val COMMANDS: List<Command> = listOf(
.register(HelpCommand) HelpCommand,
SayCommand,
fun RootNode.register(command: Command): RootNode { )
this.addChild(command.build())
return this
}
} }

View File

@ -0,0 +1,16 @@
/*
* 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
interface ConnectionCommand : Command

View File

@ -20,11 +20,8 @@ import de.bixilon.minosoft.commands.nodes.LiteralNode
import de.bixilon.minosoft.commands.parser.minosoft.enums.EnumParser import de.bixilon.minosoft.commands.parser.minosoft.enums.EnumParser
object HelpCommand : Command { object HelpCommand : Command {
override var node: LiteralNode = LiteralNode("help", setOf("?"), executor = { printHelp() })
override fun build(): LiteralNode { .addChild(ArgumentNode("subcommand", parser = EnumParser(HelpCommands), executor = { printHelp(it["subcommand"]!!) }))
return LiteralNode("help", setOf("?"), executor = { printHelp() })
.addChild(ArgumentNode("subcommand", parser = EnumParser(HelpCommands), executor = { printHelp(it["subcommand"]!!) }))
}
fun printHelp() { fun printHelp() {
println("-------------- Minosoft help --------------") println("-------------- Minosoft help --------------")

View File

@ -0,0 +1,25 @@
/*
* 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.ArgumentNode
import de.bixilon.minosoft.commands.nodes.LiteralNode
import de.bixilon.minosoft.commands.parser.brigadier.string.StringParser
object SayCommand : ConnectionCommand {
override var node = LiteralNode("say", setOf("chat", "send", "write"))
.addChild(ArgumentNode("message", StringParser(StringParser.StringModes.GREEDY), executor = {
it.connection.util.sendChatMessage(it.get<String>("message")!!)
}))
}