Support reloading features and commands at runtime
This means that if you add a new feature, you do not need to restart the game to hot-swap it into a running game (provided you're in debug mode)main
parent
e28dd79cee
commit
c0ec62d186
|
@ -1,9 +1,9 @@
|
||||||
package codes.som.hibiscus
|
package codes.som.hibiscus
|
||||||
|
|
||||||
import codes.som.hibiscus.api.command.CommandManager
|
|
||||||
import codes.som.hibiscus.api.event.EventBus
|
import codes.som.hibiscus.api.event.EventBus
|
||||||
import codes.som.hibiscus.api.event.EventPhase
|
import codes.som.hibiscus.api.event.EventPhase
|
||||||
import codes.som.hibiscus.api.keybinds.KeybindRegistry
|
import codes.som.hibiscus.api.keybinds.KeybindRegistry
|
||||||
|
import codes.som.hibiscus.commands.CommandRegistry
|
||||||
import codes.som.hibiscus.events.KeyEvent
|
import codes.som.hibiscus.events.KeyEvent
|
||||||
import codes.som.hibiscus.features.FeaturesRegistry
|
import codes.som.hibiscus.features.FeaturesRegistry
|
||||||
import codes.som.hibiscus.gui.ImGuiScreen
|
import codes.som.hibiscus.gui.ImGuiScreen
|
||||||
|
@ -23,7 +23,7 @@ object HibiscusMod : ModInitializer {
|
||||||
val bus = EventBus()
|
val bus = EventBus()
|
||||||
|
|
||||||
val features = FeaturesRegistry()
|
val features = FeaturesRegistry()
|
||||||
val commands = CommandManager()
|
val commands = CommandRegistry()
|
||||||
val keybinds = KeybindRegistry()
|
val keybinds = KeybindRegistry()
|
||||||
|
|
||||||
override fun onInitialize() {
|
override fun onInitialize() {
|
||||||
|
@ -44,5 +44,15 @@ object HibiscusMod : ModInitializer {
|
||||||
bus.register(NetworkMovingDispatcher(), EventPhase.AFTER)
|
bus.register(NetworkMovingDispatcher(), EventPhase.AFTER)
|
||||||
bus.register(ChatCommandListener())
|
bus.register(ChatCommandListener())
|
||||||
bus.register(KeybindDispatcher())
|
bus.register(KeybindDispatcher())
|
||||||
|
|
||||||
|
// TODO: Load files
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shutdown() {
|
||||||
|
// TODO: Save files
|
||||||
|
|
||||||
|
for (system in sequenceOf(bus, features, commands, keybinds)) {
|
||||||
|
system.reset()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,12 @@ import codes.som.hibiscus.api.command.exceptions.*
|
||||||
import codes.som.hibiscus.api.command.parser.ArgumentParser
|
import codes.som.hibiscus.api.command.parser.ArgumentParser
|
||||||
import codes.som.hibiscus.api.command.utils.PeekableIterator
|
import codes.som.hibiscus.api.command.utils.PeekableIterator
|
||||||
import codes.som.hibiscus.api.command.utils.splitExceptingQuotes
|
import codes.som.hibiscus.api.command.utils.splitExceptingQuotes
|
||||||
|
import codes.som.hibiscus.util.Resettable
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CommandManager(private val registerDefaultParsers: Boolean = true) {
|
abstract class CommandManager(private val registerDefaultParsers: Boolean = true) : Resettable {
|
||||||
private val parserRegistry = mutableMapOf<Class<*>, ArgumentParser<*>>()
|
private val parserRegistry = mutableMapOf<Class<*>, ArgumentParser<*>>()
|
||||||
val commands = mutableListOf<ExecutableCommand>()
|
private val commands = mutableListOf<ExecutableCommand>()
|
||||||
|
|
||||||
var context: CommandContext = CommandContext.OTHER
|
var context: CommandContext = CommandContext.OTHER
|
||||||
|
|
||||||
|
@ -226,7 +227,7 @@ class CommandManager(private val registerDefaultParsers: Boolean = true) {
|
||||||
branch.aliases.any { it.equals(args[1], ignoreCase = true) }))
|
branch.aliases.any { it.equals(args[1], ignoreCase = true) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reset() {
|
override fun reset() {
|
||||||
commands.clear()
|
commands.clear()
|
||||||
parserRegistry.clear()
|
parserRegistry.clear()
|
||||||
registerDefaultParsersIfApplicable()
|
registerDefaultParsersIfApplicable()
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package codes.som.hibiscus.api.event
|
package codes.som.hibiscus.api.event
|
||||||
|
|
||||||
|
import codes.som.hibiscus.util.Resettable
|
||||||
import java.util.concurrent.CopyOnWriteArrayList
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
class EventBus {
|
class EventBus : Resettable {
|
||||||
private val allListeners = mutableMapOf<EventPhase, MutableMap<Class<*>, MutableList<Listener<*>>>>()
|
private val allListeners = mutableMapOf<EventPhase, MutableMap<Class<*>, MutableList<Listener<*>>>>()
|
||||||
|
|
||||||
inline fun <reified T : Event> register(listener: Listener<T>, phase: EventPhase) {
|
inline fun <reified T : Event> register(listener: Listener<T>, phase: EventPhase) {
|
||||||
|
@ -35,4 +36,8 @@ class EventBus {
|
||||||
listeners.forEach { it.on(event) }
|
listeners.forEach { it.on(event) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun reset() {
|
||||||
|
allListeners.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package codes.som.hibiscus.api.keybinds
|
package codes.som.hibiscus.api.keybinds
|
||||||
|
|
||||||
class KeybindRegistry {
|
import codes.som.hibiscus.util.Resettable
|
||||||
|
|
||||||
|
class KeybindRegistry : Resettable {
|
||||||
private val keybinds = mutableMapOf<Int, MutableList<String>>()
|
private val keybinds = mutableMapOf<Int, MutableList<String>>()
|
||||||
|
|
||||||
fun register(key: Int, command: String) {
|
fun register(key: Int, command: String) {
|
||||||
|
@ -16,7 +18,7 @@ class KeybindRegistry {
|
||||||
fun getBinds(key: Int): List<String> =
|
fun getBinds(key: Int): List<String> =
|
||||||
keybinds.getOrDefault(key, emptyList())
|
keybinds.getOrDefault(key, emptyList())
|
||||||
|
|
||||||
fun reset() {
|
override fun reset() {
|
||||||
keybinds.clear()
|
keybinds.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package codes.som.hibiscus.commands
|
||||||
|
|
||||||
|
import codes.som.hibiscus.api.command.Command
|
||||||
|
import codes.som.hibiscus.api.command.CommandManager
|
||||||
|
|
||||||
|
fun allCommandClasses(): Array<() -> Command> = arrayOf(
|
||||||
|
::Reload,
|
||||||
|
)
|
||||||
|
|
||||||
|
class CommandRegistry : CommandManager() {
|
||||||
|
init {
|
||||||
|
registerCommands()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerCommands() {
|
||||||
|
allCommandClasses().map { it() }.forEach(this::register)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reset() {
|
||||||
|
super.reset()
|
||||||
|
registerCommands()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package codes.som.hibiscus.commands
|
||||||
|
|
||||||
|
import codes.som.hibiscus.HibiscusMod
|
||||||
|
import codes.som.hibiscus.api.command.Command
|
||||||
|
|
||||||
|
class Reload : Command("reload") {
|
||||||
|
init {
|
||||||
|
branch {
|
||||||
|
HibiscusMod.shutdown()
|
||||||
|
HibiscusMod.onInitialize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,13 +3,14 @@ package codes.som.hibiscus.features
|
||||||
import codes.som.hibiscus.api.feature.Feature
|
import codes.som.hibiscus.api.feature.Feature
|
||||||
import codes.som.hibiscus.features.combat.Criticals
|
import codes.som.hibiscus.features.combat.Criticals
|
||||||
import codes.som.hibiscus.features.exploits.AntiGhost
|
import codes.som.hibiscus.features.exploits.AntiGhost
|
||||||
|
import codes.som.hibiscus.features.exploits.Ghost
|
||||||
import codes.som.hibiscus.features.movement.Flight
|
import codes.som.hibiscus.features.movement.Flight
|
||||||
import codes.som.hibiscus.features.movement.Speed
|
import codes.som.hibiscus.features.movement.Speed
|
||||||
import codes.som.hibiscus.features.overlay.Overlay
|
import codes.som.hibiscus.features.overlay.Overlay
|
||||||
import codes.som.hibiscus.features.exploits.Ghost
|
|
||||||
import codes.som.hibiscus.features.player.NoFallDamage
|
import codes.som.hibiscus.features.player.NoFallDamage
|
||||||
|
import codes.som.hibiscus.util.Resettable
|
||||||
|
|
||||||
val ALL_FEATURES: Array<() -> Feature> = arrayOf(
|
fun allFeatureClasses(): Array<() -> Feature> = arrayOf(
|
||||||
::NoFallDamage,
|
::NoFallDamage,
|
||||||
::Flight,
|
::Flight,
|
||||||
::Overlay,
|
::Overlay,
|
||||||
|
@ -19,14 +20,27 @@ val ALL_FEATURES: Array<() -> Feature> = arrayOf(
|
||||||
::AntiGhost,
|
::AntiGhost,
|
||||||
)
|
)
|
||||||
|
|
||||||
class FeaturesRegistry {
|
class FeaturesRegistry : Resettable {
|
||||||
private val features = ALL_FEATURES.map { it() }.sortedBy { it.name }
|
private val features = mutableListOf<Feature>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
registerFeatures()
|
||||||
|
}
|
||||||
|
|
||||||
fun getAllFeatures() = features.asSequence()
|
fun getAllFeatures() = features.asSequence()
|
||||||
|
|
||||||
inline fun <reified T : Feature> getFeature() =
|
inline fun <reified T : Feature> getFeature() =
|
||||||
getFeature(T::class.java)
|
getFeature(T::class.java)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun <T : Feature> getFeature(type: Class<T>) =
|
fun <T : Feature> getFeature(type: Class<T>) =
|
||||||
features.first { it.javaClass == type } as T
|
getAllFeatures().first { it.javaClass == type } as T
|
||||||
|
|
||||||
|
private fun registerFeatures() {
|
||||||
|
features.addAll(allFeatureClasses().map { it() })
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reset() {
|
||||||
|
features.clear()
|
||||||
|
registerFeatures()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package codes.som.hibiscus.util
|
||||||
|
|
||||||
|
interface Resettable {
|
||||||
|
fun reset()
|
||||||
|
}
|
Loading…
Reference in New Issue