mirror of
				https://github.com/GayPizzaSpecifications/foundation.git
				synced 2025-11-04 11:39:39 +00:00 
			
		
		
		
	Major refactoring to use Koin.
This commit is contained in:
		@ -82,6 +82,9 @@ subprojects {
 | 
			
		||||
    implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
 | 
			
		||||
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
 | 
			
		||||
 | 
			
		||||
    implementation("io.insert-koin:koin-core:3.1.4")
 | 
			
		||||
    testImplementation("io.insert-koin:koin-test:3.1.4")
 | 
			
		||||
 | 
			
		||||
    // Serialization
 | 
			
		||||
    implementation("com.charleskorn.kaml:kaml:0.38.0")
 | 
			
		||||
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,5 @@
 | 
			
		||||
dependencies {
 | 
			
		||||
  // TODO: might be able to ship all dependencies in core? are we duplicating classes in JARs?
 | 
			
		||||
 | 
			
		||||
  implementation("software.amazon.awssdk:s3:2.17.102")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,27 +1,27 @@
 | 
			
		||||
package cloud.kubelet.foundation.core
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.command.*
 | 
			
		||||
import cloud.kubelet.foundation.core.devupdate.DevUpdateServer
 | 
			
		||||
import cloud.kubelet.foundation.core.persist.PersistentStore
 | 
			
		||||
import cloud.kubelet.foundation.core.persist.setAllProperties
 | 
			
		||||
import io.papermc.paper.event.player.AsyncChatEvent
 | 
			
		||||
import net.kyori.adventure.text.Component
 | 
			
		||||
import net.kyori.adventure.text.TextComponent
 | 
			
		||||
import org.bukkit.GameMode
 | 
			
		||||
import org.bukkit.command.CommandExecutor
 | 
			
		||||
import org.bukkit.command.TabCompleter
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.bukkit.event.Listener
 | 
			
		||||
import org.bukkit.plugin.java.JavaPlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.FoundationPlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.features.backup.BackupFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.dev.DevFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.player.PlayerFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.stats.StatsFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.update.UpdateFeature
 | 
			
		||||
import cloud.kubelet.foundation.core.features.world.WorldFeature
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
import java.nio.file.Path
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap
 | 
			
		||||
 | 
			
		||||
class FoundationCorePlugin : JavaPlugin(), Listener {
 | 
			
		||||
  internal val persistentStores = ConcurrentHashMap<String, PersistentStore>()
 | 
			
		||||
class FoundationCorePlugin : FoundationPlugin() {
 | 
			
		||||
  private lateinit var _pluginDataPath: Path
 | 
			
		||||
  private lateinit var chatLogStore: PersistentStore
 | 
			
		||||
  private lateinit var devUpdateServer: DevUpdateServer
 | 
			
		||||
  override val features: List<Feature>
 | 
			
		||||
    get() = listOf(
 | 
			
		||||
      BackupFeature(),
 | 
			
		||||
      DevFeature(),
 | 
			
		||||
      PlayerFeature(),
 | 
			
		||||
      StatsFeature(),
 | 
			
		||||
      UpdateFeature(),
 | 
			
		||||
      WorldFeature(),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  var pluginDataPath: Path
 | 
			
		||||
    /**
 | 
			
		||||
@ -38,104 +38,15 @@ class FoundationCorePlugin : JavaPlugin(), Listener {
 | 
			
		||||
      _pluginDataPath = value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fetch a persistent store by name. Make sure the name is path-safe, descriptive and consistent across server runs.
 | 
			
		||||
   */
 | 
			
		||||
  fun getPersistentStore(name: String) = persistentStores.getOrPut(name) { PersistentStore(this, name) }
 | 
			
		||||
  override fun module() = module {
 | 
			
		||||
    single { this@FoundationCorePlugin }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun onEnable() {
 | 
			
		||||
    // Create core plugin directory.
 | 
			
		||||
    pluginDataPath = dataFolder.toPath()
 | 
			
		||||
    val backupPath = pluginDataPath.resolve(BACKUPS_DIRECTORY)
 | 
			
		||||
 | 
			
		||||
    // Create Foundation plugin directories.
 | 
			
		||||
    pluginDataPath.toFile().mkdir()
 | 
			
		||||
    backupPath.toFile().mkdir()
 | 
			
		||||
 | 
			
		||||
    // Register this as an event listener.
 | 
			
		||||
    server.pluginManager.registerEvents(this, this)
 | 
			
		||||
 | 
			
		||||
    // Register commands.
 | 
			
		||||
    registerCommandExecutor("fbackup", BackupCommand(this, backupPath))
 | 
			
		||||
    registerCommandExecutor("fupdate", UpdateCommand())
 | 
			
		||||
    registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL))
 | 
			
		||||
    registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE))
 | 
			
		||||
    registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
 | 
			
		||||
    registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
 | 
			
		||||
    registerCommandExecutor(listOf("leaderboard", "lb"), LeaderboardCommand())
 | 
			
		||||
    registerCommandExecutor("pstore", PersistentStoreCommand(this))
 | 
			
		||||
    registerCommandExecutor("setspawn", SetSpawnCommand())
 | 
			
		||||
    registerCommandExecutor("spawn", SpawnCommand())
 | 
			
		||||
 | 
			
		||||
    val log = slF4JLogger
 | 
			
		||||
    log.info("Features:")
 | 
			
		||||
    Util.printFeatureStatus(log, "Backup", BACKUP_ENABLED)
 | 
			
		||||
    chatLogStore = getPersistentStore("chat-logs")
 | 
			
		||||
    devUpdateServer = DevUpdateServer(this)
 | 
			
		||||
    devUpdateServer.enable()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun onDisable() {
 | 
			
		||||
    persistentStores.values.forEach { store -> store.close() }
 | 
			
		||||
    persistentStores.clear()
 | 
			
		||||
    devUpdateServer.disable()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun registerCommandExecutor(name: String, executor: CommandExecutor) {
 | 
			
		||||
    registerCommandExecutor(listOf(name), executor)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private fun registerCommandExecutor(names: List<String>, executor: CommandExecutor) {
 | 
			
		||||
    for (name in names) {
 | 
			
		||||
      val command = getCommand(name) ?: throw Exception("Failed to get $name command")
 | 
			
		||||
      command.setExecutor(executor)
 | 
			
		||||
      if (executor is TabCompleter) {
 | 
			
		||||
        command.tabCompleter = executor
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // TODO: Disabling chat reformatting until I do something with it and figure out how to make it
 | 
			
		||||
  //  be less disruptive.
 | 
			
		||||
  /*@EventHandler
 | 
			
		||||
  private fun onChatMessage(e: ChatEvent) {
 | 
			
		||||
    return
 | 
			
		||||
    e.isCancelled = true
 | 
			
		||||
    val name = e.player.displayName()
 | 
			
		||||
    val component = Component.empty()
 | 
			
		||||
      .append(leftBracket)
 | 
			
		||||
      .append(name)
 | 
			
		||||
      .append(rightBracket)
 | 
			
		||||
      .append(Component.text(' '))
 | 
			
		||||
      .append(e.message())
 | 
			
		||||
    server.sendMessage(component)
 | 
			
		||||
  }*/
 | 
			
		||||
 | 
			
		||||
  @EventHandler
 | 
			
		||||
  private fun logOnChatMessage(e: AsyncChatEvent) {
 | 
			
		||||
    val player = e.player
 | 
			
		||||
    val message = e.message()
 | 
			
		||||
 | 
			
		||||
    if (message !is TextComponent) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val content = message.content()
 | 
			
		||||
    chatLogStore.create("ChatMessageEvent") {
 | 
			
		||||
      setAllProperties(
 | 
			
		||||
        "timestamp" to Instant.now().toEpochMilli(),
 | 
			
		||||
        "player.id" to player.identity().uuid().toString(),
 | 
			
		||||
        "player.name" to player.name,
 | 
			
		||||
        "message.content" to content
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    private const val BACKUPS_DIRECTORY = "backups"
 | 
			
		||||
 | 
			
		||||
    private val leftBracket: Component = Component.text('[')
 | 
			
		||||
    private val rightBracket: Component = Component.text(']')
 | 
			
		||||
 | 
			
		||||
    const val BACKUP_ENABLED = true
 | 
			
		||||
    super.onEnable()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,10 +11,6 @@ object Util {
 | 
			
		||||
  private val whitespace: Component = Component.text(' ')
 | 
			
		||||
  private val foundationName: Component = Component.text("Foundation")
 | 
			
		||||
 | 
			
		||||
  fun printFeatureStatus(logger: Logger, feature: String?, state: Boolean) {
 | 
			
		||||
    logger.info("{}: {}", feature, if (state) "Enabled" else "Disabled")
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fun formatSystemMessage(message: String): Component {
 | 
			
		||||
    return formatSystemMessage(TextColors.AMARANTH_PINK, message)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,31 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.abstraction
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import org.bukkit.command.CommandExecutor
 | 
			
		||||
import org.bukkit.command.TabCompleter
 | 
			
		||||
import org.bukkit.event.Listener
 | 
			
		||||
import org.koin.core.component.KoinComponent
 | 
			
		||||
import org.koin.core.component.inject
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
 | 
			
		||||
abstract class Feature : KoinComponent, Listener {
 | 
			
		||||
  private val plugin by inject<FoundationCorePlugin>()
 | 
			
		||||
 | 
			
		||||
  open fun enable() {}
 | 
			
		||||
  open fun disable() {}
 | 
			
		||||
  open fun module() = module {}
 | 
			
		||||
 | 
			
		||||
  protected fun registerCommandExecutor(name: String, executor: CommandExecutor) {
 | 
			
		||||
    registerCommandExecutor(listOf(name), executor)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected fun registerCommandExecutor(names: List<String>, executor: CommandExecutor) {
 | 
			
		||||
    for (name in names) {
 | 
			
		||||
      val command = plugin.getCommand(name) ?: throw Exception("Failed to get $name command")
 | 
			
		||||
      command.setExecutor(executor)
 | 
			
		||||
      if (executor is TabCompleter) {
 | 
			
		||||
        command.tabCompleter = executor
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,54 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.abstraction
 | 
			
		||||
 | 
			
		||||
import org.bukkit.plugin.java.JavaPlugin
 | 
			
		||||
import org.koin.core.KoinApplication
 | 
			
		||||
import org.koin.core.context.startKoin
 | 
			
		||||
import org.koin.core.context.stopKoin
 | 
			
		||||
import org.koin.core.module.Module
 | 
			
		||||
import org.koin.dsl.module
 | 
			
		||||
 | 
			
		||||
abstract class FoundationPlugin : JavaPlugin() {
 | 
			
		||||
  private lateinit var pluginModule: Module
 | 
			
		||||
  private lateinit var pluginApplication: KoinApplication
 | 
			
		||||
  protected abstract val features: List<Feature>
 | 
			
		||||
 | 
			
		||||
  override fun onEnable() {
 | 
			
		||||
    pluginModule = module {
 | 
			
		||||
      single { this@FoundationPlugin }
 | 
			
		||||
      single { server }
 | 
			
		||||
      single { config }
 | 
			
		||||
      single { slF4JLogger }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: If we have another plugin using this class, we may need to use context isolation.
 | 
			
		||||
    //  https://insert-koin.io/docs/reference/koin-core/context-isolation
 | 
			
		||||
    pluginApplication = startKoin {
 | 
			
		||||
      modules(pluginModule)
 | 
			
		||||
      modules(module())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    features.forEach {
 | 
			
		||||
      pluginApplication.modules(it.module())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    features.forEach {
 | 
			
		||||
      try {
 | 
			
		||||
        slF4JLogger.info("Enabling feature: ${it.javaClass.simpleName}")
 | 
			
		||||
        it.enable()
 | 
			
		||||
        server.pluginManager.registerEvents(it, this)
 | 
			
		||||
      } catch (e: Exception) {
 | 
			
		||||
        slF4JLogger.error("Failed to enable feature: ${it.javaClass.simpleName}", e)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun onDisable() {
 | 
			
		||||
    features.forEach {
 | 
			
		||||
      it.disable()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stopKoin()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected open fun module() = module {}
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.command
 | 
			
		||||
package cloud.kubelet.foundation.core.features.backup
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.Util
 | 
			
		||||
@ -26,15 +26,6 @@ class BackupCommand(
 | 
			
		||||
  override fun onCommand(
 | 
			
		||||
    sender: CommandSender, command: Command, label: String, args: Array<String>
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    if (!FoundationCorePlugin.BACKUP_ENABLED) {
 | 
			
		||||
      sender.sendMessage(
 | 
			
		||||
        Component
 | 
			
		||||
          .text("Backup is not enabled.")
 | 
			
		||||
          .color(TextColor.fromHexString("#FF0000"))
 | 
			
		||||
      )
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (RUNNING.get()) {
 | 
			
		||||
      sender.sendMessage(
 | 
			
		||||
        Component
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.backup
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import org.koin.core.component.inject
 | 
			
		||||
 | 
			
		||||
class BackupFeature : Feature() {
 | 
			
		||||
  private val plugin by inject<FoundationCorePlugin>()
 | 
			
		||||
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    // Create backup directory.
 | 
			
		||||
    val backupPath = plugin.pluginDataPath.resolve(BACKUPS_DIRECTORY)
 | 
			
		||||
    backupPath.toFile().mkdir()
 | 
			
		||||
 | 
			
		||||
    registerCommandExecutor("fbackup", BackupCommand(plugin, backupPath))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  companion object {
 | 
			
		||||
    private const val BACKUPS_DIRECTORY = "backups"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,20 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.dev
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import cloud.kubelet.foundation.core.devupdate.DevUpdateServer
 | 
			
		||||
import org.koin.core.component.inject
 | 
			
		||||
 | 
			
		||||
class DevFeature : Feature() {
 | 
			
		||||
  private val plugin = inject<FoundationCorePlugin>()
 | 
			
		||||
  private lateinit var devUpdateServer: DevUpdateServer
 | 
			
		||||
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    devUpdateServer = DevUpdateServer(plugin.value)
 | 
			
		||||
    devUpdateServer.enable()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun disable() {
 | 
			
		||||
    devUpdateServer.disable()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,14 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.player
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import cloud.kubelet.foundation.core.command.GamemodeCommand
 | 
			
		||||
import org.bukkit.GameMode
 | 
			
		||||
 | 
			
		||||
class PlayerFeature : Feature() {
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    registerCommandExecutor(listOf("survival", "s"), GamemodeCommand(GameMode.SURVIVAL))
 | 
			
		||||
    registerCommandExecutor(listOf("creative", "c"), GamemodeCommand(GameMode.CREATIVE))
 | 
			
		||||
    registerCommandExecutor(listOf("adventure", "a"), GamemodeCommand(GameMode.ADVENTURE))
 | 
			
		||||
    registerCommandExecutor(listOf("spectator", "sp"), GamemodeCommand(GameMode.SPECTATOR))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.command
 | 
			
		||||
package cloud.kubelet.foundation.core.features.stats
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.SortOrder
 | 
			
		||||
import cloud.kubelet.foundation.core.allPlayerStatisticsOf
 | 
			
		||||
@ -1,15 +1,21 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.command
 | 
			
		||||
package cloud.kubelet.foundation.core.features.stats
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import org.bukkit.command.Command
 | 
			
		||||
import org.bukkit.command.CommandExecutor
 | 
			
		||||
import org.bukkit.command.CommandSender
 | 
			
		||||
import org.bukkit.command.TabCompleter
 | 
			
		||||
 | 
			
		||||
class PersistentStoreCommand(private val plugin: FoundationCorePlugin) : CommandExecutor, TabCompleter {
 | 
			
		||||
class PersistentStoreCommand(
 | 
			
		||||
  private val statsFeature: StatsFeature
 | 
			
		||||
) : CommandExecutor, TabCompleter {
 | 
			
		||||
  private val allSubCommands = mutableListOf("stats", "sample", "delete-all-entities")
 | 
			
		||||
 | 
			
		||||
  override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
 | 
			
		||||
  override fun onCommand(
 | 
			
		||||
    sender: CommandSender,
 | 
			
		||||
    command: Command,
 | 
			
		||||
    label: String,
 | 
			
		||||
    args: Array<out String>
 | 
			
		||||
  ): Boolean {
 | 
			
		||||
    if (args.isEmpty()) {
 | 
			
		||||
      sender.sendMessage("Invalid Command Usage.")
 | 
			
		||||
      return true
 | 
			
		||||
@ -17,7 +23,7 @@ class PersistentStoreCommand(private val plugin: FoundationCorePlugin) : Command
 | 
			
		||||
 | 
			
		||||
    when (args[0]) {
 | 
			
		||||
      "stats" -> {
 | 
			
		||||
        plugin.persistentStores.forEach { (name, store) ->
 | 
			
		||||
        statsFeature.persistentStores.forEach { (name, store) ->
 | 
			
		||||
          val counts = store.transact {
 | 
			
		||||
            entityTypes.associateWith { type -> getAll(type).size() }.toSortedMap()
 | 
			
		||||
          }
 | 
			
		||||
@ -36,7 +42,7 @@ class PersistentStoreCommand(private val plugin: FoundationCorePlugin) : Command
 | 
			
		||||
 | 
			
		||||
        val storeName = args[1]
 | 
			
		||||
        val entityTypeName = args[2]
 | 
			
		||||
        val store = plugin.getPersistentStore(storeName)
 | 
			
		||||
        val store = statsFeature.getPersistentStore(storeName)
 | 
			
		||||
        store.transact {
 | 
			
		||||
          val entities = getAll(entityTypeName).take(3)
 | 
			
		||||
          for (entity in entities) {
 | 
			
		||||
@ -55,7 +61,7 @@ class PersistentStoreCommand(private val plugin: FoundationCorePlugin) : Command
 | 
			
		||||
 | 
			
		||||
        val storeName = args[1]
 | 
			
		||||
        val entityTypeName = args[2]
 | 
			
		||||
        val store = plugin.getPersistentStore(storeName)
 | 
			
		||||
        val store = statsFeature.getPersistentStore(storeName)
 | 
			
		||||
        store.transact {
 | 
			
		||||
          store.deleteAllEntities(entityTypeName)
 | 
			
		||||
        }
 | 
			
		||||
@ -0,0 +1,56 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.stats
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.FoundationCorePlugin
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import cloud.kubelet.foundation.core.persist.PersistentStore
 | 
			
		||||
import cloud.kubelet.foundation.core.persist.setAllProperties
 | 
			
		||||
import io.papermc.paper.event.player.AsyncChatEvent
 | 
			
		||||
import net.kyori.adventure.text.TextComponent
 | 
			
		||||
import org.bukkit.event.EventHandler
 | 
			
		||||
import org.koin.core.component.inject
 | 
			
		||||
import java.time.Instant
 | 
			
		||||
import java.util.concurrent.ConcurrentHashMap
 | 
			
		||||
 | 
			
		||||
class StatsFeature : Feature() {
 | 
			
		||||
  private val plugin = inject<FoundationCorePlugin>()
 | 
			
		||||
  private lateinit var chatLogStore: PersistentStore
 | 
			
		||||
  // TODO: Move persistence stuff to its own module.
 | 
			
		||||
  internal val persistentStores = ConcurrentHashMap<String, PersistentStore>()
 | 
			
		||||
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    chatLogStore = getPersistentStore("chat-logs")
 | 
			
		||||
 | 
			
		||||
    registerCommandExecutor("pstore", PersistentStoreCommand(this))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override fun disable() {
 | 
			
		||||
    persistentStores.values.forEach { store -> store.close() }
 | 
			
		||||
    persistentStores.clear()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fetch a persistent store by name. Make sure the name is path-safe, descriptive and consistent across server runs.
 | 
			
		||||
   */
 | 
			
		||||
  fun getPersistentStore(name: String) =
 | 
			
		||||
    persistentStores.getOrPut(name) { PersistentStore(plugin.value, name) }
 | 
			
		||||
 | 
			
		||||
  @EventHandler
 | 
			
		||||
  private fun logOnChatMessage(e: AsyncChatEvent) {
 | 
			
		||||
    val player = e.player
 | 
			
		||||
    val message = e.message()
 | 
			
		||||
 | 
			
		||||
    if (message !is TextComponent) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val content = message.content()
 | 
			
		||||
    chatLogStore.create("ChatMessageEvent") {
 | 
			
		||||
      setAllProperties(
 | 
			
		||||
        "timestamp" to Instant.now().toEpochMilli(),
 | 
			
		||||
        "player.id" to player.identity().uuid().toString(),
 | 
			
		||||
        "player.name" to player.name,
 | 
			
		||||
        "message.content" to content
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.update
 | 
			
		||||
package cloud.kubelet.foundation.core.features.update
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,5 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.command
 | 
			
		||||
package cloud.kubelet.foundation.core.features.update
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.service.UpdateService
 | 
			
		||||
import org.bukkit.command.Command
 | 
			
		||||
import org.bukkit.command.CommandExecutor
 | 
			
		||||
import org.bukkit.command.CommandSender
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.update
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
 | 
			
		||||
class UpdateFeature : Feature() {
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    registerCommandExecutor("fupdate", UpdateCommand())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,11 +1,10 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.service
 | 
			
		||||
package cloud.kubelet.foundation.core.features.update
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.update.UpdateUtil
 | 
			
		||||
import org.bukkit.command.CommandSender
 | 
			
		||||
import kotlin.io.path.name
 | 
			
		||||
import kotlin.io.path.toPath
 | 
			
		||||
 | 
			
		||||
// TODO: Switch to classes and use dependency injection with koin.
 | 
			
		||||
// TODO: Switch to a class and use dependency injection with koin.
 | 
			
		||||
object UpdateService {
 | 
			
		||||
  fun updatePlugins(sender: CommandSender, onFinish: (() -> Unit)? = null) {
 | 
			
		||||
    val updateDir = sender.server.pluginsFolder.resolve("update")
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.update
 | 
			
		||||
package cloud.kubelet.foundation.core.features.update
 | 
			
		||||
 | 
			
		||||
import kotlinx.serialization.DeserializationStrategy
 | 
			
		||||
import kotlinx.serialization.builtins.MapSerializer
 | 
			
		||||
@ -0,0 +1,14 @@
 | 
			
		||||
package cloud.kubelet.foundation.core.features.world
 | 
			
		||||
 | 
			
		||||
import cloud.kubelet.foundation.core.abstraction.Feature
 | 
			
		||||
import cloud.kubelet.foundation.core.command.GamemodeCommand
 | 
			
		||||
import cloud.kubelet.foundation.core.command.SetSpawnCommand
 | 
			
		||||
import cloud.kubelet.foundation.core.command.SpawnCommand
 | 
			
		||||
import org.bukkit.GameMode
 | 
			
		||||
 | 
			
		||||
class WorldFeature : Feature() {
 | 
			
		||||
  override fun enable() {
 | 
			
		||||
    registerCommandExecutor("setspawn", SetSpawnCommand())
 | 
			
		||||
    registerCommandExecutor("spawn", SpawnCommand())
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user