From 7c91d476a80a7b67c4de14396f897070c9cbb4f8 Mon Sep 17 00:00:00 2001 From: jmp Date: Thu, 25 Feb 2021 15:57:02 -0800 Subject: [PATCH] Reorganize command permissions, add license --- .editorconfig | 8 + build.gradle.kts | 119 ++++--- license.txt | 21 ++ license_header.txt | 21 ++ settings.gradle.kts | 1 + .../jpenilla/announcerplus/AnnouncerPlus.kt | 155 +++++---- .../announcerplus/JoinQuitListener.kt | 97 ++++-- .../announcerplus/command/ArgumentFactory.kt | 148 ++++---- .../announcerplus/command/BaseCommand.kt | 25 +- .../command/CommandAnnouncerPlus.kt | 326 ++++++++++-------- .../announcerplus/command/CommandBroadcast.kt | 188 +++++----- .../announcerplus/command/CommandManager.kt | 149 +++++--- .../announcerplus/command/CommandParse.kt | 206 ++++++----- .../announcerplus/command/CommandSend.kt | 188 +++++----- .../compatability/EssentialsHook.kt | 22 -- .../compatibility/EssentialsHook.kt | 45 +++ .../announcerplus/config/ConfigManager.kt | 305 ++++++++-------- .../announcerplus/config/JoinQuitConfig.kt | 320 +++++++++-------- .../announcerplus/config/MainConfig.kt | 150 ++++---- .../config/message/ActionBarSettings.kt | 59 +++- .../config/message/BossBarSettings.kt | 71 ++-- .../announcerplus/config/message/Message.kt | 93 +++-- .../config/message/MessageConfig.kt | 271 +++++++++------ .../config/message/MessageElement.kt | 35 +- .../config/message/TitleSettings.kt | 89 +++-- .../config/message/ToastSettings.kt | 174 ++++++---- .../announcerplus/task/ActionBarUpdateTask.kt | 59 +++- .../announcerplus/task/BossBarUpdateTask.kt | 91 +++-- .../announcerplus/task/TitleUpdateTask.kt | 95 +++-- .../jpenilla/announcerplus/task/ToastTask.kt | 318 +++++++++-------- .../jpenilla/announcerplus/task/UpdateTask.kt | 75 ++-- .../textanimation/AnimationHolder.kt | 221 ++++++------ .../textanimation/TextAnimation.kt | 27 +- .../textanimation/animation/FlashingText.kt | 55 ++- .../textanimation/animation/PulsingColor.kt | 95 +++-- .../textanimation/animation/RandomColor.kt | 87 +++-- .../animation/ScrollingGradient.kt | 49 ++- .../textanimation/animation/ScrollingText.kt | 87 +++-- .../textanimation/animation/Typewriter.kt | 73 ++-- .../jpenilla/announcerplus/util/Constants.kt | 71 ++-- .../jpenilla/announcerplus/util/Functions.kt | 48 ++- .../announcerplus/util/UpdateChecker.kt | 79 +++-- 42 files changed, 2900 insertions(+), 1916 deletions(-) create mode 100644 .editorconfig create mode 100644 license.txt create mode 100644 license_header.txt create mode 100644 settings.gradle.kts delete mode 100644 src/main/kotlin/xyz/jpenilla/announcerplus/compatability/EssentialsHook.kt create mode 100644 src/main/kotlin/xyz/jpenilla/announcerplus/compatibility/EssentialsHook.kt diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..76e671c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = off diff --git a/build.gradle.kts b/build.gradle.kts index fc1df77..b670d8f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,80 +2,77 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocatio import org.apache.commons.io.output.ByteArrayOutputStream plugins { - kotlin("jvm") version "1.4.21" - id("com.github.johnrengelman.shadow") version "6.1.0" - id("kr.entree.spigradle") version "2.2.3" + kotlin("jvm") version "1.4.30" + id("com.github.johnrengelman.shadow") version "6.1.0" + id("kr.entree.spigradle") version "2.2.3" + id("net.kyori.indra.license-header") version "1.3.1" } -val projectName = "AnnouncerPlus" group = "xyz.jpenilla" -version = "1.2.2.2+${getLastCommitHash()}-SNAPSHOT" +version = "1.2.3.1+${getLastCommitHash()}-SNAPSHOT" +description = "Announcement plugin with support for permissions. Supports Hex colors and clickable messages/hover text using MiniMessage." repositories { - mavenLocal() - mavenCentral() - jcenter() - maven(url = "https://hub.spigotmc.org/nexus/content/repositories/snapshots/") - maven(url = "https://oss.sonatype.org/content/groups/public/") - maven(url = "https://repo.spongepowered.org/maven") - maven(url = "https://repo.jpenilla.xyz/snapshots") - maven(url = "https://ci.ender.zone/plugin/repository/everything/") - maven(url = "https://repo.codemc.org/repository/maven-public") - maven(url = "https://jitpack.io") + mavenLocal() + mavenCentral() + jcenter() + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://oss.sonatype.org/content/groups/public/") + maven("https://repo.spongepowered.org/maven") + maven("https://repo.jpenilla.xyz/snapshots") + maven("https://ci.ender.zone/plugin/repository/everything/") + maven("https://repo.codemc.org/repository/maven-public") + maven("https://jitpack.io") } dependencies { - compileOnly("org.spigotmc", "spigot-api", "1.13.2-R0.1-SNAPSHOT") - compileOnly("com.github.MilkBowl", "VaultAPI", "1.7") - compileOnly("net.ess3", "EssentialsX", "2.17.2") + compileOnly("org.spigotmc", "spigot-api", "1.13.2-R0.1-SNAPSHOT") + compileOnly("com.github.MilkBowl", "VaultAPI", "1.7") + compileOnly("net.ess3", "EssentialsX", "2.17.2") - implementation("org.koin", "koin-core", "2.1.6") - implementation("xyz.jpenilla", "jmplib", "1.0.1+27-SNAPSHOT") - implementation("org.spongepowered", "configurate-hocon", "4.0.0") - implementation("org.bstats", "bstats-bukkit", "1.8") + implementation("org.koin", "koin-core", "2.1.6") + implementation("net.kyori", "adventure-api", "4.5.1") + implementation("xyz.jpenilla", "jmplib", "1.0.1+30-SNAPSHOT") + implementation("org.spongepowered", "configurate-hocon", "4.1.0-SNAPSHOT") + implementation("org.bstats", "bstats-bukkit", "2.2.1") - val cloudVersion = "1.3.0" - implementation("cloud.commandframework", "cloud-paper", cloudVersion) - implementation("cloud.commandframework", "cloud-kotlin-extensions", cloudVersion) - implementation("cloud.commandframework", "cloud-minecraft-extras", cloudVersion) -} - -spigot { - name = projectName - apiVersion = "1.13" - description = "Announcement plugin with support for permissions. Supports Hex colors and clickable messages/hover text using MiniMessage." - website = "https://github.com/jmanpenilla/AnnouncerPlus" - authors("jmp") - depends("Vault") - softDepends("PlaceholderAPI", "Prisma", "Essentials") -} - -val autoRelocate by tasks.register("configureShadowRelocation", ConfigureShadowRelocation::class) { - target = tasks.shadowJar.get() - val packageName = "${project.group}.${project.name.toLowerCase()}" - prefix = "$packageName.shaded" + val cloudVersion = "1.4.0" + implementation("cloud.commandframework", "cloud-paper", cloudVersion) + implementation("cloud.commandframework", "cloud-kotlin-extensions", cloudVersion) + implementation("cloud.commandframework", "cloud-minecraft-extras", cloudVersion) } tasks { - build { - dependsOn(shadowJar) - } - compileKotlin { - kotlinOptions.jvmTarget = "1.8" - } - shadowJar { - minimize() - dependsOn(autoRelocate) - archiveClassifier.set("") - archiveFileName.set("$projectName-${project.version}.jar") - } + compileKotlin { + kotlinOptions.jvmTarget = "1.8" + } + register("autoRelocate") { + target = shadowJar.get() + val packageName = "${project.group}.${project.name.toLowerCase()}" + prefix = "$packageName.lib" + } + shadowJar { + dependsOn(withType()) + minimize() + archiveClassifier.set("") + archiveFileName.set("${project.name}-${project.version}.jar") + } + build { + dependsOn(shadowJar) + } } -fun getLastCommitHash(): String { - val byteOut = ByteArrayOutputStream() - exec { - commandLine = listOf("git", "rev-parse", "--short", "HEAD") - standardOutput = byteOut - } - return byteOut.toString(Charsets.UTF_8).trim() +spigot { + apiVersion = "1.13" + website = "https://github.com/jmanpenilla/AnnouncerPlus" + authors("jmp") + depends("Vault") + softDepends("PlaceholderAPI", "Prisma", "Essentials") } + +fun getLastCommitHash(): String = ByteArrayOutputStream().apply { + exec { + commandLine = listOf("git", "rev-parse", "--short", "HEAD") + standardOutput = this@apply + } +}.toString(Charsets.UTF_8).trim() diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..40651ff --- /dev/null +++ b/license.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2021 Jason Penilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/license_header.txt b/license_header.txt new file mode 100644 index 0000000..d242208 --- /dev/null +++ b/license_header.txt @@ -0,0 +1,21 @@ +This file is part of AnnouncerPlus, licensed under the MIT License. + +Copyright (c) 2020-2021 Jason Penilla + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..df2c12f --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "AnnouncerPlus" diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/AnnouncerPlus.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/AnnouncerPlus.kt index 826f84d..ca22547 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/AnnouncerPlus.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/AnnouncerPlus.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus import com.google.gson.Gson @@ -7,92 +30,94 @@ import kr.entree.spigradle.annotations.PluginMain import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer import net.milkbowl.vault.permission.Permission import org.bstats.bukkit.Metrics +import org.bstats.charts.SimplePie import org.koin.core.KoinComponent import org.koin.core.context.startKoin import org.koin.core.inject import org.koin.dsl.module import xyz.jpenilla.announcerplus.command.CommandManager -import xyz.jpenilla.announcerplus.compatability.EssentialsHook +import xyz.jpenilla.announcerplus.compatibility.EssentialsHook import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.announcerplus.config.message.MessageConfig import xyz.jpenilla.announcerplus.task.ToastTask import xyz.jpenilla.announcerplus.util.UpdateChecker import xyz.jpenilla.jmplib.BasePlugin +import xyz.jpenilla.jmplib.Environment @PluginMain class AnnouncerPlus : BasePlugin(), KoinComponent { - val gson: Gson = GsonBuilder().create() - val jsonParser = JsonParser() - val gsonComponentSerializer = GsonComponentSerializer.gson() - val downsamplingGsonComponentSerializer = GsonComponentSerializer.colorDownsamplingGson() - val configManager: ConfigManager by inject() + val gson: Gson = GsonBuilder().create() + val jsonParser = JsonParser() + val gsonComponentSerializer = GsonComponentSerializer.gson() + val downsamplingGsonComponentSerializer = GsonComponentSerializer.colorDownsamplingGson() + val configManager: ConfigManager by inject() - var perms: Permission? = null - var essentials: EssentialsHook? = null - var toastTask: ToastTask? = null - private lateinit var commandManager: CommandManager + var perms: Permission? = null + var essentials: EssentialsHook? = null + var toastTask: ToastTask? = null + private lateinit var commandManager: CommandManager - override fun onPluginEnable() { - if (!setupPermissions()) { - logger.warning("Permissions plugin not found. AnnouncerPlus will not work.") - isEnabled = false - return - } - if (server.pluginManager.isPluginEnabled("Essentials")) { - essentials = EssentialsHook() - } - - startKoin { - modules(module { - single { this@AnnouncerPlus } - single { audience } - single { miniMessage } - single { chat } - single { ConfigManager(get()) } - single { gson } - single { jsonParser } - }) - } - - if (majorMinecraftVersion > 11) { - toastTask = ToastTask() - } else { - logger.info("Sorry, but Toast/Achievement style messages do not work on this version. Update to 1.12 or newer to use this feature.") - } - commandManager = CommandManager(this) - - server.pluginManager.registerEvents(JoinQuitListener(), this) - broadcast() - - UpdateChecker(this, "jmanpenilla/AnnouncerPlus").updateCheck() - - val metrics = Metrics(this, 8067) - metrics.addCustomChart(Metrics.SimplePie("join_quit_configs", configManager.joinQuitConfigs.size::toString)) - metrics.addCustomChart(Metrics.SimplePie("message_configs", configManager.messageConfigs.size::toString)) + override fun onPluginEnable() { + if (!setupPermissions()) { + logger.warning("Permissions plugin not found. AnnouncerPlus will not work.") + isEnabled = false + return + } + if (server.pluginManager.isPluginEnabled("Essentials")) { + essentials = EssentialsHook() } - private fun broadcast() { - if (configManager.mainConfig.enableBroadcasts) configManager.messageConfigs.values.forEach(MessageConfig::broadcast) + startKoin { + modules(module { + single { this@AnnouncerPlus } + single { audiences() } + single { miniMessage() } + single { chat() } + single { ConfigManager(get()) } + single { gson } + single { jsonParser } + }) } - fun reload() { - if (toastTask != null) { - toastTask?.cancel() - toastTask = ToastTask() - } - configManager.reload() - broadcast() + if (Environment.majorMinecraftVersion() > 11) { + toastTask = ToastTask() + } else { + logger.info("Sorry, but Toast/Achievement style messages do not work on this version. Update to 1.12 or newer to use this feature.") } + commandManager = CommandManager(this) - override fun onDisable() { - toastTask?.cancel() - } + server.pluginManager.registerEvents(JoinQuitListener(), this) + broadcast() - private fun setupPermissions(): Boolean { - val rsp = server.servicesManager.getRegistration(Permission::class.java) - if (rsp != null) { - perms = rsp.provider - } - return perms != null + UpdateChecker(this, "jmanpenilla/AnnouncerPlus").updateCheck() + + val metrics = Metrics(this, 8067) + metrics.addCustomChart(SimplePie("join_quit_configs", configManager.joinQuitConfigs.size::toString)) + metrics.addCustomChart(SimplePie("message_configs", configManager.messageConfigs.size::toString)) + } + + private fun broadcast() { + if (configManager.mainConfig.enableBroadcasts) configManager.messageConfigs.values.forEach(MessageConfig::broadcast) + } + + fun reload() { + if (toastTask != null) { + toastTask?.cancel() + toastTask = ToastTask() } + configManager.reload() + broadcast() + } + + override fun onDisable() { + toastTask?.cancel() + } + + private fun setupPermissions(): Boolean { + val rsp = server.servicesManager.getRegistration(Permission::class.java) + if (rsp != null) { + perms = rsp.provider + } + return perms != null + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/JoinQuitListener.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/JoinQuitListener.kt index 79cf5f1..112a706 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/JoinQuitListener.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/JoinQuitListener.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus import org.bukkit.event.EventHandler @@ -11,47 +34,47 @@ import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.jmplib.RandomCollection class JoinQuitListener : Listener, KoinComponent { - private val configManager: ConfigManager by inject() + private val configManager: ConfigManager by inject() - @EventHandler(priority = EventPriority.LOWEST) - fun onJoin(event: PlayerJoinEvent) { - if (configManager.mainConfig.joinFeatures) { - event.joinMessage = "" - if (configManager.mainConfig.firstJoinConfigEnabled && !event.player.hasPlayedBefore()) { - configManager.firstJoinConfig.onJoin(event.player) - return - } - for (entry in configManager.mainConfig.randomJoinConfigs.entries) { - if (entry.key != "demo" && event.player.hasPermission("announcerplus.randomjoin.${entry.key}")) { - val weights = RandomCollection() - for (pair in entry.value) { - weights.add(pair.weight, pair.configName) - } - configManager.joinQuitConfigs[weights.next()]?.onJoin(event.player) - } - } - for (config in configManager.joinQuitConfigs.values) { - config.onJoin(event.player) - } + @EventHandler(priority = EventPriority.LOWEST) + fun onJoin(event: PlayerJoinEvent) { + if (configManager.mainConfig.joinFeatures) { + event.joinMessage = "" + if (configManager.mainConfig.firstJoinConfigEnabled && !event.player.hasPlayedBefore()) { + configManager.firstJoinConfig.onJoin(event.player) + return + } + for (entry in configManager.mainConfig.randomJoinConfigs.entries) { + if (entry.key != "demo" && event.player.hasPermission("announcerplus.randomjoin.${entry.key}")) { + val weights = RandomCollection() + for (pair in entry.value) { + weights.add(pair.weight, pair.configName) + } + configManager.joinQuitConfigs[weights.next()]?.onJoin(event.player) } + } + for (config in configManager.joinQuitConfigs.values) { + config.onJoin(event.player) + } } + } - @EventHandler(priority = EventPriority.LOWEST) - fun onQuit(event: PlayerQuitEvent) { - if (configManager.mainConfig.quitFeatures) { - event.quitMessage = "" - for (entry in configManager.mainConfig.randomQuitConfigs.entries) { - if (entry.key != "demo" && event.player.hasPermission("announcerplus.randomquit.${entry.key}")) { - val weights = RandomCollection() - for (pair in entry.value) { - weights.add(pair.weight, pair.configName) - } - configManager.joinQuitConfigs[weights.next()]?.onQuit(event.player) - } - } - for (config in configManager.joinQuitConfigs.values) { - config.onQuit(event.player) - } + @EventHandler(priority = EventPriority.LOWEST) + fun onQuit(event: PlayerQuitEvent) { + if (configManager.mainConfig.quitFeatures) { + event.quitMessage = "" + for (entry in configManager.mainConfig.randomQuitConfigs.entries) { + if (entry.key != "demo" && event.player.hasPermission("announcerplus.randomquit.${entry.key}")) { + val weights = RandomCollection() + for (pair in entry.value) { + weights.add(pair.weight, pair.configName) + } + configManager.joinQuitConfigs[weights.next()]?.onQuit(event.player) } + } + for (config in configManager.joinQuitConfigs.values) { + config.onQuit(event.player) + } } + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/ArgumentFactory.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/ArgumentFactory.kt index 6893cf2..8190a8a 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/ArgumentFactory.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/ArgumentFactory.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command import cloud.commandframework.CommandHelpHandler @@ -15,69 +38,74 @@ import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.announcerplus.config.message.MessageConfig class ArgumentFactory : KoinComponent { - private val commandManager: CommandManager by inject() - private val configManager: ConfigManager by inject() + private val commandManager: CommandManager by inject() + private val configManager: ConfigManager by inject() - fun helpQuery(name: String): CommandArgument { - return StringArgument.newBuilder(name) - .greedy() - .asOptional() - .withSuggestionsProvider { context, _ -> - (commandManager.commandHelpHandler.queryHelp(context.sender, "") as CommandHelpHandler.IndexHelpTopic) - .entries.map { it.syntaxString } - } - .build() - } + fun helpQuery(name: String): CommandArgument { + return StringArgument.newBuilder(name) + .greedy() + .asOptional() + .withSuggestionsProvider { context, _ -> + (commandManager.commandHelpHandler.queryHelp( + context.sender, + "" + ) as CommandHelpHandler.IndexHelpTopic) + .entries.map { it.syntaxString } + } + .build() + } - fun positiveInteger(name: String): IntegerArgument.Builder { - return IntegerArgument.newBuilder(name) - .withMin(1) - } + fun positiveInteger(name: String): IntegerArgument.Builder { + return IntegerArgument.newBuilder(name) + .withMin(1) + } - fun messageConfig(name: String): CommandArgument { - return commandManager.argumentBuilder(MessageConfig::class.java, name) - .withSuggestionsProvider { _, _ -> configManager.messageConfigs.keys.toList() } - .withParser { _, inputQueue -> - val input = inputQueue.peek() - val config = configManager.messageConfigs[input] - ?: return@withParser ArgumentParseResult.failure(IllegalArgumentException( - "No message config for name '$input'. Must be one of: ${configManager.messageConfigs.keys.joinToString(", ")}" - )) - inputQueue.remove() - ArgumentParseResult.success(config) - } - .build() - } + fun messageConfig(name: String): CommandArgument { + return commandManager.argumentBuilder(MessageConfig::class.java, name) + .withSuggestionsProvider { _, _ -> configManager.messageConfigs.keys.toList() } + .withParser { _, inputQueue -> + val input = inputQueue.peek() + val config = configManager.messageConfigs[input] + ?: return@withParser ArgumentParseResult.failure( + IllegalArgumentException( + "No message config for name '$input'. Must be one of: ${configManager.messageConfigs.keys.joinToString(", ")}" + ) + ) + inputQueue.remove() + ArgumentParseResult.success(config) + } + .build() + } - fun worldPlayers(name: String): CommandArgument { - return commandManager.argumentBuilder(WorldPlayers::class.java, name) - .withSuggestionsProvider { _, _ -> - val suggestions = Bukkit.getWorlds().map { it.name }.toMutableList() - suggestions.add("all") - suggestions - } - .withParser { _, inputQueue -> - val input = inputQueue.peek() - if (input == null || input.isEmpty()) { - return@withParser ArgumentParseResult.failure( - IllegalArgumentException("No input provided.") - ) - } - if (input == "all") { - inputQueue.remove() - return@withParser ArgumentParseResult.success(WorldPlayers( - Bukkit.getWorlds().flatMap { it.players } - )) - } - val world = Bukkit.getWorld(input) - ?: return@withParser ArgumentParseResult.failure( - IllegalArgumentException("No such world: $input") - ) - inputQueue.remove() - ArgumentParseResult.success(WorldPlayers(ImmutableList.copyOf(world.players))) - } - .build() - } + fun worldPlayers(name: String): CommandArgument { + return commandManager.argumentBuilder(WorldPlayers::class.java, name) + .withSuggestionsProvider { _, _ -> + val suggestions = Bukkit.getWorlds().map { it.name }.toMutableList() + suggestions.add("all") + suggestions + } + .withParser { _, inputQueue -> + val input = inputQueue.peek() + if (input.isEmpty()) { + return@withParser ArgumentParseResult.failure( + IllegalArgumentException("No input provided.") + ) + } + if (input == "all") { + inputQueue.remove() + return@withParser ArgumentParseResult.success(WorldPlayers( + Bukkit.getWorlds().flatMap { it.players } + )) + } + val world = Bukkit.getWorld(input) + ?: return@withParser ArgumentParseResult.failure( + IllegalArgumentException("No such world: $input") + ) + inputQueue.remove() + ArgumentParseResult.success(WorldPlayers(ImmutableList.copyOf(world.players))) + } + .build() + } - data class WorldPlayers(val players: Collection) + data class WorldPlayers(val players: Collection) } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/BaseCommand.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/BaseCommand.kt index ef86766..9305fbc 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/BaseCommand.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/BaseCommand.kt @@ -1,7 +1,30 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command import org.koin.core.KoinComponent interface BaseCommand : KoinComponent { - fun register() + fun register() } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandAnnouncerPlus.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandAnnouncerPlus.kt index d82492e..34f062b 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandAnnouncerPlus.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandAnnouncerPlus.kt @@ -1,8 +1,29 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command import cloud.commandframework.context.CommandContext -import cloud.commandframework.kotlin.extension.commandBuilder -import cloud.commandframework.kotlin.extension.description import cloud.commandframework.minecraft.extras.MinecraftHelp import com.google.common.collect.ImmutableSet import net.kyori.adventure.text.Component @@ -19,161 +40,174 @@ import org.koin.core.inject import xyz.jpenilla.announcerplus.AnnouncerPlus import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.announcerplus.config.message.MessageConfig +import xyz.jpenilla.announcerplus.util.description import xyz.jpenilla.jmplib.Chat import java.util.Collections import kotlin.math.roundToInt class CommandAnnouncerPlus : BaseCommand { - private val announcerPlus: AnnouncerPlus by inject() - private val commandManager: CommandManager by inject() - private val configManager: ConfigManager by inject() - private val minecraftHelp: MinecraftHelp by inject() - private val argumentFactory: ArgumentFactory by inject() - private val chat: Chat by inject() + private val announcerPlus: AnnouncerPlus by inject() + private val commandManager: CommandManager by inject() + private val configManager: ConfigManager by inject() + private val minecraftHelp: MinecraftHelp by inject() + private val argumentFactory: ArgumentFactory by inject() + private val chat: Chat by inject() - override fun register() { - commandManager.commandBuilder("ap", aliases = arrayOf("announcerplus", "announcer")) { - registerCopy { - commandDescription("Shows help for AnnouncerPlus commands.") - literal("help") - argument(description("Help Query")) { - argumentFactory.helpQuery("query") - } - handler(::executeHelp) - } - registerCopy { - commandDescription("Prints some information about AnnouncerPlus.") - literal("about") - handler(::executeAbout) - } - registerCopy { - permission = "announcerplus.reload" - commandDescription("Reloads AnnouncerPlus configs.") - literal("reload") - handler(::executeReload) - } - registerCopy { - permission = "announcerplus.list" - commandDescription("Displays the chat messages of a message config to the command sender.") - literal("list") - argument(argumentFactory.messageConfig("config")) - argument(argumentFactory.positiveInteger("page").asOptional()) - handler(::executeList) - } + override fun register() { + commandManager.registerSubcommand("help") { + permission = "announcerplus.command.help" + commandDescription("Shows help for AnnouncerPlus commands.") + argument(description("Help Query")) { + argumentFactory.helpQuery("query") + } + handler(::executeHelp) + } + commandManager.registerSubcommand("about") { + permission = "announcerplus.command.about" + commandDescription("Prints some information about AnnouncerPlus.") + handler(::executeAbout) + } + commandManager.registerSubcommand("reload") { + permission = "announcerplus.command.reload" + commandDescription("Reloads AnnouncerPlus configs.") + handler(::executeReload) + } + commandManager.registerSubcommand("list") { + permission = "announcerplus.command.list" + commandDescription("Displays the chat messages of a message config to the command sender.") + argument(argumentFactory.messageConfig("config")) + argument(argumentFactory.positiveInteger("page").asOptional()) + handler(::executeList) + } + } + + private fun executeHelp(ctx: CommandContext) { + minecraftHelp.queryCommands(ctx.getOrDefault("query", "")!!, ctx.sender) + } + + private fun executeAbout(ctx: CommandContext) { + val color = randomColor() + val text = + "click me!'>${announcerPlus.name} <$color>${announcerPlus.description.version}" + val header = "${ + "-".repeat( + (announcerPlus.miniMessage().stripTokens(text).length / 1.2).roundToInt() + 4 + ) + }" + listOf( + header, + text, + "By <$color>jmp", + header + ).forEach { chat.send(ctx.sender, chat.getCenteredMessage(it)) } + } + + private fun executeReload(ctx: CommandContext) { + chat.send( + ctx.sender, + chat.getCenteredMessage("Reloading ${announcerPlus.name} config...") + ) + try { + announcerPlus.reload() + chat.send(ctx.sender, chat.getCenteredMessage("Done.")) + } catch (e: Exception) { + chat.send( + ctx.sender, + "I'm sorry, but there was an error reloading the plugin. This is most likely due to misconfiguration. Check console for more info." + ) + e.printStackTrace() + } + } + + private fun executeList(ctx: CommandContext) { + val color = randomColor() + val config = ctx.get("config") + val page = ctx.getOrDefault("page", 1) ?: 1 + val pagination = Pagination.builder().apply { + resultsPerPage(17) + width(53) + line { line -> + line.character('-') + line.style(Style.style { builder -> + builder.color(TextColor.fromHexString(color)) + builder.decorate(TextDecoration.STRIKETHROUGH) + }) + } + renderer(object : Pagination.Renderer { + override fun renderNextPageButton(character: Char, style: Style, clickEvent: ClickEvent): Component { + LinearComponents.linear( + + ) + return Component.text() + .append(Component.space()) + .append(Component.text("[", NamedTextColor.WHITE)) + .append(Component.text(character, style.clickEvent(clickEvent))) + .append(Component.text("]", NamedTextColor.WHITE)) + .append(Component.space()) + .build() } - } - private fun executeHelp(ctx: CommandContext) { - minecraftHelp.queryCommands(ctx.getOrDefault("query", "")!!, ctx.sender) - } - - private fun executeAbout(ctx: CommandContext) { - val color = randomColor() - val text = "click me!'>${announcerPlus.name} <$color>${announcerPlus.description.version}" - val header = "${"-".repeat((announcerPlus.miniMessage.stripTokens(text).length / 1.2).roundToInt() + 4)}" - listOf( - header, - text, - "By <$color>jmp", - header - ).forEach { chat.send(ctx.sender, chat.getCenteredMessage(it)) } - } - - private fun executeReload(ctx: CommandContext) { - chat.send(ctx.sender, chat.getCenteredMessage("Reloading ${announcerPlus.name} config...")) - try { - announcerPlus.reload() - chat.send(ctx.sender, chat.getCenteredMessage("Done.")) - } catch (e: Exception) { - chat.send(ctx.sender, "I'm sorry, but there was an error reloading the plugin. This is most likely due to misconfiguration. Check console for more info.") - e.printStackTrace() + override fun renderPreviousPageButton( + character: Char, + style: Style, + clickEvent: ClickEvent + ): Component { + return Component.text() + .append(Component.space()) + .append(Component.text("[", NamedTextColor.WHITE)) + .append(Component.text(character, style.clickEvent(clickEvent))) + .append(Component.text("]", NamedTextColor.WHITE)) + .append(Component.space()) + .build() } - } + }) + nextButton { nextButton -> + nextButton.style(Style.style { builder -> + builder.decorate(TextDecoration.BOLD) + builder.color(TextColor.fromHexString(color)) + builder.hoverEvent(HoverEvent.showText(Component.text("Next Page", NamedTextColor.GREEN))) + }) + } + previousButton { prevButton -> + prevButton.style(Style.style { builder -> + builder.decorate(TextDecoration.BOLD) + builder.color(TextColor.fromHexString(color)) + builder.hoverEvent(HoverEvent.showText(Component.text("Previous Page", NamedTextColor.RED))) + }) + } + }.build( + Component.text("Messages"), + { value, _ -> Collections.singleton(value?.let { announcerPlus.miniMessage().parse(it) }) }, + { "/announcerplus list ${config.name} $it" } + ) - private fun executeList(ctx: CommandContext) { - val color = randomColor() - val config = ctx.get("config") - val page = ctx.getOrDefault("page", 1) ?: 1 - val pagination = Pagination.builder().apply { - resultsPerPage(17) - width(53) - line { line -> - line.character('-') - line.style(Style.style { builder -> - builder.color(TextColor.fromHexString(color)) - builder.decorate(TextDecoration.STRIKETHROUGH) - }) - } - renderer(object : Pagination.Renderer { - override fun renderNextPageButton(character: Char, style: Style, clickEvent: ClickEvent): Component { - LinearComponents.linear( - - ) - return Component.text() - .append(Component.space()) - .append(Component.text("[", NamedTextColor.WHITE)) - .append(Component.text(character, style.clickEvent(clickEvent))) - .append(Component.text("]", NamedTextColor.WHITE)) - .append(Component.space()) - .build() - } - - override fun renderPreviousPageButton( - character: Char, - style: Style, - clickEvent: ClickEvent - ): Component { - return Component.text() - .append(Component.space()) - .append(Component.text("[", NamedTextColor.WHITE)) - .append(Component.text(character, style.clickEvent(clickEvent))) - .append(Component.text("]", NamedTextColor.WHITE)) - .append(Component.space()) - .build() - } - }) - nextButton { nextButton -> - nextButton.style(Style.style { builder -> - builder.decorate(TextDecoration.BOLD) - builder.color(TextColor.fromHexString(color)) - builder.hoverEvent(HoverEvent.showText(Component.text("Next Page", NamedTextColor.GREEN))) - }) - } - previousButton { prevButton -> - prevButton.style(Style.style { builder -> - builder.decorate(TextDecoration.BOLD) - builder.color(TextColor.fromHexString(color)) - builder.hoverEvent(HoverEvent.showText(Component.text("Previous Page", NamedTextColor.RED))) - }) - } - }.build( - Component.text("Messages"), - { value, _ -> Collections.singleton(value?.let { announcerPlus.miniMessage.parse(it) }) }, - { "/announcerplus list ${config.name} $it" } - ) - - val messages = arrayListOf() - for (msg in config.messages) { - for (line in msg.messageText) { - val b = StringBuilder() - if (msg.messageText.indexOf(line) == 0) { - b.append(" - ") - } else { - b.append(" - ") - } - b.append("\"$line\"") - messages.add(configManager.parse(ctx.sender, b.toString())) - } + val messages = arrayListOf() + for (msg in config.messages) { + for (line in msg.messageText) { + val b = StringBuilder() + if (msg.messageText.indexOf(line) == 0) { + b.append(" - ") + } else { + b.append(" - ") } - val l = arrayListOf(announcerPlus.miniMessage.parse("Config: ${config.name} Click to copy'>(announcerplus.messages.${config.name})")) - l.addAll(pagination.render(messages, page)) - chat.send(ctx.sender, l) + b.append("\"$line\"") + messages.add(configManager.parse(ctx.sender, b.toString())) + } } + val l = arrayListOf( + announcerPlus.miniMessage() + .parse("Config: ${config.name} Click to copy'>(announcerplus.messages.${config.name})") + ) + l.addAll(pagination.render(messages, page)) + chat.send(ctx.sender, l) + } - companion object { - private val colors = ImmutableSet.of( - "#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", - "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b", "#333333") - fun randomColor(): String = colors.random() - } + companion object { + private val colors = ImmutableSet.of( + "#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", + "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722", "#795548", "#9e9e9e", "#607d8b", "#333333" + ) + + fun randomColor(): String = colors.random() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandBroadcast.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandBroadcast.kt index f12dfce..f643841 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandBroadcast.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandBroadcast.kt @@ -1,11 +1,32 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command import cloud.commandframework.arguments.standard.EnumArgument import cloud.commandframework.arguments.standard.StringArgument import cloud.commandframework.bukkit.parsers.MaterialArgument import cloud.commandframework.context.CommandContext -import cloud.commandframework.kotlin.extension.commandBuilder -import cloud.commandframework.kotlin.extension.description import net.kyori.adventure.bossbar.BossBar import org.bukkit.command.CommandSender import org.koin.core.inject @@ -15,102 +36,99 @@ import xyz.jpenilla.announcerplus.config.message.ToastSettings import xyz.jpenilla.announcerplus.task.ActionBarUpdateTask import xyz.jpenilla.announcerplus.task.BossBarUpdateTask import xyz.jpenilla.announcerplus.task.TitleUpdateTask +import xyz.jpenilla.announcerplus.util.description import xyz.jpenilla.jmplib.Chat class CommandBroadcast : BaseCommand { - private val commandManager: CommandManager by inject() - private val configManager: ConfigManager by inject() - private val argumentFactory: ArgumentFactory by inject() - private val announcerPlus: AnnouncerPlus by inject() - private val chat: Chat by inject() + private val commandManager: CommandManager by inject() + private val configManager: ConfigManager by inject() + private val argumentFactory: ArgumentFactory by inject() + private val announcerPlus: AnnouncerPlus by inject() + private val chat: Chat by inject() - override fun register() { - commandManager.commandBuilder("ap") { - registerCopy("broadcast") { - permission = "announcerplus.broadcast" - commandDescription("Parses and broadcasts a message to chat in the specified world or all worlds.") - argument(argumentFactory.worldPlayers("world")) - argument(StringArgument.greedy("message")) - handler(::executeBroadcast) - } - if (announcerPlus.toastTask != null) { - registerCopy("broadcasttoast") { - permission = "announcerplus.broadcasttoast" - commandDescription("Parses and broadcasts a Toast style message to the specified world or all worlds.") - argument(argumentFactory.worldPlayers("world")) - argument(MaterialArgument.of("icon")) - argument(EnumArgument.of(ToastSettings.FrameType::class.java, "frame")) - argument(StringArgument.quoted("header"), description("Quoted String")) - argument(StringArgument.quoted("body"), description("Quoted String")) - handler(::executeBroadcastToast) - } - } - registerCopy("broadcasttitle") { - permission = "announcerplus.broadcasttitle" - commandDescription("Parses and broadcasts a Title and Subtitle style message to the specified world or all worlds.") - argument(argumentFactory.worldPlayers("world")) - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.quoted("title"), description("Quoted String")) - argument(StringArgument.quoted("subtitle"), description("Quoted String")) - handler(::executeBroadcastTitle) - } - registerCopy("broadcastactionbar") { - permission = "announcerplus.broadcastactionbar" - commandDescription("Parses and broadcasts an Action Bar style message to the specified world or all worlds.") - argument(argumentFactory.worldPlayers("world")) - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.greedy("text")) - handler(::executeBroadcastActionBar) - } - registerCopy("broadcastbossbar") { - permission = "announcerplus.broadcastbossbar" - commandDescription("Parses and broadcasts a Boss Bar style message to the specified world or all worlds.") - argument(argumentFactory.worldPlayers("world")) - argument(argumentFactory.positiveInteger("seconds")) - argument(EnumArgument.of(BossBar.Overlay::class.java, "overlay")) - argument(EnumArgument.of(BossBarUpdateTask.FillMode::class.java, "fillmode")) - argument(EnumArgument.of(BossBar.Color::class.java, "color")) - argument(StringArgument.greedy("text")) - handler(::executeBroadcastBossBar) - } - } + override fun register() { + commandManager.registerSubcommand("broadcast") { + permission = "announcerplus.command.broadcast.chat" + commandDescription("Parses and broadcasts a message to chat in the specified world or all worlds.") + argument(argumentFactory.worldPlayers("world")) + argument(StringArgument.greedy("message")) + handler(::executeBroadcast) } + commandManager.registerSubcommand("broadcasttoast", announcerPlus.toastTask != null) { + permission = "announcerplus.command.broadcast.toast" + commandDescription("Parses and broadcasts a Toast style message to the specified world or all worlds.") + argument(argumentFactory.worldPlayers("world")) + argument(MaterialArgument.of("icon")) + argument(EnumArgument.of(ToastSettings.FrameType::class.java, "frame")) + argument(StringArgument.quoted("header"), description("Quoted String")) + argument(StringArgument.quoted("body"), description("Quoted String")) + handler(::executeBroadcastToast) + } + commandManager.registerSubcommand("broadcasttitle") { + permission = "announcerplus.command.broadcast.title" + commandDescription("Parses and broadcasts a Title and Subtitle style message to the specified world or all worlds.") + argument(argumentFactory.worldPlayers("world")) + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.quoted("title"), description("Quoted String")) + argument(StringArgument.quoted("subtitle"), description("Quoted String")) + handler(::executeBroadcastTitle) + } + commandManager.registerSubcommand("broadcastactionbar") { + permission = "announcerplus.command.broadcast.actionbar" + commandDescription("Parses and broadcasts an Action Bar style message to the specified world or all worlds.") + argument(argumentFactory.worldPlayers("world")) + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.greedy("text")) + handler(::executeBroadcastActionBar) + } + commandManager.registerSubcommand("broadcastbossbar") { + permission = "announcerplus.command.broadcast.bossbar" + commandDescription("Parses and broadcasts a Boss Bar style message to the specified world or all worlds.") + argument(argumentFactory.worldPlayers("world")) + argument(argumentFactory.positiveInteger("seconds")) + argument(EnumArgument.of(BossBar.Overlay::class.java, "overlay")) + argument(EnumArgument.of(BossBarUpdateTask.FillMode::class.java, "fillmode")) + argument(EnumArgument.of(BossBar.Color::class.java, "color")) + argument(StringArgument.greedy("text")) + handler(::executeBroadcastBossBar) + } + } - private fun executeBroadcast(ctx: CommandContext) { - for (player in ctx.get("world").players) { - chat.send(player, configManager.parse(player, ctx.get("message"))) - } + private fun executeBroadcast(ctx: CommandContext) { + for (player in ctx.get("world").players) { + chat.send(player, configManager.parse(player, ctx.get("message"))) } + } - private fun executeBroadcastToast(ctx: CommandContext) { - val toast = ToastSettings(ctx.get("icon"), ctx.get("frame"), ctx.get("header"), ctx.get("body")) - for (player in ctx.get("world").players) { - toast.displayIfEnabled(player) - } + private fun executeBroadcastToast(ctx: CommandContext) { + val toast = ToastSettings(ctx.get("icon"), ctx.get("frame"), ctx.get("header"), ctx.get("body")) + for (player in ctx.get("world").players) { + toast.displayIfEnabled(player) } + } - private fun executeBroadcastTitle(ctx: CommandContext) { - for (player in ctx.get("world").players) { - TitleUpdateTask(player, 0, ctx.get("seconds"), 1, ctx.get("title"), ctx.get("subtitle")).start() - } + private fun executeBroadcastTitle(ctx: CommandContext) { + for (player in ctx.get("world").players) { + TitleUpdateTask(player, 0, ctx.get("seconds"), 1, ctx.get("title"), ctx.get("subtitle")).start() } + } - private fun executeBroadcastActionBar(ctx: CommandContext) { - for (player in ctx.get("world").players) { - ActionBarUpdateTask(player, ctx.get("seconds") * 20L, true, ctx.get("text")).start() - } + private fun executeBroadcastActionBar(ctx: CommandContext) { + for (player in ctx.get("world").players) { + ActionBarUpdateTask(player, ctx.get("seconds") * 20L, true, ctx.get("text")).start() } + } - private fun executeBroadcastBossBar(ctx: CommandContext) { - for (player in ctx.get("world").players) { - BossBarUpdateTask( - player, - ctx.get("seconds"), - ctx.get("overlay"), - ctx.get("fillmode"), - ctx.get("color").toString(), - ctx.get("text") - ).start() - } + private fun executeBroadcastBossBar(ctx: CommandContext) { + for (player in ctx.get("world").players) { + BossBarUpdateTask( + player, + ctx.get("seconds"), + ctx.get("overlay"), + ctx.get("fillmode"), + ctx.get("color").toString(), + ctx.get("text") + ).start() } + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandManager.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandManager.kt index 9360621..850780d 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandManager.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandManager.kt @@ -1,6 +1,32 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command +import cloud.commandframework.bukkit.CloudBukkitCapabilities import cloud.commandframework.execution.AsynchronousCommandExecutionCoordinator +import cloud.commandframework.kotlin.MutableCommandBuilder +import cloud.commandframework.kotlin.extension.commandBuilder import cloud.commandframework.minecraft.extras.MinecraftExceptionHandler import cloud.commandframework.minecraft.extras.MinecraftHelp import cloud.commandframework.paper.PaperCommandManager @@ -15,63 +41,80 @@ import xyz.jpenilla.announcerplus.util.Constants import java.util.function.Function class CommandManager(announcerPlus: AnnouncerPlus) : PaperCommandManager( - announcerPlus, - AsynchronousCommandExecutionCoordinator.newBuilder().build(), - Function.identity(), - Function.identity() + announcerPlus, + AsynchronousCommandExecutionCoordinator.newBuilder().build(), + Function.identity(), + Function.identity() ) { - private val minecraftHelp = MinecraftHelp( - "/announcerplus help", - announcerPlus.audience::sender, - this - ).apply { - helpColors = MinecraftHelp.HelpColors.of( - TextColor.color(0x00a3ff), - NamedTextColor.WHITE, - TextColor.color(0x284fff), - NamedTextColor.GRAY, - NamedTextColor.DARK_GRAY - ) - setMessage(MinecraftHelp.MESSAGE_HELP_TITLE, "AnnouncerPlus Help") + private val minecraftHelp = MinecraftHelp( + "/announcerplus help", + announcerPlus.audiences()::sender, + this + ).apply { + helpColors = MinecraftHelp.HelpColors.of( + TextColor.color(0x00a3ff), + NamedTextColor.WHITE, + TextColor.color(0x284fff), + NamedTextColor.GRAY, + NamedTextColor.DARK_GRAY + ) + setMessage(MinecraftHelp.MESSAGE_HELP_TITLE, "AnnouncerPlus Help") + } + + init { + MinecraftExceptionHandler() + .withDefaultHandlers() + .withDecorator { + Component.text() + .append(Constants.CHAT_PREFIX) + .append(it) + .build() + } + .apply(this, announcerPlus.audiences()::sender) + + if (queryCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) { + this.registerBrigadier() + this.brigadierManager()?.setNativeNumberSuggestions(false) + announcerPlus.logger.info("Successfully registered Mojang Brigadier support for commands.") } - init { - MinecraftExceptionHandler() - .withDefaultHandlers() - .withDecorator { - Component.text() - .append(Constants.CHAT_PREFIX) - .append(it) - .build() - } - .apply(this, announcerPlus.audience::sender) - - try { - this.registerBrigadier() - this.brigadierManager()?.setNativeNumberSuggestions(false) - announcerPlus.logger.info("Successfully registered Mojang Brigadier support for commands.") - } catch (ignored: Exception) { - } - - try { - this.registerAsynchronousCompletions() - announcerPlus.logger.info("Successfully registered asynchronous command completion listener.") - } catch (ignored: Exception) { - } - - loadKoinModules(module { - single { this@CommandManager } - single { minecraftHelp } - single { ArgumentFactory() } - }) - - listOf( - CommandAnnouncerPlus(), - CommandBroadcast(), - CommandSend(), - CommandParse() - ).forEach(BaseCommand::register) + if (queryCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + this.registerAsynchronousCompletions() + announcerPlus.logger.info("Successfully registered asynchronous command completion listener.") } + loadKoinModules(module { + single { this@CommandManager } + single { minecraftHelp } + single { ArgumentFactory() } + }) + + listOf( + CommandAnnouncerPlus(), + CommandBroadcast(), + CommandSend(), + CommandParse() + ).forEach(BaseCommand::register) + } + + fun rootBuilder( + lambda: MutableCommandBuilder.() -> Unit = {} + ) = this.commandBuilder("ap", aliases = arrayOf("announcerplus", "announcer"), lambda = lambda) + + fun registerSubcommand( + literal: String, + lambda: MutableCommandBuilder.() -> Unit + ) = this.rootBuilder().literal(literal).apply(lambda).register() + + fun registerSubcommand( + literal: String, + registrationPredicate: Boolean, + lambda: MutableCommandBuilder.() -> Unit + ) { + if (registrationPredicate) { + this.registerSubcommand(literal, lambda) + } + } + } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandParse.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandParse.kt index c434dbc..ee394db 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandParse.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandParse.kt @@ -1,11 +1,32 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command import cloud.commandframework.arguments.standard.EnumArgument import cloud.commandframework.arguments.standard.StringArgument import cloud.commandframework.bukkit.parsers.MaterialArgument import cloud.commandframework.context.CommandContext -import cloud.commandframework.kotlin.extension.commandBuilder -import cloud.commandframework.kotlin.extension.description import net.kyori.adventure.bossbar.BossBar import org.bukkit.command.CommandSender import org.bukkit.entity.Player @@ -16,107 +37,104 @@ import xyz.jpenilla.announcerplus.config.message.ToastSettings import xyz.jpenilla.announcerplus.task.ActionBarUpdateTask import xyz.jpenilla.announcerplus.task.BossBarUpdateTask import xyz.jpenilla.announcerplus.task.TitleUpdateTask +import xyz.jpenilla.announcerplus.util.description import xyz.jpenilla.jmplib.Chat class CommandParse : BaseCommand { - private val commandManager: CommandManager by inject() - private val configManager: ConfigManager by inject() - private val argumentFactory: ArgumentFactory by inject() - private val announcerPlus: AnnouncerPlus by inject() - private val chat: Chat by inject() + private val commandManager: CommandManager by inject() + private val configManager: ConfigManager by inject() + private val argumentFactory: ArgumentFactory by inject() + private val announcerPlus: AnnouncerPlus by inject() + private val chat: Chat by inject() - override fun register() { - commandManager.commandBuilder("ap") { - registerCopy("parse") { - permission = "announcerplus.parse" - commandDescription("Parses a message and echoes it back.") - argument(StringArgument.greedy("message")) - handler(::executeParse) - } - if (announcerPlus.toastTask != null) { - registerCopy("parsetoast") { - permission = "announcerplus.parsetoast" - senderType() - commandDescription("Parses a Toast style message and displays it to the command sender.") - argument(MaterialArgument.of("icon")) - argument(EnumArgument.of(ToastSettings.FrameType::class.java, "frame")) - argument(StringArgument.quoted("header"), description("Quoted String")) - argument(StringArgument.quoted("body"), description("Quoted String")) - handler(::executeParseToast) - } - } - registerCopy("parsetitle") { - permission = "announcerplus.parsetitle" - senderType() - commandDescription("Parses a Title and Subtitle style message and displays it to the command sender.") - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.quoted("title"), description("Quoted String")) - argument(StringArgument.quoted("subtitle"), description("Quoted String")) - handler(::executeParseTitle) - } - registerCopy("parseactionbar") { - permission = "announcerplus.parseactionbar" - senderType() - commandDescription("Parses an Action Bar style message and displays it to the command sender.") - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.greedy("text")) - handler(::executeParseActionBar) - } - registerCopy("parsebossbar") { - permission = "announcerplus.parsebossbar" - senderType() - commandDescription("Parses a Boss Bar style message and displays it to the command sender.") - argument(argumentFactory.positiveInteger("seconds")) - argument(EnumArgument.of(BossBar.Overlay::class.java, "overlay")) - argument(EnumArgument.of(BossBarUpdateTask.FillMode::class.java, "fillmode")) - argument(EnumArgument.of(BossBar.Color::class.java, "color")) - argument(StringArgument.greedy("text")) - handler(::executeParseBossBar) - } - registerCopy("parseanimation") { - permission = "announcerplus.parseanimation" - senderType() - commandDescription("Parses a message with an animation and displays it to the command sender.") - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.greedy("message")) - handler(::executeParseAnimation) - } - } + override fun register() { + commandManager.registerSubcommand("parse") { + permission = "announcerplus.command.parse.chat" + commandDescription("Parses a message and echoes it back.") + argument(StringArgument.greedy("message")) + handler(::executeParse) } + commandManager.registerSubcommand("parsetoast", announcerPlus.toastTask != null) { + permission = "announcerplus.command.parse.toast" + senderType() + commandDescription("Parses a Toast style message and displays it to the command sender.") + argument(MaterialArgument.of("icon")) + argument(EnumArgument.of(ToastSettings.FrameType::class.java, "frame")) + argument(StringArgument.quoted("header"), description("Quoted String")) + argument(StringArgument.quoted("body"), description("Quoted String")) + handler(::executeParseToast) + } + commandManager.registerSubcommand("parsetitle") { + permission = "announcerplus.command.parse.title" + senderType() + commandDescription("Parses a Title and Subtitle style message and displays it to the command sender.") + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.quoted("title"), description("Quoted String")) + argument(StringArgument.quoted("subtitle"), description("Quoted String")) + handler(::executeParseTitle) + } + commandManager.registerSubcommand("parseactionbar") { + permission = "announcerplus.command.parse.actionbar" + senderType() + commandDescription("Parses an Action Bar style message and displays it to the command sender.") + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.greedy("text")) + handler(::executeParseActionBar) + } + commandManager.registerSubcommand("parsebossbar") { + permission = "announcerplus.command.parse.bossbar" + senderType() + commandDescription("Parses a Boss Bar style message and displays it to the command sender.") + argument(argumentFactory.positiveInteger("seconds")) + argument(EnumArgument.of(BossBar.Overlay::class.java, "overlay")) + argument(EnumArgument.of(BossBarUpdateTask.FillMode::class.java, "fillmode")) + argument(EnumArgument.of(BossBar.Color::class.java, "color")) + argument(StringArgument.greedy("text")) + handler(::executeParseBossBar) + } + commandManager.registerSubcommand("parseanimation") { + permission = "announcerplus.command.parse.animation" + senderType() + commandDescription("Parses a message with an animation and displays it to the command sender.") + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.greedy("message")) + handler(::executeParseAnimation) + } + } - private fun executeParse(ctx: CommandContext) { - chat.send(ctx.sender, configManager.parse(ctx.sender, ctx.get("message"))) - } + private fun executeParse(ctx: CommandContext) { + chat.send(ctx.sender, configManager.parse(ctx.sender, ctx.get("message"))) + } - private fun executeParseToast(ctx: CommandContext) { - val toast = ToastSettings(ctx.get("icon"), ctx.get("frame"), ctx.get("header"), ctx.get("body")) - toast.displayIfEnabled(ctx.sender as Player) - } + private fun executeParseToast(ctx: CommandContext) { + val toast = ToastSettings(ctx.get("icon"), ctx.get("frame"), ctx.get("header"), ctx.get("body")) + toast.displayIfEnabled(ctx.sender as Player) + } - private fun executeParseTitle(ctx: CommandContext) { - TitleUpdateTask(ctx.sender as Player, 0, ctx.get("seconds"), 1, ctx.get("title"), ctx.get("subtitle")).start() - } + private fun executeParseTitle(ctx: CommandContext) { + TitleUpdateTask(ctx.sender as Player, 0, ctx.get("seconds"), 1, ctx.get("title"), ctx.get("subtitle")).start() + } - private fun executeParseActionBar(ctx: CommandContext) { - ActionBarUpdateTask(ctx.sender as Player, ctx.get("seconds") * 20L, true, ctx.get("text")).start() - } + private fun executeParseActionBar(ctx: CommandContext) { + ActionBarUpdateTask(ctx.sender as Player, ctx.get("seconds") * 20L, true, ctx.get("text")).start() + } - private fun executeParseBossBar(ctx: CommandContext) { - BossBarUpdateTask( - ctx.sender as Player, - ctx.get("seconds"), - ctx.get("overlay"), - ctx.get("fillmode"), - ctx.get("color").toString(), - ctx.get("text") - ).start() - } + private fun executeParseBossBar(ctx: CommandContext) { + BossBarUpdateTask( + ctx.sender as Player, + ctx.get("seconds"), + ctx.get("overlay"), + ctx.get("fillmode"), + ctx.get("color").toString(), + ctx.get("text") + ).start() + } - private fun executeParseAnimation(ctx: CommandContext) { - val player = ctx.sender as Player - val seconds = ctx.get("seconds") - val message = ctx.get("message") - TitleUpdateTask(player, 0, seconds, 0, message, message).start() - ActionBarUpdateTask(player, seconds * 20L, false, message).start() - } + private fun executeParseAnimation(ctx: CommandContext) { + val player = ctx.sender as Player + val seconds = ctx.get("seconds") + val message = ctx.get("message") + TitleUpdateTask(player, 0, seconds, 0, message, message).start() + ActionBarUpdateTask(player, seconds * 20L, false, message).start() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandSend.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandSend.kt index 511f028..e3c2045 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandSend.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/command/CommandSend.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.command import cloud.commandframework.arguments.standard.EnumArgument @@ -6,8 +29,6 @@ import cloud.commandframework.bukkit.arguments.selector.MultiplePlayerSelector import cloud.commandframework.bukkit.parsers.MaterialArgument import cloud.commandframework.bukkit.parsers.selector.MultiplePlayerSelectorArgument import cloud.commandframework.context.CommandContext -import cloud.commandframework.kotlin.extension.commandBuilder -import cloud.commandframework.kotlin.extension.description import net.kyori.adventure.bossbar.BossBar import org.bukkit.command.CommandSender import org.koin.core.inject @@ -17,102 +38,99 @@ import xyz.jpenilla.announcerplus.config.message.ToastSettings import xyz.jpenilla.announcerplus.task.ActionBarUpdateTask import xyz.jpenilla.announcerplus.task.BossBarUpdateTask import xyz.jpenilla.announcerplus.task.TitleUpdateTask +import xyz.jpenilla.announcerplus.util.description import xyz.jpenilla.jmplib.Chat class CommandSend : BaseCommand { - private val commandManager: CommandManager by inject() - private val configManager: ConfigManager by inject() - private val argumentFactory: ArgumentFactory by inject() - private val announcerPlus: AnnouncerPlus by inject() - private val chat: Chat by inject() + private val commandManager: CommandManager by inject() + private val configManager: ConfigManager by inject() + private val argumentFactory: ArgumentFactory by inject() + private val announcerPlus: AnnouncerPlus by inject() + private val chat: Chat by inject() - override fun register() { - commandManager.commandBuilder("ap") { - registerCopy("send") { - permission = "announcerplus.send" - commandDescription("Parses a message and sends it to the specified players.") - argument(MultiplePlayerSelectorArgument.of("players")) - argument(StringArgument.greedy("message")) - handler(::executeSend) - } - if (announcerPlus.toastTask != null) { - registerCopy("sendtoast") { - permission = "announcerplus.sendtoast" - commandDescription("Parses and sends a Toast style message to the specified players.") - argument(MultiplePlayerSelectorArgument.of("players")) - argument(MaterialArgument.of("icon")) - argument(EnumArgument.of(ToastSettings.FrameType::class.java, "frame")) - argument(StringArgument.quoted("header"), description("Quoted String")) - argument(StringArgument.quoted("body"), description("Quoted String")) - handler(::executeSendToast) - } - } - registerCopy("sendtitle") { - permission = "announcerplus.sendtitle" - commandDescription("Parses and sends a Title and Subtitle style message to the specified players.") - argument(MultiplePlayerSelectorArgument.of("players")) - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.quoted("title"), description("Quoted String")) - argument(StringArgument.quoted("subtitle"), description("Quoted String")) - handler(::executeSendTitle) - } - registerCopy("sendactionbar") { - permission = "announcerplus.sendactionbar" - commandDescription("Parses and sends an Action Bar style message to the specified players.") - argument(MultiplePlayerSelectorArgument.of("players")) - argument(argumentFactory.positiveInteger("seconds")) - argument(StringArgument.greedy("text")) - handler(::executeSendActionBar) - } - registerCopy("sendbossbar") { - permission = "announcerplus.sendbossbar" - commandDescription("Parses and sends a Boss Bar style message to the specified players.") - argument(MultiplePlayerSelectorArgument.of("players")) - argument(argumentFactory.positiveInteger("seconds")) - argument(EnumArgument.of(BossBar.Overlay::class.java, "overlay")) - argument(EnumArgument.of(BossBarUpdateTask.FillMode::class.java, "fillmode")) - argument(EnumArgument.of(BossBar.Color::class.java, "color")) - argument(StringArgument.greedy("text")) - handler(::executeSendBossBar) - } - } + override fun register() { + commandManager.registerSubcommand("send") { + permission = "announcerplus.command.send.chat" + commandDescription("Parses a message and sends it to the specified players.") + argument(MultiplePlayerSelectorArgument.of("players")) + argument(StringArgument.greedy("message")) + handler(::executeSend) } + commandManager.registerSubcommand("sendtoast", announcerPlus.toastTask != null) { + permission = "announcerplus.command.send.toast" + commandDescription("Parses and sends a Toast style message to the specified players.") + argument(MultiplePlayerSelectorArgument.of("players")) + argument(MaterialArgument.of("icon")) + argument(EnumArgument.of(ToastSettings.FrameType::class.java, "frame")) + argument(StringArgument.quoted("header"), description("Quoted String")) + argument(StringArgument.quoted("body"), description("Quoted String")) + handler(::executeSendToast) + } + commandManager.registerSubcommand("sendtitle") { + permission = "announcerplus.command.send.title" + commandDescription("Parses and sends a Title and Subtitle style message to the specified players.") + argument(MultiplePlayerSelectorArgument.of("players")) + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.quoted("title"), description("Quoted String")) + argument(StringArgument.quoted("subtitle"), description("Quoted String")) + handler(::executeSendTitle) + } + commandManager.registerSubcommand("sendactionbar") { + permission = "announcerplus.command.send.actionbar" + commandDescription("Parses and sends an Action Bar style message to the specified players.") + argument(MultiplePlayerSelectorArgument.of("players")) + argument(argumentFactory.positiveInteger("seconds")) + argument(StringArgument.greedy("text")) + handler(::executeSendActionBar) + } + commandManager.registerSubcommand("sendbossbar") { + permission = "announcerplus.command.send.bossbar" + commandDescription("Parses and sends a Boss Bar style message to the specified players.") + argument(MultiplePlayerSelectorArgument.of("players")) + argument(argumentFactory.positiveInteger("seconds")) + argument(EnumArgument.of(BossBar.Overlay::class.java, "overlay")) + argument(EnumArgument.of(BossBarUpdateTask.FillMode::class.java, "fillmode")) + argument(EnumArgument.of(BossBar.Color::class.java, "color")) + argument(StringArgument.greedy("text")) + handler(::executeSendBossBar) + } + } - private fun executeSend(ctx: CommandContext) { - for (player in ctx.get("players").players) { - chat.send(player, configManager.parse(player, ctx.get("message"))) - } + private fun executeSend(ctx: CommandContext) { + for (player in ctx.get("players").players) { + chat.send(player, configManager.parse(player, ctx.get("message"))) } + } - private fun executeSendToast(ctx: CommandContext) { - val toast = ToastSettings(ctx.get("icon"), ctx.get("frame"), ctx.get("header"), ctx.get("body")) - for (player in ctx.get("players").players) { - toast.displayIfEnabled(player) - } + private fun executeSendToast(ctx: CommandContext) { + val toast = ToastSettings(ctx.get("icon"), ctx.get("frame"), ctx.get("header"), ctx.get("body")) + for (player in ctx.get("players").players) { + toast.displayIfEnabled(player) } + } - private fun executeSendTitle(ctx: CommandContext) { - for (player in ctx.get("players").players) { - TitleUpdateTask(player, 0, ctx.get("seconds"), 1, ctx.get("title"), ctx.get("subtitle")).start() - } + private fun executeSendTitle(ctx: CommandContext) { + for (player in ctx.get("players").players) { + TitleUpdateTask(player, 0, ctx.get("seconds"), 1, ctx.get("title"), ctx.get("subtitle")).start() } + } - private fun executeSendActionBar(ctx: CommandContext) { - for (player in ctx.get("players").players) { - ActionBarUpdateTask(player, ctx.get("seconds") * 20L, true, ctx.get("text")).start() - } + private fun executeSendActionBar(ctx: CommandContext) { + for (player in ctx.get("players").players) { + ActionBarUpdateTask(player, ctx.get("seconds") * 20L, true, ctx.get("text")).start() } + } - private fun executeSendBossBar(ctx: CommandContext) { - for (player in ctx.get("players").players) { - BossBarUpdateTask( - player, - ctx.get("seconds"), - ctx.get("overlay"), - ctx.get("fillmode"), - ctx.get("color").toString(), - ctx.get("text") - ).start() - } + private fun executeSendBossBar(ctx: CommandContext) { + for (player in ctx.get("players").players) { + BossBarUpdateTask( + player, + ctx.get("seconds"), + ctx.get("overlay"), + ctx.get("fillmode"), + ctx.get("color").toString(), + ctx.get("text") + ).start() } + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/compatability/EssentialsHook.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/compatability/EssentialsHook.kt deleted file mode 100644 index 63e7d05..0000000 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/compatability/EssentialsHook.kt +++ /dev/null @@ -1,22 +0,0 @@ -package xyz.jpenilla.announcerplus.compatability - -import net.ess3.api.IEssentials -import net.ess3.api.IUser -import org.bukkit.Bukkit -import org.bukkit.entity.Player - -class EssentialsHook { - val essentials = Bukkit.getServer().pluginManager.getPlugin("Essentials") as IEssentials - - fun isAfk(player: Player): Boolean { - return user(player).isAfk - } - - fun isVanished(player: Player): Boolean { - return user(player).isVanished - } - - fun user(player: Player): IUser { - return essentials.getUser(player) - } -} diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/compatibility/EssentialsHook.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/compatibility/EssentialsHook.kt new file mode 100644 index 0000000..b145ef2 --- /dev/null +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/compatibility/EssentialsHook.kt @@ -0,0 +1,45 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package xyz.jpenilla.announcerplus.compatibility + +import net.ess3.api.IEssentials +import net.ess3.api.IUser +import org.bukkit.Bukkit +import org.bukkit.entity.Player + +class EssentialsHook { + val essentials = Bukkit.getServer().pluginManager.getPlugin("Essentials") as IEssentials + + fun isAfk(player: Player): Boolean { + return user(player).isAfk + } + + fun isVanished(player: Player): Boolean { + return user(player).isVanished + } + + fun user(player: Player): IUser { + return essentials.getUser(player) + } +} diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/ConfigManager.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/ConfigManager.kt index 9ed4397..4d6bbde 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/ConfigManager.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/ConfigManager.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config import org.bukkit.command.CommandSender @@ -14,71 +37,73 @@ import java.nio.file.Files import kotlin.streams.toList class ConfigManager(private val announcerPlus: AnnouncerPlus) { - private val serializers: TypeSerializerCollection - private val configOptions: ConfigurationOptions + private val serializers: TypeSerializerCollection + private val configOptions: ConfigurationOptions - private val mainConfigPath = announcerPlus.dataFolder.toPath().resolve("main.conf") - private val mainConfigLoader: HoconConfigurationLoader - lateinit var mainConfig: MainConfig + private val mainConfigPath = announcerPlus.dataFolder.toPath().resolve("main.conf") + private val mainConfigLoader: HoconConfigurationLoader + lateinit var mainConfig: MainConfig - private val firstJoinConfigPath = announcerPlus.dataFolder.toPath().resolve("first-join.conf") - private val firstJoinConfigLoader: HoconConfigurationLoader - lateinit var firstJoinConfig: JoinQuitConfig + private val firstJoinConfigPath = announcerPlus.dataFolder.toPath().resolve("first-join.conf") + private val firstJoinConfigLoader: HoconConfigurationLoader + lateinit var firstJoinConfig: JoinQuitConfig - private val mapperFactory = ObjectMapper.factoryBuilder() - .addNodeResolver { name, _ -> - /* We don't want to attempt serializing delegated properties, and they can't be @Transient */ - if (name.endsWith("delegate")) { - NodeResolver.SKIP_FIELD - } else { - null - } - }.build() + private val mapperFactory = ObjectMapper.factoryBuilder() + .addNodeResolver { name, _ -> + /* We don't want to attempt serializing delegated properties, and they can't be @Transient */ + if (name.endsWith("delegate")) { + NodeResolver.SKIP_FIELD + } else { + null + } + }.build() - val messageConfigs = hashMapOf() - val joinQuitConfigs = hashMapOf() + val messageConfigs = hashMapOf() + val joinQuitConfigs = hashMapOf() - init { - serializers = TypeSerializerCollection.defaults().childBuilder() - .registerAnnotatedObjects(mapperFactory) - .build() + init { + serializers = TypeSerializerCollection.defaults().childBuilder() + .registerAnnotatedObjects(mapperFactory) + .build() - configOptions = ConfigurationOptions.defaults().serializers(serializers) + configOptions = ConfigurationOptions.defaults().serializers(serializers) - mainConfigLoader = HoconConfigurationLoader.builder().path(mainConfigPath).build() - firstJoinConfigLoader = HoconConfigurationLoader.builder().path(firstJoinConfigPath).build() + mainConfigLoader = HoconConfigurationLoader.builder().path(mainConfigPath).build() + firstJoinConfigLoader = HoconConfigurationLoader.builder().path(firstJoinConfigPath).build() - reload() + reload() + } + + fun reload() { + load() + save() + } + + private fun load() { + Files.createDirectories(announcerPlus.dataFolder.toPath()) + + val mainConfigRoot = mainConfigLoader.load(configOptions) + try { + mainConfig = MainConfig.loadFrom(mainConfigRoot) + } catch (e: Exception) { + throw InvalidConfigurationException("Failed to load the main.conf config file. This is due to misconfiguration", e) } - fun reload() { - load() - save() + val firstJoinConfigRoot = firstJoinConfigLoader.load(configOptions) + try { + firstJoinConfig = JoinQuitConfig.loadFrom(firstJoinConfigRoot, null) + } catch (e: Exception) { + throw InvalidConfigurationException("Failed to load the main.conf config file. This is due to misconfiguration", e) } - private fun load() { - Files.createDirectories(announcerPlus.dataFolder.toPath()) + loadMessageConfigs() + loadJoinQuitConfigs() + } - val mainConfigRoot = mainConfigLoader.load(configOptions) - try { - mainConfig = MainConfig.loadFrom(mainConfigRoot) - } catch (e: Exception) { - throw InvalidConfigurationException("Failed to load the main.conf config file. This is due to misconfiguration", e) - } - - val firstJoinConfigRoot = firstJoinConfigLoader.load(configOptions) - try { - firstJoinConfig = JoinQuitConfig.loadFrom(firstJoinConfigRoot, null) - } catch (e: Exception) { - throw InvalidConfigurationException("Failed to load the main.conf config file. This is due to misconfiguration", e) - } - - loadMessageConfigs() - loadJoinQuitConfigs() - } - - private fun save() { - val mainConfigRoot = mainConfigLoader.createNode(configOptions.header(""" + private fun save() { + val mainConfigRoot = mainConfigLoader.createNode( + configOptions.header( + """ | ___ ____ __ __ | / | ____ ____ ____ __ ______ ________ _____/ __ \/ /_ _______ __/ /_ | / /| | / __ \/ __ \/ __ \/ / / / __ \/ ___/ _ \/ ___/ /_/ / / / / / ___/ /_ __/ @@ -86,98 +111,108 @@ class ConfigManager(private val announcerPlus: AnnouncerPlus) { | /_/ |_/_/ /_/_/ /_/\____/\__,_/_/ /_/\___/\___/_/ /_/ /_/\__,_/____/ | | v${announcerPlus.description.version} - """.trimMargin())) - mainConfig.saveTo(mainConfigRoot) - mainConfigLoader.save(mainConfigRoot) + """.trimMargin() + ) + ) + mainConfig.saveTo(mainConfigRoot) + mainConfigLoader.save(mainConfigRoot) - val firstJoinConfigRoot = firstJoinConfigLoader.createNode(configOptions.header( - "If enabled in main.conf, this join config will be used when players join the server for the first time.\n" + - "All other join configs will be skipped for first-join if this is enabled." - )) - firstJoinConfig.saveTo(firstJoinConfigRoot) - firstJoinConfigRoot.removeChild("quit-section") - firstJoinConfigLoader.save(firstJoinConfigRoot) + val firstJoinConfigRoot = firstJoinConfigLoader.createNode( + configOptions.header( + "If enabled in main.conf, this join config will be used when players join the server for the first time.\n" + + "All other join configs will be skipped for first-join if this is enabled." + ) + ) + firstJoinConfig.saveTo(firstJoinConfigRoot) + firstJoinConfigRoot.removeChild("quit-section") + firstJoinConfigLoader.save(firstJoinConfigRoot) + } + + private fun loadJoinQuitConfigs() { + joinQuitConfigs.clear() + val path = announcerPlus.dataFolder.toPath().resolve("join-quit-configs") + if (!Files.exists(path)) { + announcerPlus.logger.info("Creating join quit config folder") + Files.createDirectories(path) } + if (Files.list(path).toList().isEmpty()) { + announcerPlus.logger.info("No join/quit configs found, creating default.conf") - private fun loadJoinQuitConfigs() { - joinQuitConfigs.clear() - val path = announcerPlus.dataFolder.toPath().resolve("join-quit-configs") - if (!Files.exists(path)) { - announcerPlus.logger.info("Creating join quit config folder") - Files.createDirectories(path) - } - if (Files.list(path).toList().isEmpty()) { - announcerPlus.logger.info("No join/quit configs found, creating default.conf") - - val defaultConfig = path.resolve("default.conf") - val defaultConfigLoader = HoconConfigurationLoader.builder().path(defaultConfig).build() - val defaultConfigRoot = defaultConfigLoader.createNode(configOptions.header( - "To give a player these join/quit messages give them the announcerplus.join.default\n" + - " and announcerplus.quit.default permissions")) - JoinQuitConfig().saveTo(defaultConfigRoot) - defaultConfigLoader.save(defaultConfigRoot) - } - Files.list(path).forEach { - val configLoader = HoconConfigurationLoader.builder().path(it).build() - val name = it.toFile().nameWithoutExtension - try { - val root = configLoader.load(configOptions) - joinQuitConfigs[name] = JoinQuitConfig.loadFrom(root, name) - - joinQuitConfigs[name]?.saveTo(root) - configLoader.save(root) - } catch (e: Exception) { - throw InvalidConfigurationException("Failed to load message config: ${it.fileName}. This is due to an invalid config file.", e) - } - } + val defaultConfig = path.resolve("default.conf") + val defaultConfigLoader = HoconConfigurationLoader.builder().path(defaultConfig).build() + val defaultConfigRoot = defaultConfigLoader.createNode( + configOptions.header( + "To give a player these join/quit messages give them the announcerplus.join.default\n" + + " and announcerplus.quit.default permissions" + ) + ) + JoinQuitConfig().saveTo(defaultConfigRoot) + defaultConfigLoader.save(defaultConfigRoot) } + Files.list(path).forEach { + val configLoader = HoconConfigurationLoader.builder().path(it).build() + val name = it.toFile().nameWithoutExtension + try { + val root = configLoader.load(configOptions) + joinQuitConfigs[name] = JoinQuitConfig.loadFrom(root, name) - private fun loadMessageConfigs() { - messageConfigs.values.forEach(MessageConfig::stop) - messageConfigs.clear() - val path = announcerPlus.dataFolder.toPath().resolve("message-configs") - if (!Files.exists(path)) { - announcerPlus.logger.info("Creating message config folder") - Files.createDirectories(path) - } - if (Files.list(path).toList().isEmpty()) { - announcerPlus.logger.info("No message configs found, creating demo.conf") + joinQuitConfigs[name]?.saveTo(root) + configLoader.save(root) + } catch (e: Exception) { + throw InvalidConfigurationException("Failed to load message config: ${it.fileName}. This is due to an invalid config file.", e) + } + } + } - val defaultConfig = path.resolve("demo.conf") - val defaultConfigLoader = HoconConfigurationLoader.builder().path(defaultConfig).build() - val defaultConfigRoot = defaultConfigLoader.createNode(configOptions.header( - """For a player to get these messages give them the announcerplus.messages.demo permission + private fun loadMessageConfigs() { + messageConfigs.values.forEach(MessageConfig::stop) + messageConfigs.clear() + val path = announcerPlus.dataFolder.toPath().resolve("message-configs") + if (!Files.exists(path)) { + announcerPlus.logger.info("Creating message config folder") + Files.createDirectories(path) + } + if (Files.list(path).toList().isEmpty()) { + announcerPlus.logger.info("No message configs found, creating demo.conf") + + val defaultConfig = path.resolve("demo.conf") + val defaultConfigLoader = HoconConfigurationLoader.builder().path(defaultConfig).build() + val defaultConfigRoot = defaultConfigLoader.createNode( + configOptions.header( + """For a player to get these messages give them the announcerplus.messages.demo permission | If EssentialsX is installed, then giving a player the announcerplus.messages.demo.afk permission - | will stop them from receiving these messages while afk""".trimMargin())) - MessageConfig().saveTo(defaultConfigRoot) - defaultConfigLoader.save(defaultConfigRoot) - } - Files.list(path).forEach { - val configLoader = HoconConfigurationLoader.builder().path(it).build() - val name = it.toFile().nameWithoutExtension - try { - val root = configLoader.load(configOptions) - messageConfigs[name] = MessageConfig.loadFrom(root, name) + | will stop them from receiving these messages while afk""".trimMargin() + ) + ) + MessageConfig().saveTo(defaultConfigRoot) + defaultConfigLoader.save(defaultConfigRoot) + } + Files.list(path).forEach { + val configLoader = HoconConfigurationLoader.builder().path(it).build() + val name = it.toFile().nameWithoutExtension + try { + val root = configLoader.load(configOptions) + messageConfigs[name] = MessageConfig.loadFrom(root, name) - messageConfigs[name]?.saveTo(root) - configLoader.save(root) - } catch (e: Exception) { - throw InvalidConfigurationException("Failed to load join/quit config: ${it.fileName}. This is due to an invalid config file.", e) - } - } + messageConfigs[name]?.saveTo(root) + configLoader.save(root) + } catch (e: Exception) { + throw InvalidConfigurationException("Failed to load join/quit config: ${it.fileName}. This is due to an invalid config file.", e) + } + } + } + + fun parse(player: CommandSender?, message: String): String { + val p = player as? Player + + val msg = announcerPlus.chat().parse(p, message, mainConfig.customPlaceholders) + if (msg.startsWith("
")) { + return announcerPlus.chat().getCenteredMessage(msg.replace("
", "")) } - fun parse(player: CommandSender?, message: String): String { - val p = player as? Player + return msg + } - val msg = announcerPlus.chat.parse(p, message, mainConfig.customPlaceholders) - if (msg.startsWith("
")) { - return announcerPlus.chat.getCenteredMessage(msg.replace("
", "")) - } - - return msg - } - - fun parse(player: CommandSender?, messages: List): List = - messages.map { parse(player, it) } + fun parse(player: CommandSender?, messages: List): List = + messages.map { parse(player, it) } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/JoinQuitConfig.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/JoinQuitConfig.kt index 66bce63..609f75b 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/JoinQuitConfig.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/JoinQuitConfig.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config import com.google.common.collect.ImmutableList @@ -24,189 +47,190 @@ import xyz.jpenilla.announcerplus.util.dispatchCommandAsConsole import xyz.jpenilla.announcerplus.util.runAsync import xyz.jpenilla.announcerplus.util.runSync import xyz.jpenilla.jmplib.Chat +import xyz.jpenilla.jmplib.Environment @ConfigSerializable class JoinQuitConfig : KoinComponent { - @Setting("visible-permission") - @Comment("If set to something other than \"\", this setting's value will be the permission required to see these join/quit messages when they are broadcasted for a player") - var permission = "" + @Setting("visible-permission") + @Comment("If set to something other than \"\", this setting's value will be the permission required to see these join/quit messages when they are broadcasted for a player") + var permission = "" - @Setting("join-section") - @Comment("Player Join related settings") - var join = JoinSection() + @Setting("join-section") + @Comment("Player Join related settings") + var join = JoinSection() - @Setting("quit-section") - @Comment("Player Quit related settings") - var quit = QuitSection() + @Setting("quit-section") + @Comment("Player Quit related settings") + var quit = QuitSection() - @ConfigSerializable - class JoinSection { - @Setting("randomize-join-sounds") - @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) - var randomSound = true + @ConfigSerializable + class JoinSection { + @Setting("randomize-join-sounds") + @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) + var randomSound = true - @Setting("randomize-join-broadcast-sounds") - @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) - var randomBroadcastSound = true + @Setting("randomize-join-broadcast-sounds") + @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) + var randomBroadcastSound = true - @Setting("join-sounds") - @Comment("These sound(s) will be played to the joining player.\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") - var sounds = - "minecraft:entity.strider.happy,minecraft:entity.villager.ambient,minecraft:block.note_block.cow_bell" + @Setting("join-sounds") + @Comment("These sound(s) will be played to the joining player.\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") + var sounds = + "minecraft:entity.strider.happy,minecraft:entity.villager.ambient,minecraft:block.note_block.cow_bell" - @Setting("join-broadcast-sounds") - @Comment("These sound(s) will be played to the joining player.\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") - var broadcastSounds = "minecraft:entity.enderman.teleport" + @Setting("join-broadcast-sounds") + @Comment("These sound(s) will be played to the joining player.\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") + var broadcastSounds = "minecraft:entity.enderman.teleport" - @Setting("join-messages") - @Comment("These messages will be sent to the joining Player. These messages are sometimes called a \"Message of the Day\" or a \"MotD\"") - val messages = arrayListOf( - "Username: {user}'>{nick} joined the game", - "
Welcome, {user}!", - "
------------------------------------", - "This server is using AnnouncerPlus!", - "Configure these messages by editing the config files!" - ) + @Setting("join-messages") + @Comment("These messages will be sent to the joining Player. These messages are sometimes called a \"Message of the Day\" or a \"MotD\"") + val messages = arrayListOf( + "Username: {user}'>{nick} joined the game", + "
Welcome, {user}!", + "
------------------------------------", + "This server is using AnnouncerPlus!", + "Configure these messages by editing the config files!" + ) - @Setting("join-broadcasts") - @Comment("These messages will be sent to every Player online except the joining Player. Also known as join messages.") - val broadcasts = - arrayListOf("Username: {user}'>{nick} joined the game") + @Setting("join-broadcasts") + @Comment("These messages will be sent to every Player online except the joining Player. Also known as join messages.") + val broadcasts = + arrayListOf("Username: {user}'>{nick} joined the game") - @Setting("join-commands") - @Comment("These commands will be run by the console on Player join.\n Example: \"minecraft:give %player_name% dirt\"") - val commands = arrayListOf() + @Setting("join-commands") + @Comment("These commands will be run by the console on Player join.\n Example: \"minecraft:give %player_name% dirt\"") + val commands = arrayListOf() - @Setting("as-player-join-commands") - @Comment("These commands will be run as the Player on Player join.\n Example: \"ap about\"") - val asPlayerCommands = arrayListOf() + @Setting("as-player-join-commands") + @Comment("These commands will be run as the Player on Player join.\n Example: \"ap about\"") + val asPlayerCommands = arrayListOf() - @Setting("title-settings") - @Comment("Settings relating to showing a title to the joining Player") - var title = TitleSettings( - 1, 7, 1, - "Welcome{animate:flash:!:!!:!!!:10}", - "<{animate:pulse:red:blue:yellow:green:10}>{user}" - ) + @Setting("title-settings") + @Comment("Settings relating to showing a title to the joining Player") + var title = TitleSettings( + 1, 7, 1, + "Welcome{animate:flash:!:!!:!!!:10}", + "<{animate:pulse:red:blue:yellow:green:10}>{user}" + ) - @Setting("action-bar-settings") - @Comment("Settings relating to showing an Action Bar to the joining Player") - var actionBar = ActionBarSettings( - false, 8, - "|||||||||||||||||||||||||||||||||||||||" - ) + @Setting("action-bar-settings") + @Comment("Settings relating to showing an Action Bar to the joining Player") + var actionBar = ActionBarSettings( + false, 8, + "|||||||||||||||||||||||||||||||||||||||" + ) - @Setting("boss-bar-settings") - @Comment("Settings relating to showing a Boss Bar to the joining Player") - var bossBar = BossBarSettings() + @Setting("boss-bar-settings") + @Comment("Settings relating to showing a Boss Bar to the joining Player") + var bossBar = BossBarSettings() - @Setting("toast-settings") - @Comment("Configure the Toast that will be showed to the joining player") - var toast = ToastSettings( - Material.DIAMOND, ToastSettings.FrameType.CHALLENGE, - "AnnouncerPlus", "Welcome to the server!" - ) + @Setting("toast-settings") + @Comment("Configure the Toast that will be showed to the joining player") + var toast = ToastSettings( + Material.DIAMOND, ToastSettings.FrameType.CHALLENGE, + "AnnouncerPlus", "Welcome to the server!" + ) - fun messageElements(): Collection = ImmutableSet.of( - actionBar, - bossBar, - title, - toast - ) - } + fun messageElements(): Collection = ImmutableSet.of( + actionBar, + bossBar, + title, + toast + ) + } - @ConfigSerializable - class QuitSection { - @Setting("randomize-quit-sounds") - @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) - var randomSound = true + @ConfigSerializable + class QuitSection { + @Setting("randomize-quit-sounds") + @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) + var randomSound = true - @Setting("quit-sounds") - @Comment("These sound(s) will be played to online players on player quit\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") - var sounds = "minecraft:entity.enderman.teleport" + @Setting("quit-sounds") + @Comment("These sound(s) will be played to online players on player quit\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") + var sounds = "minecraft:entity.enderman.teleport" - @Setting("quit-broadcasts") - @Comment("These messages will be sent to online players on player quit. Also known as quit messages") - val broadcasts = - arrayListOf("Username: {user}'>{nick} left the game") + @Setting("quit-broadcasts") + @Comment("These messages will be sent to online players on player quit. Also known as quit messages") + val broadcasts = + arrayListOf("Username: {user}'>{nick} left the game") - @Setting("quit-commands") - @Comment("These commands will be run by the console on Player quit.\n Example: \"broadcast %player_name% left\"") - val commands = arrayListOf() - } + @Setting("quit-commands") + @Comment("These commands will be run by the console on Player quit.\n Example: \"broadcast %player_name% left\"") + val commands = arrayListOf() + } - companion object { - private val MAPPER = ObjectMapper.factoryBuilder().addNodeResolver(NodeResolver.onlyWithSetting()).build() - .get(JoinQuitConfig::class.java) + companion object { + private val MAPPER = ObjectMapper.factoryBuilder().addNodeResolver(NodeResolver.onlyWithSetting()).build() + .get(JoinQuitConfig::class.java) - fun loadFrom(node: CommentedConfigurationNode, name: String?): JoinQuitConfig = - MAPPER.load(node).populate(name) - } + fun loadFrom(node: CommentedConfigurationNode, name: String?): JoinQuitConfig = + MAPPER.load(node).populate(name) + } - fun saveTo(node: CommentedConfigurationNode) = - MAPPER.save(this, node) + fun saveTo(node: CommentedConfigurationNode) = + MAPPER.save(this, node) - fun populate(name: String?): JoinQuitConfig = - this.apply { this.name = name } + fun populate(name: String?): JoinQuitConfig = + this.apply { this.name = name } - private val announcerPlus: AnnouncerPlus by inject() - private val chat: Chat by inject() - private var name: String? = null + private val announcerPlus: AnnouncerPlus by inject() + private val chat: Chat by inject() + private var name: String? = null - fun onJoin(player: Player) { - /* name is null for first-join config */ - if (name != null && !player.hasPermission("announcerplus.join.$name")) return - chat.send(player, announcerPlus.configManager.parse(player, join.messages)) - announcerPlus.runSync(3L) { - if (!isVanished(player)) { - val onlinePlayers = ImmutableList.copyOf(Bukkit.getOnlinePlayers()) - announcerPlus.runAsync { - onlinePlayers.forEach { onlinePlayer -> - if (onlinePlayer.name != player.name) { - if (announcerPlus.perms!!.playerHas(onlinePlayer, permission) || permission.isEmpty()) { - chat.send(onlinePlayer, announcerPlus.configManager.parse(player, join.broadcasts)) - if (join.broadcastSounds != "") { - chat.playSounds(onlinePlayer, join.randomBroadcastSound, join.broadcastSounds) - } - } - } - } - } - join.commands.forEach { dispatchCommandAsConsole(announcerPlus.configManager.parse(player, it)) } - join.asPlayerCommands.forEach { player.performCommand(announcerPlus.configManager.parse(player, it)) } - } - } - announcerPlus.runAsync(if (announcerPlus.majorMinecraftVersion <= 12) 5L else 0L) { - join.messageElements().forEach { it.displayIfEnabled(player) } - if (join.sounds != "") { - chat.playSounds(player, join.randomSound, join.sounds) - } - } - } - - fun onQuit(player: Player) { - if (name == null || !player.hasPermission("announcerplus.quit.$name") || isVanished(player)) return - ImmutableList.copyOf(Bukkit.getOnlinePlayers()).forEach { onlinePlayer -> + fun onJoin(player: Player) { + /* name is null for first-join config */ + if (name != null && !player.hasPermission("announcerplus.join.$name")) return + chat.send(player, announcerPlus.configManager.parse(player, join.messages)) + announcerPlus.runSync(3L) { + if (!isVanished(player)) { + val onlinePlayers = ImmutableList.copyOf(Bukkit.getOnlinePlayers()) + announcerPlus.runAsync { + onlinePlayers.forEach { onlinePlayer -> if (onlinePlayer.name != player.name) { - if (announcerPlus.perms!!.playerHas(onlinePlayer, permission) || permission.isEmpty()) { - chat.send(onlinePlayer, announcerPlus.configManager.parse(player, quit.broadcasts)) - if (quit.sounds.isNotEmpty()) { - chat.playSounds(onlinePlayer, quit.randomSound, quit.sounds) - } + if (announcerPlus.perms!!.playerHas(onlinePlayer, permission) || permission.isEmpty()) { + chat.send(onlinePlayer, announcerPlus.configManager.parse(player, join.broadcasts)) + if (join.broadcastSounds != "") { + chat.playSounds(onlinePlayer, join.randomBroadcastSound, join.broadcastSounds) } + } } + } } - quit.commands.forEach { dispatchCommandAsConsole(announcerPlus.configManager.parse(player, it)) } + join.commands.forEach { dispatchCommandAsConsole(announcerPlus.configManager.parse(player, it)) } + join.asPlayerCommands.forEach { player.performCommand(announcerPlus.configManager.parse(player, it)) } + } } + announcerPlus.runAsync(if (Environment.majorMinecraftVersion() <= 12) 5L else 0L) { + join.messageElements().forEach { it.displayIfEnabled(player) } + if (join.sounds != "") { + chat.playSounds(player, join.randomSound, join.sounds) + } + } + } - private fun isVanished(player: Player): Boolean { - for (meta in player.getMetadata("vanished")) { - if (meta.asBoolean()) return true + fun onQuit(player: Player) { + if (name == null || !player.hasPermission("announcerplus.quit.$name") || isVanished(player)) return + ImmutableList.copyOf(Bukkit.getOnlinePlayers()).forEach { onlinePlayer -> + if (onlinePlayer.name != player.name) { + if (announcerPlus.perms!!.playerHas(onlinePlayer, permission) || permission.isEmpty()) { + chat.send(onlinePlayer, announcerPlus.configManager.parse(player, quit.broadcasts)) + if (quit.sounds.isNotEmpty()) { + chat.playSounds(onlinePlayer, quit.randomSound, quit.sounds) + } } - if (announcerPlus.essentials != null) { - return announcerPlus.essentials!!.isVanished(player) - } - return false + } } + quit.commands.forEach { dispatchCommandAsConsole(announcerPlus.configManager.parse(player, it)) } + } + + private fun isVanished(player: Player): Boolean { + for (meta in player.getMetadata("vanished")) { + if (meta.asBoolean()) return true + } + if (announcerPlus.essentials != null) { + return announcerPlus.essentials!!.isVanished(player) + } + return false + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/MainConfig.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/MainConfig.kt index d7067de..9109680 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/MainConfig.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/MainConfig.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config import org.spongepowered.configurate.CommentedConfigurationNode @@ -8,73 +31,80 @@ import org.spongepowered.configurate.objectmapping.meta.Comment @ConfigSerializable class MainConfig { - @Comment("Here you can define custom placeholders for use in plugin messages\n These placeholders can be used like \"{placeholder}\", i.e. \"{nick}\" or \"{r}rainbow text{rc}\"") - val customPlaceholders = hashMapOf( - "nick" to "%essentials_nickname%", - "user" to "%player_name%", - "prefix1" to "[!]", - "r" to "", - "rc" to "" + @Comment("Here you can define custom placeholders for use in plugin messages\n These placeholders can be used like \"{placeholder}\", i.e. \"{nick}\" or \"{r}rainbow text{rc}\"") + val customPlaceholders = hashMapOf( + "nick" to "%essentials_nickname%", + "user" to "%player_name%", + "prefix1" to "[!]", + "r" to "", + "rc" to "" + ) + + @Comment("This setting enables or disables all timed broadcasts") + var enableBroadcasts = true + + @Comment("This setting enables or disables all Join event features") + var joinFeatures = true + + @Comment("This setting enables or disables all Quit event features") + var quitFeatures = true + + @Comment( + "This setting enables or disables the first-join.conf\n" + + "If enabled, on a player's first join the first-join.conf will be used instead of any other join configs." + ) + var firstJoinConfigEnabled = false + + @Comment( + "Here you can define randomized join configs.\n" + + " To assign randomized join configs, give the announcerplus.randomjoin.demo permission, replacing demo with your randomized config\n" + + " NOTE: The randomized config named 'demo' will be ignored by the plugin. You must choose a new name to use this feature.\n" + + " WARNING: If you are OP make sure to negate the appropriate permissions so that you do not get duplicate join/quit messages" + ) + val randomJoinConfigs = hashMapOf( + "demo" to arrayListOf( + JoinQuitPair("default", 0.1), + JoinQuitPair("default", 0.2) ) + ) - @Comment("This setting enables or disables all timed broadcasts") - var enableBroadcasts = true - - @Comment("This setting enables or disables all Join event features") - var joinFeatures = true - - @Comment("This setting enables or disables all Quit event features") - var quitFeatures = true - - @Comment("This setting enables or disables the first-join.conf\n" + - "If enabled, on a player's first join the first-join.conf will be used instead of any other join configs.") - var firstJoinConfigEnabled = false - - @Comment("Here you can define randomized join configs.\n" + - " To assign randomized join configs, give the announcerplus.randomjoin.demo permission, replacing demo with your randomized config\n" + - " WARNING: If you are OP make sure to negate the appropriate permissions so that you do not get duplicate join/quit messages") - val randomJoinConfigs = hashMapOf( - "demo" to arrayListOf( - JoinQuitPair("default", 0.1), - JoinQuitPair("default", 0.2) - ) + @Comment( + "Here you can define randomized quit configs.\n" + + " To assign randomized quit configs, give the announcerplus.randomquit.demo permission, replacing demo with your randomized config\n" + + " NOTE: The randomized config named 'demo' will be ignored by the plugin. You must choose a new name to use this feature.\n" + + " WARNING: If you are OP make sure to negate the appropriate permissions so that you do not get duplicate join/quit messages" + ) + val randomQuitConfigs = hashMapOf( + "demo" to arrayListOf( + JoinQuitPair("default", 0.2), + JoinQuitPair("default", 0.1) ) + ) - @Comment("Here you can define randomized join configs.\n" + - " To assign randomized quit configs, give the announcerplus.randomquit.demo permission, replacing demo with your randomized config\n" + - " NOTE: The randomized config named 'demo' will be ignored by the plugin. You must choose a new name to use this feature.\n" + - " WARNING: If you are OP make sure to negate the appropriate permissions so that you do not get duplicate join/quit messages") - val randomQuitConfigs = hashMapOf( - "demo" to arrayListOf( - JoinQuitPair("default", 0.2), - JoinQuitPair("default", 0.1) - ) - ) - - @ConfigSerializable - class JoinQuitPair { - constructor() - constructor(configName: String, weight: Double) { - this.configName = configName - this.weight = weight - } - - @Comment("The name of the config (the text before .conf in the file name)") - var configName = "default" - - @Comment("The weight of this config for random selection, 0.0-1.0") - var weight = 0.1 + @ConfigSerializable + class JoinQuitPair { + constructor() + constructor(configName: String, weight: Double) { + this.configName = configName + this.weight = weight } - companion object { - private val MAPPER = ObjectMapper.factory().get(MainConfig::class.java) + @Comment("The name of the config (the text before .conf in the file name)") + var configName = "default" - fun loadFrom(node: CommentedConfigurationNode): MainConfig { - return MAPPER.load(node) - } - } + @Comment("The weight of this config for random selection, 0.0-1.0") + var weight = 0.1 + } - fun saveTo(node: CommentedConfigurationNode) { - MAPPER.save(this, node) + companion object { + private val MAPPER = ObjectMapper.factory().get(MainConfig::class.java) + + fun loadFrom(node: CommentedConfigurationNode): MainConfig { + return MAPPER.load(node) } + } + + fun saveTo(node: CommentedConfigurationNode) { + MAPPER.save(this, node) + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ActionBarSettings.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ActionBarSettings.kt index cbd6119..0eb6aae 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ActionBarSettings.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ActionBarSettings.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import org.bukkit.entity.Player @@ -8,27 +31,27 @@ import xyz.jpenilla.announcerplus.task.ActionBarUpdateTask @ConfigSerializable class ActionBarSettings : MessageElement { - @Comment("Seconds of duration for the Action Bar to stay on screen") - var durationSeconds = 6 + @Comment("Seconds of duration for the Action Bar to stay on screen") + var durationSeconds = 6 - @Comment("Should the fade out animation of the Action Bar be enabled?") - var enableFadeOut = false + @Comment("Should the fade out animation of the Action Bar be enabled?") + var enableFadeOut = false - @Comment("The text for the Action Bar. Set to \"\" (empty string) to disable. Accepts animations") - var text = "" + @Comment("The text for the Action Bar. Set to \"\" (empty string) to disable. Accepts animations") + var text = "" - constructor() - constructor(fadeEnabled: Boolean, durationSeconds: Int, text: String) { - this.enableFadeOut = fadeEnabled - this.durationSeconds = durationSeconds - this.text = text - } + constructor() + constructor(fadeEnabled: Boolean, durationSeconds: Int, text: String) { + this.enableFadeOut = fadeEnabled + this.durationSeconds = durationSeconds + this.text = text + } - override fun isEnabled(): Boolean { - return text != "" - } + override fun isEnabled(): Boolean { + return text != "" + } - override fun display(player: Player) { - ActionBarUpdateTask(player, durationSeconds * 20L, enableFadeOut, text).start() - } + override fun display(player: Player) { + ActionBarUpdateTask(player, durationSeconds * 20L, enableFadeOut, text).start() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/BossBarSettings.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/BossBarSettings.kt index d737e24..f656073 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/BossBarSettings.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/BossBarSettings.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import net.kyori.adventure.bossbar.BossBar @@ -9,34 +32,36 @@ import xyz.jpenilla.announcerplus.task.BossBarUpdateTask @ConfigSerializable class BossBarSettings : MessageElement { - @Comment("Seconds of duration for the Boss Bar to stay on screen") - var durationSeconds = 12 + @Comment("Seconds of duration for the Boss Bar to stay on screen") + var durationSeconds = 12 - @Comment("The text for the Boss Bar. Set to \"\" (empty string) to disable. Accepts animations") - var text = "" + @Comment("The text for the Boss Bar. Set to \"\" (empty string) to disable. Accepts animations") + var text = "" - @Comment("The color for the Boss Bar. For a list of colors, visit: https://papermc.io/javadocs/paper/1.16/org/bukkit/boss/BarColor.html\n" + - " This field technically accepts animations, although only the \"Flashing Text\" animation used with valid Boss Bar colors will actually work.") - var color = "YELLOW" + @Comment( + "The color for the Boss Bar. For a list of colors, visit: https://papermc.io/javadocs/paper/1.16/org/bukkit/boss/BarColor.html\n" + + " This field technically accepts animations, although only the \"Flashing Text\" animation used with valid Boss Bar colors will actually work." + ) + var color = "YELLOW" - @Comment("The overlay for the Boss Bar. Possible values: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20") - var overlay = BossBar.Overlay.PROGRESS + @Comment("The overlay for the Boss Bar. Possible values: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20") + var overlay = BossBar.Overlay.PROGRESS - @Comment("The fill mode for the Boss Bar. Possible modes: FILL, DRAIN, FULL, EMPTY") - var fillMode = BossBarUpdateTask.FillMode.DRAIN + @Comment("The fill mode for the Boss Bar. Possible modes: FILL, DRAIN, FULL, EMPTY") + var fillMode = BossBarUpdateTask.FillMode.DRAIN - constructor() - constructor(durationSeconds: Int, color: String, text: String) { - this.durationSeconds = durationSeconds - this.color = color - this.text = text - } + constructor() + constructor(durationSeconds: Int, color: String, text: String) { + this.durationSeconds = durationSeconds + this.color = color + this.text = text + } - override fun isEnabled(): Boolean { - return text != "" - } + override fun isEnabled(): Boolean { + return text != "" + } - override fun display(player: Player) { - BossBarUpdateTask(player, durationSeconds, overlay, fillMode, color, text).start() - } + override fun display(player: Player) { + BossBarUpdateTask(player, durationSeconds, overlay, fillMode, color, text).start() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/Message.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/Message.kt index 93d4066..f6daa59 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/Message.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/Message.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import com.google.common.collect.ImmutableSet @@ -8,56 +31,56 @@ import xyz.jpenilla.announcerplus.util.Constants @ConfigSerializable class Message { - @Comment("The lines of text for this message. Can be empty for no chat messages.") - val messageText = arrayListOf() + @Comment("The lines of text for this message. Can be empty for no chat messages.") + val messageText = arrayListOf() - @Comment("Configure the Action Bar for this message") - var actionBar = ActionBarSettings() + @Comment("Configure the Action Bar for this message") + var actionBar = ActionBarSettings() - @Comment("Configure the Boss Bar for this message") - var bossBar = BossBarSettings() + @Comment("Configure the Boss Bar for this message") + var bossBar = BossBarSettings() - @Comment("Configure the Title for this message") - var title = TitleSettings() + @Comment("Configure the Title for this message") + var title = TitleSettings() - @Comment("Configure the Toast/Achievement/Advancement for this message") - var toast = ToastSettings() + @Comment("Configure the Toast/Achievement/Advancement for this message") + var toast = ToastSettings() - @Comment("The sounds to play when this message is sent\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") - var sounds = "" + @Comment("The sounds to play when this message is sent\n ${Constants.CONFIG_COMMENT_SOUNDS_LINE2}") + var sounds = "" - @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) - var soundsRandomized = true + @Comment(Constants.CONFIG_COMMENT_SOUNDS_RANDOM) + var soundsRandomized = true - @Comment("These commands will run as console on broadcast. Example: \"broadcast This is a test\"") - val commands = arrayListOf() + @Comment("These commands will run as console on broadcast. Example: \"broadcast This is a test\"") + val commands = arrayListOf() - @Comment("These commands will run as console once per player on broadcast. Example: \"minecraft:give %player_name% dirt\"") - val perPlayerCommands = arrayListOf() + @Comment("These commands will run as console once per player on broadcast. Example: \"minecraft:give %player_name% dirt\"") + val perPlayerCommands = arrayListOf() - @Comment("These commands will run once per player, as the player on broadcast. Example: \"ap about\"") - val asPlayerCommands = arrayListOf() + @Comment("These commands will run once per player, as the player on broadcast. Example: \"ap about\"") + val asPlayerCommands = arrayListOf() - constructor() - constructor(text: List) { - this.messageText.addAll(text) - } + constructor() + constructor(text: List) { + this.messageText.addAll(text) + } - fun messageElements(): Collection = ImmutableSet.of( - actionBar, - bossBar, - title, - toast - ) + fun messageElements(): Collection = ImmutableSet.of( + actionBar, + bossBar, + title, + toast + ) - fun sounds(sounds: String): Message = apply { this.sounds = sounds } + fun sounds(sounds: String): Message = apply { this.sounds = sounds } - fun actionBar(actionBar: ActionBarSettings): Message = apply { this.actionBar = actionBar } + fun actionBar(actionBar: ActionBarSettings): Message = apply { this.actionBar = actionBar } - fun bossBar(bossBar: BossBarSettings): Message = apply { this.bossBar = bossBar } + fun bossBar(bossBar: BossBarSettings): Message = apply { this.bossBar = bossBar } - fun title(title: TitleSettings): Message = apply { this.title = title } + fun title(title: TitleSettings): Message = apply { this.title = title } - fun toast(toast: ToastSettings): Message = apply { this.toast = toast } + fun toast(toast: ToastSettings): Message = apply { this.toast = toast } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageConfig.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageConfig.kt index 4546991..839eedb 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageConfig.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageConfig.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import com.google.common.collect.ImmutableList @@ -23,134 +46,164 @@ import xyz.jpenilla.jmplib.Chat @ConfigSerializable class MessageConfig : KoinComponent { - @Setting - @Comment("The list of messages for a config") - val messages = arrayListOf( - Message(arrayListOf("
Test AnnouncerPlus broadcast!")), - Message().bossBar(BossBarSettings(25, "{animate:flash:YELLOW:PURPLE:40}", - "-| {animate:scrolltext:Hello this is an example Boss Bar announcement:20:3} |-")), - Message(arrayListOf( - "{prefix1} 1. Multi-line test AnnouncerPlus broadcast", - "{prefix1} 2. Line number two of three", - "{prefix1} 3. this is the last line (line 3)")) - .toast(ToastSettings(Material.DIAMOND, ToastSettings.FrameType.CHALLENGE, - "AnnouncerPlus", "This is a Toast message!")), - Message(arrayListOf("{prefix1} Test AnnouncerPlus broadcast with sound!")) - .sounds("minecraft:entity.strider.happy,minecraft:entity.villager.ambient,minecraft:block.note_block.cow_bell"), - Message(arrayListOf("{prefix1} Use Click to run!'>/ap about to check the plugin version")) - .actionBar(ActionBarSettings(true, 15, "<{animate:randomcolor:pulse:25}>-| {animate:scrolltext:Hello there this is some very long text being displayed in a scrolling window!! =):20:3} |-")), - Message(arrayListOf("Hello, {nick} {prefix1} {r}!!!!!!!!!{rc}")) - .title(TitleSettings(1, 13, 2, "||||||||||||||||||||||||||||||||||||||||||||", "<{animate:pulse:red:blue:10}>{animate:type:This is a test... typing...:6}")), - Message(arrayListOf("
Centered text Example")) - .bossBar(BossBarSettings(25, "PINK", - "This is an example Boss Bar")) + @Setting + @Comment("The list of messages for a config") + val messages = arrayListOf( + Message(arrayListOf("
Test AnnouncerPlus broadcast!")), + Message().bossBar( + BossBarSettings( + 25, "{animate:flash:YELLOW:PURPLE:40}", + "-| {animate:scrolltext:Hello this is an example Boss Bar announcement:20:3} |-" + ) + ), + Message( + arrayListOf( + "{prefix1} 1. Multi-line test AnnouncerPlus broadcast", + "{prefix1} 2. Line number two of three", + "{prefix1} 3. this is the last line (line 3)" + ) ) + .toast( + ToastSettings( + Material.DIAMOND, ToastSettings.FrameType.CHALLENGE, + "AnnouncerPlus", "This is a Toast message!" + ) + ), + Message(arrayListOf("{prefix1} Test AnnouncerPlus broadcast with sound!")) + .sounds("minecraft:entity.strider.happy,minecraft:entity.villager.ambient,minecraft:block.note_block.cow_bell"), + Message(arrayListOf("{prefix1} Use Click to run!'>/ap about to check the plugin version")) + .actionBar( + ActionBarSettings( + true, + 15, + "<{animate:randomcolor:pulse:25}>-| {animate:scrolltext:Hello there this is some very long text being displayed in a scrolling window!! =):20:3} |-" + ) + ), + Message(arrayListOf("Hello, {nick} {prefix1} {r}!!!!!!!!!{rc}")) + .title( + TitleSettings( + 1, + 13, + 2, + "||||||||||||||||||||||||||||||||||||||||||||", + "<{animate:pulse:red:blue:10}>{animate:type:This is a test... typing...:6}" + ) + ), + Message(arrayListOf("
Centered text Example")) + .bossBar( + BossBarSettings( + 25, "PINK", + "This is an example Boss Bar" + ) + ) + ) - @Setting("every-broadcast-commands") - @Comment("These commands will run as console once each interval\n Example: \"broadcast This is a test\"") - val commands = arrayListOf() + @Setting("every-broadcast-commands") + @Comment("These commands will run as console once each interval\n Example: \"broadcast This is a test\"") + val commands = arrayListOf() - @Setting("every-broadcast-per-player-commands") - @Comment("These commands will run as console once per player each interval\n Example: \"minecraft:give %player_name% dirt\"") - val perPlayerCommands = arrayListOf() + @Setting("every-broadcast-per-player-commands") + @Comment("These commands will run as console once per player each interval\n Example: \"minecraft:give %player_name% dirt\"") + val perPlayerCommands = arrayListOf() - @Setting("every-broadcast-as-player-commands") - @Comment("These commands will run once per player each interval, as the player\n Example: \"ap about\"") - val asPlayerCommands = arrayListOf() + @Setting("every-broadcast-as-player-commands") + @Comment("These commands will run once per player each interval, as the player\n Example: \"ap about\"") + val asPlayerCommands = arrayListOf() - @Setting("interval-time-unit") - @Comment("The unit of time used for the interval\n Can be SECONDS, MINUTES, or HOURS") - var timeUnit = TimeUnit.MINUTES + @Setting("interval-time-unit") + @Comment("The unit of time used for the interval\n Can be SECONDS, MINUTES, or HOURS") + var timeUnit = TimeUnit.MINUTES - @Setting("interval-time-amount") - @Comment("The amount of time used for the interval") - var interval = 3 + @Setting("interval-time-amount") + @Comment("The amount of time used for the interval") + var interval = 3 - @Setting("random-message-order") - @Comment("Should the messages be sent in order of the config or in random order") - var randomOrder = false + @Setting("random-message-order") + @Comment("Should the messages be sent in order of the config or in random order") + var randomOrder = false - companion object { - private val MAPPER = ObjectMapper.factoryBuilder().addNodeResolver(NodeResolver.onlyWithSetting()).build() - .get(MessageConfig::class.java) + companion object { + private val MAPPER = ObjectMapper.factoryBuilder().addNodeResolver(NodeResolver.onlyWithSetting()).build() + .get(MessageConfig::class.java) - fun loadFrom(node: CommentedConfigurationNode, name: String): MessageConfig = - MAPPER.load(node).populate(name) + fun loadFrom(node: CommentedConfigurationNode, name: String): MessageConfig = + MAPPER.load(node).populate(name) + } + + fun saveTo(node: CommentedConfigurationNode) { + MAPPER.save(this, node) + } + + fun populate(name: String): MessageConfig = + this.apply { this.name = name } + + private var broadcastTask: BukkitTask? = null + lateinit var name: String + private val announcerPlus: AnnouncerPlus by inject() + private val configManager: ConfigManager by inject() + private val chat: Chat by inject() + private val broadcastQueue = ArrayDeque() + + fun broadcast() { + stop() + broadcastQueue.clear() + broadcastQueue.addAll(shuffledMessages()) + broadcastTask = announcerPlus.asyncTimer(0L, timeUnit.getTicks(interval)) { + if (broadcastQueue.isNotEmpty()) { + broadcast(broadcastQueue.removeFirst()) + } else { + broadcast() + } } + } - fun saveTo(node: CommentedConfigurationNode) = - MAPPER.save(this, node) - - fun populate(name: String): MessageConfig = - this.apply { this.name = name } - - private var broadcastTask: BukkitTask? = null - lateinit var name: String - private val announcerPlus: AnnouncerPlus by inject() - private val configManager: ConfigManager by inject() - private val chat: Chat by inject() - private val broadcastQueue = ArrayDeque() - - fun broadcast() { - stop() - broadcastQueue.clear() - broadcastQueue.addAll(shuffledMessages()) - broadcastTask = announcerPlus.asyncTimer(0L, timeUnit.getTicks(interval)) { - if (broadcastQueue.isNotEmpty()) { - broadcast(broadcastQueue.removeFirst()) - } else { - broadcast() - } + private fun broadcast(message: Message) { + val onlinePlayers = announcerPlus.getOnMain { ImmutableList.copyOf(Bukkit.getOnlinePlayers()) } + for (onlinePlayer in onlinePlayers) { + if (announcerPlus.essentials != null) { + if (announcerPlus.essentials!!.isAfk(onlinePlayer) && announcerPlus.perms!!.playerHas(onlinePlayer, "${announcerPlus.name}.messages.$name.afk")) { + continue } - } - - private fun broadcast(message: Message) { - val onlinePlayers = announcerPlus.getOnMain { ImmutableList.copyOf(Bukkit.getOnlinePlayers()) } - for (onlinePlayer in onlinePlayers) { - if (announcerPlus.essentials != null) { - if (announcerPlus.essentials!!.isAfk(onlinePlayer) && announcerPlus.perms!!.playerHas(onlinePlayer, "${announcerPlus.name}.messages.$name.afk")) { - continue - } - } - if (announcerPlus.perms!!.playerHas(onlinePlayer, "${announcerPlus.name}.messages.$name")) { - with(message) { - if (messageText.size != 0) { - chat.send(onlinePlayer, configManager.parse(onlinePlayer, messageText)) - } - chat.playSounds(onlinePlayer, soundsRandomized, sounds) - messageElements().forEach { it.displayIfEnabled(onlinePlayer) } - } - announcerPlus.runSync { - message.perPlayerCommands.forEach { dispatchCommandAsConsole(configManager.parse(onlinePlayer, it)) } - message.asPlayerCommands.forEach { onlinePlayer.performCommand(configManager.parse(onlinePlayer, it)) } - perPlayerCommands.forEach { dispatchCommandAsConsole(configManager.parse(onlinePlayer, it)) } - asPlayerCommands.forEach { onlinePlayer.performCommand(configManager.parse(onlinePlayer, it)) } - } - } + } + if (announcerPlus.perms!!.playerHas(onlinePlayer, "${announcerPlus.name}.messages.$name")) { + with(message) { + if (messageText.size != 0) { + chat.send(onlinePlayer, configManager.parse(onlinePlayer, messageText)) + } + chat.playSounds(onlinePlayer, soundsRandomized, sounds) + messageElements().forEach { it.displayIfEnabled(onlinePlayer) } } announcerPlus.runSync { - message.commands.forEach { dispatchCommandAsConsole(configManager.parse(null, it)) } - commands.forEach { dispatchCommandAsConsole(configManager.parse(null, it)) } + message.perPlayerCommands.forEach { dispatchCommandAsConsole(configManager.parse(onlinePlayer, it)) } + message.asPlayerCommands.forEach { onlinePlayer.performCommand(configManager.parse(onlinePlayer, it)) } + perPlayerCommands.forEach { dispatchCommandAsConsole(configManager.parse(onlinePlayer, it)) } + asPlayerCommands.forEach { onlinePlayer.performCommand(configManager.parse(onlinePlayer, it)) } } + } } - - private fun shuffledMessages(): List { - if (randomOrder) { - return messages.shuffled() - } - return messages + announcerPlus.runSync { + message.commands.forEach { dispatchCommandAsConsole(configManager.parse(null, it)) } + commands.forEach { dispatchCommandAsConsole(configManager.parse(null, it)) } } + } - fun stop() { - broadcastTask?.cancel() + private fun shuffledMessages(): List { + if (randomOrder) { + return messages.shuffled() } + return messages + } - enum class TimeUnit(val ticks: Long) { - SECONDS(20L), - MINUTES(1200L), - HOURS(72000L); + fun stop() { + broadcastTask?.cancel() + } - fun getTicks(units: Int): Long = - ticks * units - } + enum class TimeUnit(val ticks: Long) { + SECONDS(20L), + MINUTES(1200L), + HOURS(72000L); + + fun getTicks(units: Int): Long = + ticks * units + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageElement.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageElement.kt index 8c93640..99058ee 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageElement.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/MessageElement.kt @@ -1,15 +1,38 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import org.bukkit.entity.Player import org.koin.core.KoinComponent interface MessageElement : KoinComponent { - fun isEnabled(): Boolean - fun display(player: Player) + fun isEnabled(): Boolean + fun display(player: Player) - fun displayIfEnabled(player: Player) { - if (isEnabled()) { - display(player) - } + fun displayIfEnabled(player: Player) { + if (isEnabled()) { + display(player) } + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/TitleSettings.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/TitleSettings.kt index 6f11246..a8ae530 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/TitleSettings.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/TitleSettings.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import org.bukkit.entity.Player @@ -7,39 +30,39 @@ import xyz.jpenilla.announcerplus.task.TitleUpdateTask @ConfigSerializable class TitleSettings : MessageElement { - constructor() - constructor(fadeInSeconds: Int, durationSeconds: Int, fadeOutSeconds: Int, title: String, subTitle: String) { - this.fadeInSeconds = fadeInSeconds - this.fadeOutSeconds = fadeOutSeconds - this.title = title - this.subtitle = subTitle - this.durationSeconds = durationSeconds + constructor() + constructor(fadeInSeconds: Int, durationSeconds: Int, fadeOutSeconds: Int, title: String, subTitle: String) { + this.fadeInSeconds = fadeInSeconds + this.fadeOutSeconds = fadeOutSeconds + this.title = title + this.subtitle = subTitle + this.durationSeconds = durationSeconds + } + + @Comment("Seconds of duration for the title fade-in animation") + var fadeInSeconds = 1 + + @Comment("Seconds of duration for the title to stay on screen") + var durationSeconds = 5 + + @Comment("Seconds of duration for the title fade-out animation") + var fadeOutSeconds = 1 + + @Comment("Title text. If the title and subtitle are both set to \"\" (empty string), then this title is disabled") + var title = "" + + @Comment("Subtitle text. If the title and subtitle are both set to \"\" (empty string), then this title is disabled") + var subtitle = "" + + override fun isEnabled(): Boolean { + return if (fadeInSeconds == 0 && durationSeconds == 0 && fadeOutSeconds == 0) { + false + } else { + title != "" || subtitle != "" } + } - @Comment("Seconds of duration for the title fade-in animation") - var fadeInSeconds = 1 - - @Comment("Seconds of duration for the title to stay on screen") - var durationSeconds = 5 - - @Comment("Seconds of duration for the title fade-out animation") - var fadeOutSeconds = 1 - - @Comment("Title text. If the title and subtitle are both set to \"\" (empty string), then this title is disabled") - var title = "" - - @Comment("Subtitle text. If the title and subtitle are both set to \"\" (empty string), then this title is disabled") - var subtitle = "" - - override fun isEnabled(): Boolean { - return if (fadeInSeconds == 0 && durationSeconds == 0 && fadeOutSeconds == 0) { - false - } else { - title != "" || subtitle != "" - } - } - - override fun display(player: Player) { - TitleUpdateTask(player, fadeInSeconds, durationSeconds, fadeOutSeconds, title, subtitle).start() - } + override fun display(player: Player) { + TitleUpdateTask(player, fadeInSeconds, durationSeconds, fadeOutSeconds, title, subtitle).start() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ToastSettings.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ToastSettings.kt index c26aeaf..e6cb98e 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ToastSettings.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/config/message/ToastSettings.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.config.message import com.google.gson.JsonObject @@ -10,91 +33,92 @@ import org.koin.core.inject import org.spongepowered.configurate.objectmapping.ConfigSerializable import org.spongepowered.configurate.objectmapping.meta.Comment import xyz.jpenilla.announcerplus.AnnouncerPlus +import xyz.jpenilla.jmplib.Environment @ConfigSerializable class ToastSettings : MessageElement { - @Comment("The icon for the Toast/Advancement notification") - var icon = Material.DIAMOND + @Comment("The icon for the Toast/Advancement notification") + var icon = Material.DIAMOND - @Comment("Should the icon item be enchanted?") - var iconEnchanted = false + @Comment("Should the icon item be enchanted?") + var iconEnchanted = false - @Comment("Enter custom model data for the icon item. -1 to disable") - var iconCustomModelData = -1 + @Comment("Enter custom model data for the icon item. -1 to disable") + var iconCustomModelData = -1 - @Comment("The text for the header of the Toast. If this and the footer are set to \"\" (empty string), the toast is disabled") - var header = "" + @Comment("The text for the header of the Toast. If this and the footer are set to \"\" (empty string), the toast is disabled") + var header = "" - @Comment("The text for the footer of the Toast. If this and the header are set to \"\" (empty string), the toast is disabled") - var footer = "" + @Comment("The text for the footer of the Toast. If this and the header are set to \"\" (empty string), the toast is disabled") + var footer = "" - @Comment("The frame for the Toast. Can be CHALLENGE, GOAL, or TASK") - var frame = FrameType.GOAL + @Comment("The frame for the Toast. Can be CHALLENGE, GOAL, or TASK") + var frame = FrameType.GOAL - constructor() - constructor(icon: Material, frameType: FrameType, header: String, footer: String) { - this.icon = icon - this.frame = frameType - this.header = header - this.footer = footer + constructor() + constructor(icon: Material, frameType: FrameType, header: String, footer: String) { + this.icon = icon + this.frame = frameType + this.header = header + this.footer = footer + } + + private val announcerPlus: AnnouncerPlus by inject() + private val miniMessage: MiniMessage by inject() + + override fun isEnabled(): Boolean { + return header != "" || footer != "" + } + + override fun display(player: Player) { + announcerPlus.toastTask?.queueToast(this, player) + } + + fun getJson(player: Player): JsonObject { + val json = JsonObject() + val display = JsonObject() + val icon = JsonObject() + val iconString = if (Environment.majorMinecraftVersion() <= 12) this.icon.name else this.icon.key.toString() + icon.addProperty("item", iconString) + val nbtBuilder = StringBuilder("{CustomModelData:$iconCustomModelData") + if (iconEnchanted) { + nbtBuilder.append(",Enchantments:[{id:\"aqua_affinity\",lvl:1}]}") + } else { + nbtBuilder.append("}") } + icon.addProperty("nbt", nbtBuilder.toString()) + display.add("icon", icon) + val titleComponent = LinearComponents.linear( + miniMessage.parse(announcerPlus.configManager.parse(player, header)), + Component.newline(), + miniMessage.parse(announcerPlus.configManager.parse(player, footer)) + ) + val title = announcerPlus.jsonParser.parse( + if (Environment.majorMinecraftVersion() >= 16) { + announcerPlus.gsonComponentSerializer.serialize(titleComponent) + } else { + announcerPlus.downsamplingGsonComponentSerializer.serialize(titleComponent) + } + ) + display.add("title", title) + display.addProperty("description", "AnnouncerPlus Toast Description") + display.addProperty("frame", frame.value) + display.addProperty("announce_to_chat", false) + display.addProperty("show_toast", true) + display.addProperty("hidden", true) + val trigger = JsonObject() + trigger.addProperty("trigger", "minecraft:impossible") + val criteria = JsonObject() + criteria.add("impossible", trigger) + json.add("criteria", criteria) + json.add("display", display) + return json + } - private val announcerPlus: AnnouncerPlus by inject() - private val miniMessage: MiniMessage by inject() - - override fun isEnabled(): Boolean { - return header != "" || footer != "" - } - - override fun display(player: Player) { - announcerPlus.toastTask?.queueToast(this, player) - } - - fun getJson(player: Player): JsonObject { - val json = JsonObject() - val display = JsonObject() - val icon = JsonObject() - val iconString = if (announcerPlus.majorMinecraftVersion <= 12) this.icon.name else this.icon.key.toString() - icon.addProperty("item", iconString) - val nbtBuilder = StringBuilder("{CustomModelData:$iconCustomModelData") - if (iconEnchanted) { - nbtBuilder.append(",Enchantments:[{id:\"aqua_affinity\",lvl:1}]}") - } else { - nbtBuilder.append("}") - } - icon.addProperty("nbt", nbtBuilder.toString()) - display.add("icon", icon) - val titleComponent = LinearComponents.linear( - miniMessage.parse(announcerPlus.configManager.parse(player, header)), - Component.newline(), - miniMessage.parse(announcerPlus.configManager.parse(player, footer)) - ) - val title = announcerPlus.jsonParser.parse( - if (announcerPlus.majorMinecraftVersion >= 16) { - announcerPlus.gsonComponentSerializer.serialize(titleComponent) - } else { - announcerPlus.downsamplingGsonComponentSerializer.serialize(titleComponent) - } - ) - display.add("title", title) - display.addProperty("description", "AnnouncerPlus Toast Description") - display.addProperty("frame", frame.value) - display.addProperty("announce_to_chat", false) - display.addProperty("show_toast", true) - display.addProperty("hidden", true) - val trigger = JsonObject() - trigger.addProperty("trigger", "minecraft:impossible") - val criteria = JsonObject() - criteria.add("impossible", trigger) - json.add("criteria", criteria) - json.add("display", display) - return json - } - - enum class FrameType(val value: String) { - CHALLENGE("challenge"), - GOAL("goal"), - TASK("task"), - } + enum class FrameType(val value: String) { + CHALLENGE("challenge"), + GOAL("goal"), + TASK("task"), + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/task/ActionBarUpdateTask.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/task/ActionBarUpdateTask.kt index ac05f28..0b1e455 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/task/ActionBarUpdateTask.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/task/ActionBarUpdateTask.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.task import org.bukkit.entity.Player @@ -6,28 +29,28 @@ import xyz.jpenilla.announcerplus.textanimation.AnimationHolder import xyz.jpenilla.jmplib.Chat class ActionBarUpdateTask( - private val player: Player, - private val lifeTime: Long, - private val shouldFade: Boolean, - private val text: String + private val player: Player, + private val lifeTime: Long, + private val shouldFade: Boolean, + private val text: String ) : UpdateTask() { - private val chat: Chat by inject() - private val animationHolder = AnimationHolder(player, text) + private val chat: Chat by inject() + private val animationHolder = AnimationHolder(player, text) - override fun stop() { - super.stop() - if (!shouldFade) { - chat.sendActionBar(player, "") - } + override fun stop() { + super.stop() + if (!shouldFade) { + chat.sendActionBar(player, "") } + } - override fun update() { - chat.sendActionBar(player, animationHolder.parseNext(text)) - } + override fun update() { + chat.sendActionBar(player, animationHolder.parseNext(text)) + } - override fun shouldContinue(): Boolean { - return ticksLived < lifeTime && player.isOnline - } + override fun shouldContinue(): Boolean { + return ticksLived < lifeTime && player.isOnline + } - override fun synchronizationContext() = SynchronizationContext.ASYNC + override fun synchronizationContext() = SynchronizationContext.ASYNC } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/task/BossBarUpdateTask.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/task/BossBarUpdateTask.kt index 9762b2e..8d09b2b 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/task/BossBarUpdateTask.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/task/BossBarUpdateTask.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.task import net.kyori.adventure.bossbar.BossBar @@ -11,44 +34,44 @@ import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.announcerplus.textanimation.AnimationHolder class BossBarUpdateTask( - private val player: Player, - private val lifeTime: Int, - overlay: BossBar.Overlay, - private val fillMode: FillMode, - private val color: String, - private val text: String + private val player: Player, + private val lifeTime: Int, + overlay: BossBar.Overlay, + private val fillMode: FillMode, + private val color: String, + private val text: String ) : UpdateTask() { - private val configManager: ConfigManager by inject() - private val miniMessage: MiniMessage by inject() - private val audience = get().player(player) - private val textAnimation = AnimationHolder(player, text) - private val colorAnimation = AnimationHolder(player, color) - private val bar = BossBar.bossBar(Component.empty(), 0.5f, BossBar.Color.BLUE, overlay) + private val configManager: ConfigManager by inject() + private val miniMessage: MiniMessage by inject() + private val audience = get().player(player) + private val textAnimation = AnimationHolder(player, text) + private val colorAnimation = AnimationHolder(player, color) + private val bar = BossBar.bossBar(Component.empty(), 0.5f, BossBar.Color.BLUE, overlay) - override fun stop() { - super.stop() - audience.hideBossBar(bar) + override fun stop() { + super.stop() + audience.hideBossBar(bar) + } + + override fun update() { + bar.color(BossBar.Color.NAMES.value(colorAnimation.parseNext(color).toLowerCase()) ?: BossBar.Color.BLUE) + bar.name(miniMessage.parse(configManager.parse(player, textAnimation.parseNext(text)))) + when (fillMode) { + FillMode.FILL -> bar.progress(ticksLived / (lifeTime * 20f)) + FillMode.DRAIN -> bar.progress(1f - (ticksLived / (lifeTime * 20f))) + FillMode.FULL -> if (ticksLived == 0L) bar.progress(1f) + FillMode.EMPTY -> if (ticksLived == 0L) bar.progress(0f) } + if (ticksLived == 0L) audience.showBossBar(bar) + } - override fun update() { - bar.color(BossBar.Color.NAMES.value(colorAnimation.parseNext(color).toLowerCase()) ?: BossBar.Color.BLUE) - bar.name(miniMessage.parse(configManager.parse(player, textAnimation.parseNext(text)))) - when (fillMode) { - FillMode.FILL -> bar.progress(ticksLived / (lifeTime * 20f)) - FillMode.DRAIN -> bar.progress(1f - (ticksLived / (lifeTime * 20f))) - FillMode.FULL -> if (ticksLived == 0L) bar.progress(1f) - FillMode.EMPTY -> if (ticksLived == 0L) bar.progress(0f) - } - if (ticksLived == 0L) audience.showBossBar(bar) - } + override fun shouldContinue(): Boolean { + return ticksLived < lifeTime * 20L && player.isOnline + } - override fun shouldContinue(): Boolean { - return ticksLived < lifeTime * 20L && player.isOnline - } + override fun synchronizationContext() = SynchronizationContext.ASYNC - override fun synchronizationContext() = SynchronizationContext.ASYNC - - enum class FillMode { - FILL, DRAIN, FULL, EMPTY - } + enum class FillMode { + FILL, DRAIN, FULL, EMPTY + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/task/TitleUpdateTask.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/task/TitleUpdateTask.kt index c075400..137f9c1 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/task/TitleUpdateTask.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/task/TitleUpdateTask.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.task import org.bukkit.entity.Player @@ -8,42 +31,52 @@ import xyz.jpenilla.jmplib.Chat import java.time.temporal.ChronoUnit class TitleUpdateTask( - private val player: Player, - private val fadeIn: Int, - private val duration: Int, - private val fadeOut: Int, - private val title: String, - private val subTitle: String + private val player: Player, + private val fadeIn: Int, + private val duration: Int, + private val fadeOut: Int, + private val title: String, + private val subTitle: String ) : UpdateTask() { - private val chat: Chat by inject() - private val configManager: ConfigManager by inject() - private val titleAnimation = AnimationHolder(player, title) - private val subTitleAnimation = AnimationHolder(player, subTitle) + private val chat: Chat by inject() + private val configManager: ConfigManager by inject() + private val titleAnimation = AnimationHolder(player, title) + private val subTitleAnimation = AnimationHolder(player, subTitle) - override fun stop() { - super.stop() - if (fadeOut == 0) { - chat.showTitle(player, chat.getTitleSeconds("", "", 0, 0, 0)) - } else { - chat.showTitle(player, chat.getTitleSeconds(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), 0, 0, fadeOut)) - } + override fun stop() { + super.stop() + if (fadeOut != 0) { + chat.showTitle( + player, + chat.getTitleSeconds(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), 0, 0, fadeOut) + ) } + } - override fun update() { - when (fadeIn) { - 0 -> chat.showTitle(player, chat.getTitle(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), ChronoUnit.SECONDS, 0, ChronoUnit.MILLIS, 200, ChronoUnit.SECONDS, 0)) - else -> when (ticksLived) { - 0L -> chat.showTitle(player, chat.getTitle(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), ChronoUnit.SECONDS, fadeIn, ChronoUnit.MILLIS, 200, ChronoUnit.SECONDS, 0)) - else -> if (ticksLived > fadeIn * 20L) { - chat.showTitle(player, chat.getTitle(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), ChronoUnit.SECONDS, 0, ChronoUnit.MILLIS, 200, ChronoUnit.SECONDS, 0)) - } - } - } + override fun update() { + if (fadeIn == 0) { + chat.showTitle( + player, + chat.getTitle(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), ChronoUnit.SECONDS, 0, ChronoUnit.MILLIS, 200, ChronoUnit.SECONDS, 0) + ) + } else { + if (ticksLived == 0L) { + chat.showTitle( + player, + chat.getTitle(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), ChronoUnit.SECONDS, fadeIn, ChronoUnit.MILLIS, 200, ChronoUnit.SECONDS, 0) + ) + } else if (ticksLived > fadeIn * 20L) { + chat.showTitle( + player, + chat.getTitle(configManager.parse(player, titleAnimation.parseNext(title)), subTitleAnimation.parseNext(subTitle), ChronoUnit.SECONDS, 0, ChronoUnit.MILLIS, 200, ChronoUnit.SECONDS, 0) + ) + } } + } - override fun shouldContinue(): Boolean { - return ticksLived < 20L * (fadeIn + duration) && player.isOnline - } + override fun shouldContinue(): Boolean { + return ticksLived < 20L * (fadeIn + duration) && player.isOnline + } - override fun synchronizationContext() = SynchronizationContext.ASYNC + override fun synchronizationContext() = SynchronizationContext.ASYNC } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/task/ToastTask.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/task/ToastTask.kt index 04f9948..bcd233d 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/task/ToastTask.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/task/ToastTask.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.task import com.google.gson.Gson @@ -10,165 +33,166 @@ import xyz.jpenilla.announcerplus.config.message.ToastSettings import xyz.jpenilla.announcerplus.util.asyncTimer import xyz.jpenilla.announcerplus.util.runSync import xyz.jpenilla.jmplib.Crafty +import xyz.jpenilla.jmplib.Environment import java.lang.reflect.Modifier import java.util.concurrent.ConcurrentLinkedDeque import kotlin.random.Random.Default.nextInt class ToastTask : KoinComponent { - private val announcerPlus: AnnouncerPlus by inject() - private val queuedToasts = ConcurrentLinkedDeque() + private val announcerPlus: AnnouncerPlus by inject() + private val queuedToasts = ConcurrentLinkedDeque() - private val toastTask = announcerPlus.asyncTimer(0L, 1L) { - if (queuedToasts.isNotEmpty()) { - val toast = queuedToasts.removeFirst() - if (toast.player.isOnline) { - displayToastImmediately(toast) - } + private val toastTask = announcerPlus.asyncTimer(0L, 1L) { + if (queuedToasts.isNotEmpty()) { + val toast = queuedToasts.removeFirst() + if (toast.player.isOnline) { + displayToastImmediately(toast) + } + } + } + + fun queueToast(toastSettings: ToastSettings, player: Player) { + queuedToasts.add(QueuedToast(player, toastSettings)) + } + + fun cancel() { + toastTask.cancel() + } + + data class QueuedToast(val player: Player, val toast: ToastSettings) + + private fun displayToastImmediately(queuedToast: QueuedToast) { + val (player, toast) = queuedToast + announcerPlus.runSync { + val minecraftKey = + MinecraftKey_ctr.newInstance(announcerPlus.name.toLowerCase(), nextInt(1000000).toString()) + val serializedAdvancement = if (Environment.majorMinecraftVersion() >= 16) { + SerializedAdvancement_deserialize( + null, + toast.getJson(player), + LootDeserializationContext_ctr.newInstance( + minecraftKey, + MinecraftServer_getLootPredicateManager(MinecraftServer_getServer()) + ) + ) + } else { + ChatDeserializer_deserialize( + null, + AdvancementDataWorld_DESERIALIZER, + announcerPlus.gson.toJson(toast.getJson(player)), + SerializedAdvancement_class + ) + } + val advancement = SerializedAdvancement_getAdvancement(serializedAdvancement, minecraftKey) + val nmsPlayer = player.handle + val advancementData = EntityPlayer_getAdvancementData(nmsPlayer) + val advancementProgress = AdvancementDataPlayer_getProgress(advancementData, advancement) + (AdvancementProgress_remainingCriteria(advancementProgress) as Iterable<*>).forEach { + AdvancementDataPlayer_grantCriteria(advancementData, advancement, it) + } + announcerPlus.runSync(20L) { + (AdvancementProgress_awardedCriteria(advancementProgress) as Iterable<*>).forEach { + AdvancementDataPlayer_revokeCriteria(advancementData, advancement, it) } + } + } + } + + private companion object Reflect { + val MinecraftServer_class = Crafty.needNmsClass("MinecraftServer") + val MinecraftServer_getServer = + Crafty.findStaticMethod(MinecraftServer_class, "getServer", MinecraftServer_class) + ?: error("Cannot find getServer") + + val MinecraftKey_class = Crafty.needNmsClass("MinecraftKey") + val MinecraftKey_ctr = MinecraftKey_class.getDeclaredConstructor(String::class.java, String::class.java) + ?: error("Cannot find MinecraftKey constructor") + + val Advancement_class = Crafty.needNmsClass("Advancement") + val SerializedAdvancement_class = Crafty.needNmsClass("Advancement\$SerializedAdvancement") + + val SerializedAdvancement_getAdvancement = SerializedAdvancement_class.declaredMethods.find { method -> + method.returnType == Advancement_class + && method.parameterCount == 1 + && method.parameterTypes[0] == MinecraftKey_class + && !Modifier.isStatic(method.modifiers) + } ?: error("Cannot find SerializedAdvancement#getAdvancement") + + val EntityPlayer_class = Crafty.needNmsClass("EntityPlayer") + val CraftPlayer_class = Crafty.needCraftClass("entity.CraftPlayer") + val CraftPlayer_getHandle = CraftPlayer_class.getMethod("getHandle") + ?: error("Cannot find CraftPlayer#getHandle") + val Player.handle: Any + get() = CraftPlayer_getHandle(this) + + val AdvancementProgress_class = Crafty.needNmsClass("AdvancementProgress") + val AdvancementDataPlayer_class = Crafty.needNmsClass("AdvancementDataPlayer") + val EntityPlayer_getAdvancementData = EntityPlayer_class.methods.find { method -> + method.returnType == AdvancementDataPlayer_class + && method.parameterCount == 0 + } ?: error("Cannot find EntityPlayer#getAdvancementData") + + val AdvancementDataPlayer_getProgress = AdvancementDataPlayer_class.methods.find { method -> + method.returnType == AdvancementProgress_class + && method.parameterCount == 1 + && method.parameterTypes[0] == Advancement_class + } ?: error("Cannot find AdvancementDataPlayer#getProgress") + + val AdvancementDataPlayer_grantCriteria = + AdvancementDataPlayer_class.getDeclaredMethod("grantCriteria", Advancement_class, String::class.java) + ?: error("Cannot find AdvancementDataPlayer#grantCriteria") + val AdvancementDataPlayer_revokeCriteria = try { + AdvancementDataPlayer_class.getDeclaredMethod("revokeCritera", Advancement_class, String::class.java) // lol + } catch (e: NoSuchMethodException) { + AdvancementDataPlayer_class.getDeclaredMethod("revokeCriteria", Advancement_class, String::class.java) + } ?: error("Cannot find AdvancementDataPlayer#revokeCriteria") + val AdvancementProgress_remainingCriteria = AdvancementProgress_class.getDeclaredMethod("getRemainingCriteria") + ?: error("Cannot find AdvancementProgress#getRemainingCriteria") + val AdvancementProgress_awardedCriteria = AdvancementProgress_class.getDeclaredMethod("getAwardedCriteria") + ?: error("Cannot find AdvancementProgress#getAwardedCriteria") + + // start 1.12 -> 1.15 + val ChatDeserializer_class by lazy { Crafty.needNmsClass("ChatDeserializer") } + val AdvancementDataWorld_class by lazy { Crafty.needNmsClass("AdvancementDataWorld") } + + val ChatDeserializer_deserialize by lazy { + ChatDeserializer_class.methods.find { method -> + method.parameterCount == 3 + && method.parameterTypes.contains(Gson::class.java) + && method.parameterTypes.contains(String::class.java) + && method.parameterTypes.contains(Class::class.java) + } ?: error("Cannot find ChatDeserializer.deserialize") } - fun queueToast(toastSettings: ToastSettings, player: Player) { - queuedToasts.add(QueuedToast(player, toastSettings)) + val AdvancementDataWorld_DESERIALIZER by lazy { + AdvancementDataWorld_class.getField("DESERIALIZER").get(null) + ?: error("Cannot find AdvancementDataWorld.DESERIALIZER") + } + // end 1.12 -> 1.15 + + // start 1.16+ + val LootPredicateManager_class by lazy { Crafty.needNmsClass("LootPredicateManager") } + val MinecraftServer_getLootPredicateManager by lazy { + MinecraftServer_class.declaredMethods.find { method -> + method.returnType == LootPredicateManager_class + && method.parameterCount == 0 + } ?: error("Cannot find MinecraftServer#getLootPredicateManager") } - fun cancel() { - toastTask.cancel() + val LootDeserializationContext_class by lazy { Crafty.needNmsClass("LootDeserializationContext") } + val LootDeserializationContext_ctr by lazy { + LootDeserializationContext_class.getDeclaredConstructor(MinecraftKey_class, LootPredicateManager_class) + ?: error("Cannot find LootDeserializationContext constructor") } - data class QueuedToast(val player: Player, val toast: ToastSettings) - - private fun displayToastImmediately(queuedToast: QueuedToast) { - val (player, toast) = queuedToast - announcerPlus.runSync { - val minecraftKey = - MinecraftKey_ctr.newInstance(announcerPlus.name.toLowerCase(), nextInt(1000000).toString()) - val serializedAdvancement = if (announcerPlus.majorMinecraftVersion >= 16) { - SerializedAdvancement_deserialize( - null, - toast.getJson(player), - LootDeserializationContext_ctr.newInstance( - minecraftKey, - MinecraftServer_getLootPredicateManager(MinecraftServer_getServer()) - ) - ) - } else { - ChatDeserializer_deserialize( - null, - AdvancementDataWorld_DESERIALIZER, - announcerPlus.gson.toJson(toast.getJson(player)), - SerializedAdvancement_class - ) - } - val advancement = SerializedAdvancement_getAdvancement(serializedAdvancement, minecraftKey) - val nmsPlayer = player.handle - val advancementData = EntityPlayer_getAdvancementData(nmsPlayer) - val advancementProgress = AdvancementDataPlayer_getProgress(advancementData, advancement) - (AdvancementProgress_remainingCriteria(advancementProgress) as Iterable<*>).forEach { - AdvancementDataPlayer_grantCriteria(advancementData, advancement, it) - } - announcerPlus.runSync(20L) { - (AdvancementProgress_awardedCriteria(advancementProgress) as Iterable<*>).forEach { - AdvancementDataPlayer_revokeCriteria(advancementData, advancement, it) - } - } - } - } - - private companion object Reflect { - val MinecraftServer_class = Crafty.needNmsClass("MinecraftServer") - val MinecraftServer_getServer = - Crafty.findStaticMethod(MinecraftServer_class, "getServer", MinecraftServer_class) - ?: error("Cannot find getServer") - - val MinecraftKey_class = Crafty.needNmsClass("MinecraftKey") - val MinecraftKey_ctr = MinecraftKey_class.getDeclaredConstructor(String::class.java, String::class.java) - ?: error("Cannot find MinecraftKey constructor") - - val Advancement_class = Crafty.needNmsClass("Advancement") - val SerializedAdvancement_class = Crafty.needNmsClass("Advancement\$SerializedAdvancement") - - val SerializedAdvancement_getAdvancement = SerializedAdvancement_class.declaredMethods.find { method -> - method.returnType == Advancement_class - && method.parameterCount == 1 - && method.parameterTypes[0] == MinecraftKey_class - && !Modifier.isStatic(method.modifiers) - } ?: error("Cannot find SerializedAdvancement#getAdvancement") - - val EntityPlayer_class = Crafty.needNmsClass("EntityPlayer") - val CraftPlayer_class = Crafty.needCraftClass("entity.CraftPlayer") - val CraftPlayer_getHandle = CraftPlayer_class.getMethod("getHandle") - ?: error("Cannot find CraftPlayer#getHandle") - val Player.handle: Any - get() = CraftPlayer_getHandle(this) - - val AdvancementProgress_class = Crafty.needNmsClass("AdvancementProgress") - val AdvancementDataPlayer_class = Crafty.needNmsClass("AdvancementDataPlayer") - val EntityPlayer_getAdvancementData = EntityPlayer_class.methods.find { method -> - method.returnType == AdvancementDataPlayer_class - && method.parameterCount == 0 - } ?: error("Cannot find EntityPlayer#getAdvancementData") - - val AdvancementDataPlayer_getProgress = AdvancementDataPlayer_class.methods.find { method -> - method.returnType == AdvancementProgress_class - && method.parameterCount == 1 - && method.parameterTypes[0] == Advancement_class - } ?: error("Cannot find AdvancementDataPlayer#getProgress") - - val AdvancementDataPlayer_grantCriteria = - AdvancementDataPlayer_class.getDeclaredMethod("grantCriteria", Advancement_class, String::class.java) - ?: error("Cannot find AdvancementDataPlayer#grantCriteria") - val AdvancementDataPlayer_revokeCriteria = try { - AdvancementDataPlayer_class.getDeclaredMethod("revokeCritera", Advancement_class, String::class.java) // lol - } catch (e: NoSuchMethodException) { - AdvancementDataPlayer_class.getDeclaredMethod("revokeCriteria", Advancement_class, String::class.java) - } ?: error("Cannot find AdvancementDataPlayer#revokeCriteria") - val AdvancementProgress_remainingCriteria = AdvancementProgress_class.getDeclaredMethod("getRemainingCriteria") - ?: error("Cannot find AdvancementProgress#getRemainingCriteria") - val AdvancementProgress_awardedCriteria = AdvancementProgress_class.getDeclaredMethod("getAwardedCriteria") - ?: error("Cannot find AdvancementProgress#getAwardedCriteria") - - // start 1.12 -> 1.15 - val ChatDeserializer_class by lazy { Crafty.needNmsClass("ChatDeserializer") } - val AdvancementDataWorld_class by lazy { Crafty.needNmsClass("AdvancementDataWorld") } - - val ChatDeserializer_deserialize by lazy { - ChatDeserializer_class.methods.find { method -> - method.parameterCount == 3 - && method.parameterTypes.contains(Gson::class.java) - && method.parameterTypes.contains(String::class.java) - && method.parameterTypes.contains(Class::class.java) - } ?: error("Cannot find ChatDeserializer.deserialize") - } - - val AdvancementDataWorld_DESERIALIZER by lazy { - AdvancementDataWorld_class.getField("DESERIALIZER").get(null) - ?: error("Cannot find AdvancementDataWorld.DESERIALIZER") - } - // end 1.12 -> 1.15 - - // start 1.16+ - val LootPredicateManager_class by lazy { Crafty.needNmsClass("LootPredicateManager") } - val MinecraftServer_getLootPredicateManager by lazy { - MinecraftServer_class.declaredMethods.find { method -> - method.returnType == LootPredicateManager_class - && method.parameterCount == 0 - } ?: error("Cannot find MinecraftServer#getLootPredicateManager") - } - - val LootDeserializationContext_class by lazy { Crafty.needNmsClass("LootDeserializationContext") } - val LootDeserializationContext_ctr by lazy { - LootDeserializationContext_class.getDeclaredConstructor(MinecraftKey_class, LootPredicateManager_class) - ?: error("Cannot find LootDeserializationContext constructor") - } - - val SerializedAdvancement_deserialize by lazy { - SerializedAdvancement_class.declaredMethods.find { method -> - method.returnType == SerializedAdvancement_class - && method.parameterCount == 2 - && method.parameterTypes.contains(JsonObject::class.java) - && method.parameterTypes.contains(LootDeserializationContext_class) - } ?: error("Cannot find SerializedAdvancement.deserialize") - } - // end 1.16+ + val SerializedAdvancement_deserialize by lazy { + SerializedAdvancement_class.declaredMethods.find { method -> + method.returnType == SerializedAdvancement_class + && method.parameterCount == 2 + && method.parameterTypes.contains(JsonObject::class.java) + && method.parameterTypes.contains(LootDeserializationContext_class) + } ?: error("Cannot find SerializedAdvancement.deserialize") } + // end 1.16+ + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/task/UpdateTask.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/task/UpdateTask.kt index 5651f78..1d0115b 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/task/UpdateTask.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/task/UpdateTask.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.task import org.bukkit.scheduler.BukkitTask @@ -8,38 +31,38 @@ import xyz.jpenilla.announcerplus.util.asyncTimer import xyz.jpenilla.announcerplus.util.syncTimer abstract class UpdateTask : KoinComponent { - private val announcerPlus: AnnouncerPlus by inject() - private var updateTask: BukkitTask? = null - var ticksLived = 0L + private val announcerPlus: AnnouncerPlus by inject() + private var updateTask: BukkitTask? = null + var ticksLived = 0L - open fun start(): UpdateTask = apply { + open fun start(): UpdateTask = apply { + stop() + val runnable = Runnable { + if (!shouldContinue()) { stop() - val runnable = Runnable { - if (!shouldContinue()) { - stop() - } - update() - ticksLived++ - } - updateTask = when (synchronizationContext()) { - SynchronizationContext.SYNC -> announcerPlus.syncTimer(0L, 1L, runnable) - SynchronizationContext.ASYNC -> announcerPlus.asyncTimer(0L, 1L, runnable) - } + } + update() + ticksLived++ } - - open fun stop() { - updateTask?.cancel() - updateTask = null + updateTask = when (synchronizationContext()) { + SynchronizationContext.SYNC -> announcerPlus.syncTimer(0L, 1L, runnable) + SynchronizationContext.ASYNC -> announcerPlus.asyncTimer(0L, 1L, runnable) } + } - abstract fun update() + open fun stop() { + updateTask?.cancel() + updateTask = null + } - abstract fun shouldContinue(): Boolean + abstract fun update() - abstract fun synchronizationContext(): SynchronizationContext + abstract fun shouldContinue(): Boolean - enum class SynchronizationContext { - SYNC, - ASYNC - } + abstract fun synchronizationContext(): SynchronizationContext + + enum class SynchronizationContext { + SYNC, + ASYNC + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/AnimationHolder.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/AnimationHolder.kt index 0dc933b..b8efba9 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/AnimationHolder.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/AnimationHolder.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation import net.kyori.adventure.text.format.NamedTextColor @@ -14,112 +37,112 @@ import xyz.jpenilla.announcerplus.textanimation.animation.ScrollingText import xyz.jpenilla.announcerplus.textanimation.animation.Typewriter class AnimationHolder(private val player: Player?, private val message: String) : KoinComponent { - companion object { - private val pattern = "\\{animate:/?([a-z][^}]*)/?}?".toPattern() - } + companion object { + private val pattern = "\\{animate:/?([a-z][^}]*)/?}?".toPattern() + } - private val configManager: ConfigManager by inject() - private val matcher = pattern.matcher(message) - private val animations = HashMap() + private val configManager: ConfigManager by inject() + private val matcher = pattern.matcher(message) + private val animations = HashMap() - init { - while (matcher.find()) { - val tokens = matcher.group(1).split(":") + init { + while (matcher.find()) { + val tokens = matcher.group(1).split(":") - when (tokens[0]) { - "scroll" -> { - val speed = try { - tokens[1].toFloat() - } catch (e: Exception) { - 0.1f - } - animations[matcher.group()] = ScrollingGradient(speed) - } - - "flash" -> { - val colors = ArrayList(tokens.subList(1, tokens.size)) - var ticks: Int - try { - ticks = colors.last().toInt() - colors.removeAt(colors.lastIndex) - } catch (e: Exception) { - ticks = 10 - } - animations[matcher.group()] = FlashingText(colors, ticks) - } - - "pulse" -> { - val colors = ArrayList(tokens.subList(1, tokens.size)) - var ticks: Int - try { - ticks = colors.last().toInt() - colors.removeAt(colors.lastIndex) - } catch (e: Exception) { - ticks = 10 - } - val textColors = colors.map { color -> - NamedTextColor.NAMES.value(color) - ?: TextColor.fromHexString(color) - ?: NamedTextColor.WHITE - } - animations[matcher.group()] = PulsingColor(textColors, ticks) - } - - "type" -> { - val text = tokens[1] - val ticks = try { - tokens[2].toInt() - } catch (e: Exception) { - 6 - } - animations[matcher.group()] = Typewriter(player, text, ticks) - } - - "randomcolor" -> { - val type = try { - RandomColor.Type.of(tokens[1]) - } catch (e: Exception) { - RandomColor.Type.PULSE - } - val ticks = try { - tokens[2].toInt() - } catch (e: Exception) { - 10 - } - animations[matcher.group()] = RandomColor(type, ticks) - } - - "scrolltext" -> { - val text = tokens[1] - val window = try { - tokens[2].toInt() - } catch (e: Exception) { - 10 - } - val ticks = try { - tokens[3].toInt() - } catch (e: Exception) { - 4 - } - animations[matcher.group()] = ScrollingText(player, text, window, ticks) - } - } + when (tokens[0]) { + "scroll" -> { + val speed = try { + tokens[1].toFloat() + } catch (e: Exception) { + 0.1f + } + animations[matcher.group()] = ScrollingGradient(speed) } - } - fun parseNext(text: String = message): String { - var msg = text - animations.forEach { animation -> - msg = msg.replace(animation.key, animation.value.nextValue()) + "flash" -> { + val colors = ArrayList(tokens.subList(1, tokens.size)) + var ticks: Int + try { + ticks = colors.last().toInt() + colors.removeAt(colors.lastIndex) + } catch (e: Exception) { + ticks = 10 + } + animations[matcher.group()] = FlashingText(colors, ticks) } - return configManager.parse(player, msg) - } - fun parseCurrent(text: String = message): String { - var msg = text - animations.forEach { animation -> - msg = msg.replace(animation.key, animation.value.getValue()) + "pulse" -> { + val colors = ArrayList(tokens.subList(1, tokens.size)) + var ticks: Int + try { + ticks = colors.last().toInt() + colors.removeAt(colors.lastIndex) + } catch (e: Exception) { + ticks = 10 + } + val textColors = colors.map { color -> + NamedTextColor.NAMES.value(color) + ?: TextColor.fromHexString(color) + ?: NamedTextColor.WHITE + } + animations[matcher.group()] = PulsingColor(textColors, ticks) } - return configManager.parse(player, msg) + + "type" -> { + val text = tokens[1] + val ticks = try { + tokens[2].toInt() + } catch (e: Exception) { + 6 + } + animations[matcher.group()] = Typewriter(player, text, ticks) + } + + "randomcolor" -> { + val type = try { + RandomColor.Type.of(tokens[1]) + } catch (e: Exception) { + RandomColor.Type.PULSE + } + val ticks = try { + tokens[2].toInt() + } catch (e: Exception) { + 10 + } + animations[matcher.group()] = RandomColor(type, ticks) + } + + "scrolltext" -> { + val text = tokens[1] + val window = try { + tokens[2].toInt() + } catch (e: Exception) { + 10 + } + val ticks = try { + tokens[3].toInt() + } catch (e: Exception) { + 4 + } + animations[matcher.group()] = ScrollingText(player, text, window, ticks) + } + } } + } + + fun parseNext(text: String = message): String { + var msg = text + animations.forEach { animation -> + msg = msg.replace(animation.key, animation.value.nextValue()) + } + return configManager.parse(player, msg) + } + + fun parseCurrent(text: String = message): String { + var msg = text + animations.forEach { animation -> + msg = msg.replace(animation.key, animation.value.getValue()) + } + return configManager.parse(player, msg) + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/TextAnimation.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/TextAnimation.kt index 63a878d..4f4f09b 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/TextAnimation.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/TextAnimation.kt @@ -1,8 +1,31 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation import org.koin.core.KoinComponent interface TextAnimation : KoinComponent { - fun getValue(): String - fun nextValue(): String + fun getValue(): String + fun nextValue(): String } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/FlashingText.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/FlashingText.kt index 0daebe7..820a603 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/FlashingText.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/FlashingText.kt @@ -1,25 +1,48 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation.animation import xyz.jpenilla.announcerplus.textanimation.TextAnimation class FlashingText(private val colors: List, private val ticks: Int) : TextAnimation { - private var index = 0 - private var ticksLived = 0 - private var color = colors[0] + private var index = 0 + private var ticksLived = 0 + private var color = colors[0] - override fun getValue(): String { - return color - } + override fun getValue(): String { + return color + } - override fun nextValue(): String { - ticksLived++ - if (ticksLived % ticks == 0) { - index++ - if (index > colors.lastIndex) { - index = 0 - } - color = colors[index] - } - return getValue() + override fun nextValue(): String { + ticksLived++ + if (ticksLived % ticks == 0) { + index++ + if (index > colors.lastIndex) { + index = 0 + } + color = colors[index] } + return getValue() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/PulsingColor.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/PulsingColor.kt index bd489c1..b1b44bb 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/PulsingColor.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/PulsingColor.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation.animation import net.kyori.adventure.text.format.TextColor @@ -5,46 +28,46 @@ import xyz.jpenilla.announcerplus.textanimation.TextAnimation import kotlin.math.roundToInt class PulsingColor(colors: List, ticks: Int) : TextAnimation { - private var index = 0 - private var colorIndex = 0 - private val colors = ArrayList(colors) + private var index = 0 + private var colorIndex = 0 + private val colors = ArrayList(colors) - init { - this.colors.add(this.colors[0]) + init { + this.colors.add(this.colors[0]) + } + + private var color = this.colors[0].asHexString() + private val factorStep = 1.0f / ticks + + override fun getValue(): String { + return color + } + + override fun nextValue(): String { + color = nextColor().asHexString() + return getValue() + } + + private fun interpolate(color1: TextColor, color2: TextColor, factor: Float): TextColor { + return TextColor.color( + (color1.red() + factor * (color2.red() - color1.red())).roundToInt(), + (color1.green() + factor * (color2.green() - color1.green())).roundToInt(), + (color1.blue() + factor * (color2.blue() - color1.blue())).roundToInt() + ) + } + + private fun nextColor(): TextColor { + if (factorStep * index > 1) { + colorIndex++ + index = 0 } - private var color = this.colors[0].asHexString() - private val factorStep = 1.0f / ticks + val factor = factorStep * index++ - override fun getValue(): String { - return color + if (colorIndex + 1 > colors.lastIndex) { + colorIndex = 0 } - override fun nextValue(): String { - color = nextColor().asHexString() - return getValue() - } - - private fun interpolate(color1: TextColor, color2: TextColor, factor: Float): TextColor { - return TextColor.color( - (color1.red() + factor * (color2.red() - color1.red())).roundToInt(), - (color1.green() + factor * (color2.green() - color1.green())).roundToInt(), - (color1.blue() + factor * (color2.blue() - color1.blue())).roundToInt() - ) - } - - private fun nextColor(): TextColor { - if (factorStep * index > 1) { - colorIndex++ - index = 0 - } - - val factor = factorStep * index++ - - if (colorIndex + 1 > colors.lastIndex) { - colorIndex = 0 - } - - return interpolate(colors[colorIndex], colors[colorIndex + 1], factor) - } + return interpolate(colors[colorIndex], colors[colorIndex + 1], factor) + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/RandomColor.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/RandomColor.kt index 5255e30..8d93b0c 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/RandomColor.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/RandomColor.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation.animation import net.kyori.adventure.text.format.TextColor @@ -5,42 +28,42 @@ import xyz.jpenilla.announcerplus.textanimation.TextAnimation import kotlin.random.Random class RandomColor(type: Type, ticks: Int) : TextAnimation { - private var animation = when (type) { - Type.FLASH -> { - FlashingText(getRandomColors(64).map { color -> color.asHexString() }, ticks) - } - Type.PULSE -> { - PulsingColor(getRandomColors(64), ticks) - } + private var animation = when (type) { + Type.FLASH -> { + FlashingText(getRandomColors(64).map { color -> color.asHexString() }, ticks) } - - override fun getValue(): String { - return animation.getValue() + Type.PULSE -> { + PulsingColor(getRandomColors(64), ticks) } + } - override fun nextValue(): String { - return animation.nextValue() + override fun getValue(): String { + return animation.getValue() + } + + override fun nextValue(): String { + return animation.nextValue() + } + + private fun getRandomColor(): TextColor { + return TextColor.color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256)) + } + + private fun getRandomColors(amount: Int): List { + val randomColors = arrayListOf() + for (i in 0 until amount) { + randomColors.add(getRandomColor()) } + return randomColors + } - private fun getRandomColor(): TextColor { - return TextColor.color(Random.nextInt(256), Random.nextInt(256), Random.nextInt(256)) - } - - private fun getRandomColors(amount: Int): List { - val randomColors = arrayListOf() - for (i in 0 until amount) { - randomColors.add(getRandomColor()) - } - return randomColors - } - - enum class Type { - FLASH, PULSE; - - companion object { - fun of(text: String): Type { - return valueOf(text.toUpperCase()) - } - } + enum class Type { + FLASH, PULSE; + + companion object { + fun of(text: String): Type { + return valueOf(text.toUpperCase()) + } } + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingGradient.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingGradient.kt index cc1af8e..b9603af 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingGradient.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingGradient.kt @@ -1,23 +1,46 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation.animation import xyz.jpenilla.announcerplus.textanimation.TextAnimation class ScrollingGradient(private val increment: Float) : TextAnimation { - companion object { - private val GRADIENT_PHASE_RANGE = -1.0f..1.0f - } + companion object { + private val GRADIENT_PHASE_RANGE = -1.0f..1.0f + } - private var phase = -1f + private var phase = -1f - override fun getValue(): String { - return phase.coerceIn(GRADIENT_PHASE_RANGE).toString() - } + override fun getValue(): String { + return phase.coerceIn(GRADIENT_PHASE_RANGE).toString() + } - override fun nextValue(): String { - if (phase >= 1f) { - phase = -1f - } - phase += increment - return getValue() + override fun nextValue(): String { + if (phase >= 1f) { + phase = -1f } + phase += increment + return getValue() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingText.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingText.kt index c060874..160cf29 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingText.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/ScrollingText.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation.animation import org.bukkit.entity.Player @@ -6,43 +29,43 @@ import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.announcerplus.textanimation.TextAnimation class ScrollingText( - private val player: Player?, - text: String, - private val windowSize: Int, - private val ticks: Int + private val player: Player?, + text: String, + private val windowSize: Int, + private val ticks: Int ) : TextAnimation { - private val configManager: ConfigManager by inject() - private val spaces = getSpaces(windowSize) - private val text = "$spaces$text$spaces" - private var index = 0 - private var ticksLived = 0 + private val configManager: ConfigManager by inject() + private val spaces = getSpaces(windowSize) + private val text = "$spaces$text$spaces" + private var index = 0 + private var ticksLived = 0 - private fun getSpaces(amount: Int): String { - val sb = StringBuilder() - for (i in 1..amount) { - sb.append(" ") - } - return sb.toString() + private fun getSpaces(amount: Int): String { + val sb = StringBuilder() + for (i in 1..amount) { + sb.append(" ") } + return sb.toString() + } - override fun getValue(): String { - return try { - configManager.parse(player, text).substring(index, index + windowSize) - } catch (e: Exception) { - //if the placeholders changed in a way that causes us to out of bounds - index = 0 - configManager.parse(player, text).substring(index, index + windowSize) - } + override fun getValue(): String { + return try { + configManager.parse(player, text).substring(index, index + windowSize) + } catch (e: Exception) { + //if the placeholders changed in a way that causes us to out of bounds + index = 0 + configManager.parse(player, text).substring(index, index + windowSize) } + } - override fun nextValue(): String { - ticksLived++ - if (ticksLived % ticks == 0) { - index++ - if (index > configManager.parse(player, text).length - windowSize) { - index = 0 - } - } - return getValue() + override fun nextValue(): String { + ticksLived++ + if (ticksLived % ticks == 0) { + index++ + if (index > configManager.parse(player, text).length - windowSize) { + index = 0 + } } + return getValue() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/Typewriter.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/Typewriter.kt index 1d5d1c2..0fe2a4a 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/Typewriter.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/textanimation/animation/Typewriter.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.textanimation.animation import org.bukkit.entity.Player @@ -6,33 +29,33 @@ import xyz.jpenilla.announcerplus.config.ConfigManager import xyz.jpenilla.announcerplus.textanimation.TextAnimation class Typewriter(private val player: Player?, private val text: String, private val ticks: Int) : TextAnimation { - private val configManager: ConfigManager by inject() - private var index = 0 - private var ticksLived = 0 - private var showUnderscore = true + private val configManager: ConfigManager by inject() + private var index = 0 + private var ticksLived = 0 + private var showUnderscore = true - override fun getValue(): String { - val s = try { - configManager.parse(player, text).substring(0, index) - } catch (e: Exception) { - //if the placeholders changed in a way that causes us to out of bounds - index = 0 - configManager.parse(player, text).substring(0, index) - } - return "$s${if (showUnderscore) "_" else " "}" + override fun getValue(): String { + val s = try { + configManager.parse(player, text).substring(0, index) + } catch (e: Exception) { + //if the placeholders changed in a way that causes us to out of bounds + index = 0 + configManager.parse(player, text).substring(0, index) } + return "$s${if (showUnderscore) "_" else " "}" + } - override fun nextValue(): String { - ticksLived++ - if (ticksLived % 5 == 0) { - showUnderscore = !showUnderscore - } - if (ticksLived % ticks == 0) { - index++ - if (index > configManager.parse(player, text).length) { - index = 0 - } - } - return getValue() + override fun nextValue(): String { + ticksLived++ + if (ticksLived % 5 == 0) { + showUnderscore = !showUnderscore } + if (ticksLived % ticks == 0) { + index++ + if (index > configManager.parse(player, text).length) { + index = 0 + } + } + return getValue() + } } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/util/Constants.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/util/Constants.kt index 500c268..2b21423 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/util/Constants.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/util/Constants.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.util import net.kyori.adventure.text.Component @@ -10,32 +33,32 @@ import net.kyori.adventure.text.format.TextColor.color import net.kyori.adventure.text.format.TextDecoration object Constants { - const val CONFIG_COMMENT_SOUNDS_LINE2: String = - "Separate multiple sounds by commas, i.e. \"minecraft:entity.strider.happy,minecraft:entity.villager.ambient,minecraft:block.note_block.cow_bell\"" + const val CONFIG_COMMENT_SOUNDS_LINE2: String = + "Separate multiple sounds by commas, i.e. \"minecraft:entity.strider.happy,minecraft:entity.villager.ambient,minecraft:block.note_block.cow_bell\"" - const val CONFIG_COMMENT_SOUNDS_RANDOM: String = - "Should a random join sound be chosen(true) or should all of them play(false)" + const val CONFIG_COMMENT_SOUNDS_RANDOM: String = + "Should a random join sound be chosen(true) or should all of them play(false)" - val CHAT_PREFIX: Component = text() - .append(text("[", NamedTextColor.WHITE)) - .append(text("A", color(0x47EB46))) - .append(text("P", color(0x2CF58B))) - .append(text("]", NamedTextColor.WHITE)) - .append(space()) - .hoverEvent( - text() - .append(text("Announcer", color(0x47EB46))) - .append(text("Plus", color(0x2CF58B), TextDecoration.ITALIC)) - .append(newline()) - .append( - text( - " Click for help", - NamedTextColor.GRAY, - TextDecoration.ITALIC - ) - ) - .build() + val CHAT_PREFIX: Component = text() + .append(text("[", NamedTextColor.WHITE)) + .append(text("A", color(0x47EB46))) + .append(text("P", color(0x2CF58B))) + .append(text("]", NamedTextColor.WHITE)) + .append(space()) + .hoverEvent( + text() + .append(text("Announcer", color(0x47EB46))) + .append(text("Plus", color(0x2CF58B), TextDecoration.ITALIC)) + .append(newline()) + .append( + text( + " Click for help", + NamedTextColor.GRAY, + TextDecoration.ITALIC + ) ) - .clickEvent(ClickEvent.runCommand("/announcerplus help")) .build() + ) + .clickEvent(ClickEvent.runCommand("/announcerplus help")) + .build() } diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/util/Functions.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/util/Functions.kt index 4ffbe3b..8ec2309 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/util/Functions.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/util/Functions.kt @@ -1,5 +1,29 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.util +import cloud.commandframework.ArgumentDescription import org.bukkit.Bukkit import org.bukkit.plugin.Plugin import org.bukkit.scheduler.BukkitTask @@ -20,10 +44,18 @@ fun Plugin.runAsync( ): BukkitTask = Bukkit.getScheduler().runTaskLaterAsynchronously(this, runnable, delay) -fun Plugin.asyncTimer(delay: Long, interval: Long, runnable: Runnable): BukkitTask = +fun Plugin.asyncTimer( + delay: Long, + interval: Long, + runnable: Runnable +): BukkitTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, runnable, delay, interval) -fun Plugin.syncTimer(delay: Long, interval: Long, runnable: Runnable): BukkitTask = +fun Plugin.syncTimer( + delay: Long, + interval: Long, + runnable: Runnable +): BukkitTask = Bukkit.getScheduler().runTaskTimer(this, runnable, delay, interval) fun Plugin.getOnMain(supplier: () -> T): T { @@ -31,3 +63,15 @@ fun Plugin.getOnMain(supplier: () -> T): T { runSync { future.complete(supplier()) } return future.join() } + +/** + * Get a [ArgumentDescription], defaulting to [ArgumentDescription.empty] + * + * @param description description string + * @return the description + * @since 1.4.0 + */ +fun description( + description: String = "" +): ArgumentDescription = + if (description.isEmpty()) ArgumentDescription.empty() else ArgumentDescription.of(description) diff --git a/src/main/kotlin/xyz/jpenilla/announcerplus/util/UpdateChecker.kt b/src/main/kotlin/xyz/jpenilla/announcerplus/util/UpdateChecker.kt index 7cc30f1..37f6f2c 100644 --- a/src/main/kotlin/xyz/jpenilla/announcerplus/util/UpdateChecker.kt +++ b/src/main/kotlin/xyz/jpenilla/announcerplus/util/UpdateChecker.kt @@ -1,3 +1,26 @@ +/* + * This file is part of AnnouncerPlus, licensed under the MIT License. + * + * Copyright (c) 2020-2021 Jason Penilla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ package xyz.jpenilla.announcerplus.util import com.google.common.base.Charsets @@ -9,34 +32,34 @@ import java.net.URL import java.util.LinkedList class UpdateChecker(private val plugin: AnnouncerPlus, private val githubRepo: String) { - private val parser = JsonParser() + private val parser = JsonParser() - fun updateCheck() { - plugin.runAsync { - val result = try { - parser.parse(InputStreamReader(URL("https://api.github.com/repos/$githubRepo/releases").openStream(), Charsets.UTF_8)).asJsonArray - } catch (exception: IOException) { - plugin.logger.info("Cannot look for updates: " + exception.message) - return@runAsync - } - val versionMap = LinkedHashMap() - result.forEach { versionMap[it.asJsonObject["tag_name"].asString] = it.asJsonObject["html_url"].asString } - val versionList = LinkedList(versionMap.keys) - val currentVersion = "v" + plugin.description.version - if (versionList[0] == currentVersion) { - plugin.logger.info("You are running the latest version of ${plugin.name}! :)") - return@runAsync - } - if (currentVersion.contains("SNAPSHOT")) { - plugin.logger.info("You are running a development build of ${plugin.name}! ($currentVersion)") - plugin.logger.info("The latest official release is " + versionList[0]) - return@runAsync - } - val versionsBehind = versionList.indexOf(currentVersion) - plugin.logger.info("There is an update available for ${plugin.name}!") - plugin.logger.info("You are running version $currentVersion, which is ${if (versionsBehind == -1) "many" else versionsBehind} versions outdated.") - plugin.logger.info("Download the latest version, ${versionList[0]} from GitHub at the link below:") - plugin.logger.info(versionMap[versionList[0]]) - } + fun updateCheck() { + plugin.runAsync { + val result = try { + parser.parse(InputStreamReader(URL("https://api.github.com/repos/$githubRepo/releases").openStream(), Charsets.UTF_8)).asJsonArray + } catch (exception: IOException) { + plugin.logger.info("Cannot look for updates: " + exception.message) + return@runAsync + } + val versionMap = LinkedHashMap() + result.forEach { versionMap[it.asJsonObject["tag_name"].asString] = it.asJsonObject["html_url"].asString } + val versionList = LinkedList(versionMap.keys) + val currentVersion = "v" + plugin.description.version + if (versionList[0] == currentVersion) { + // Up to date, do nothing + return@runAsync + } + if (currentVersion.contains("SNAPSHOT")) { + plugin.logger.info("You are running a development build of ${plugin.name}! ($currentVersion)") + plugin.logger.info("The latest official release is " + versionList[0]) + return@runAsync + } + val versionsBehind = versionList.indexOf(currentVersion) + plugin.logger.info("There is an update available for ${plugin.name}!") + plugin.logger.info("You are running version $currentVersion, which is ${if (versionsBehind == -1) "many" else versionsBehind} versions outdated.") + plugin.logger.info("Download the latest version, ${versionList[0]} from GitHub at the link below:") + plugin.logger.info(versionMap[versionList[0]]) } + } }