From d3d33c5e3bdb55ed5774489e49156f13c3377361 Mon Sep 17 00:00:00 2001 From: Taylor Becker Date: Sat, 7 Jun 2025 14:37:09 -0400 Subject: [PATCH 1/2] GH-4: Introduce sound support --- CHANGELOG.md | 2 + .../config/SimpleAnnounceConfig.kt | 49 +++++++++++++------ .../sender/AnnouncementSender.kt | 14 ++++-- .../simpleannounce/sender/BossBarSender.kt | 4 +- .../simpleannounce/sender/ChatSender.kt | 9 ++-- .../simpleannounce/sender/TitleSender.kt | 24 ++++----- src/main/resources/config.yml | 24 +++++++-- 7 files changed, 82 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4090bc3..8c2b5fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] ### Added +- GH-4: Sound support (@tajobe) - GH-3: Title type announcement sender (@tajobe) - MIT license @@ -13,6 +14,7 @@ - Config Auto-reload is now a repeating task as intended - Config actually created on first run - Copy updated default header when config is updated +- Chat sender advancing the current message per player ### Removed - Offline variant is now the default jar, no longer producing an "online" version diff --git a/src/main/kotlin/org/simplemc/simpleannounce/config/SimpleAnnounceConfig.kt b/src/main/kotlin/org/simplemc/simpleannounce/config/SimpleAnnounceConfig.kt index 35cb256..1c83bc1 100644 --- a/src/main/kotlin/org/simplemc/simpleannounce/config/SimpleAnnounceConfig.kt +++ b/src/main/kotlin/org/simplemc/simpleannounce/config/SimpleAnnounceConfig.kt @@ -4,8 +4,10 @@ import com.fasterxml.jackson.annotation.JsonAlias import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonUnwrapped +import org.bukkit.Sound import org.bukkit.boss.BarColor import org.bukkit.boss.BarStyle +import org.simplemc.simpleannounce.config.SimpleAnnounceConfig.AnnouncementConfig.Chat.ChatMessage import org.simplemc.simpleannounce.inTicks import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -16,8 +18,8 @@ data class SimpleAnnounceConfig( val announcements: List>, ) { companion object { - private fun Duration?.checkNullZeroOrPositive(name: String) { - check(this == null || this == Duration.Companion.ZERO || this.isPositive()) { + private fun Duration?.requireNullZeroOrPositive(name: String) { + require(this == null || this == Duration.Companion.ZERO || this.isPositive()) { "When set, $name must be >= 0s" } } @@ -27,20 +29,25 @@ data class SimpleAnnounceConfig( val autoReloadTicks = autoReload?.inTicks init { - check(autoReload == null || autoReload == Duration.Companion.ZERO || autoReload.inWholeMinutes >= 1) { + require(autoReload == null || autoReload == Duration.Companion.ZERO || autoReload.inWholeMinutes >= 1) { "When set, Auto Reload Duration must be > 1 minute" } } @JsonTypeInfo(use = JsonTypeInfo.Id.SIMPLE_NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") - sealed class AnnouncementConfig { + sealed class AnnouncementConfig { abstract val random: Boolean abstract val delay: Duration abstract val repeat: Duration? + abstract val sound: SoundConfig? abstract val includesPermissions: List abstract val excludesPermissions: List abstract val messages: List + interface Message { + val sound: SoundConfig? + } + @JsonIgnore val delayTicks = delay.inTicks.toInt() @@ -48,23 +55,27 @@ data class SimpleAnnounceConfig( val repeatTicks = repeat?.inTicks?.toInt() init { - delay.checkNullZeroOrPositive("delay") - repeat.checkNullZeroOrPositive("repeat") + delay.requireNullZeroOrPositive("delay") + repeat.requireNullZeroOrPositive("repeat") } data class Chat( override val random: Boolean = false, override val delay: Duration = Duration.Companion.ZERO, override val repeat: Duration? = null, + override val sound: SoundConfig? = null, override val includesPermissions: List = emptyList(), override val excludesPermissions: List = emptyList(), - @field:JsonAlias("message") override val messages: List, - ) : AnnouncementConfig() + @field:JsonAlias("message") override val messages: List, + ) : AnnouncementConfig() { + data class ChatMessage(val message: String, override val sound: SoundConfig? = null) : Message + } data class Boss( override val random: Boolean = false, override val delay: Duration = Duration.Companion.ZERO, override val repeat: Duration? = null, + override val sound: SoundConfig? = null, override val includesPermissions: List = emptyList(), override val excludesPermissions: List = emptyList(), @field:JsonAlias("message") override val messages: List, @@ -72,8 +83,9 @@ data class SimpleAnnounceConfig( ) : AnnouncementConfig() { data class BossBarMessage( val message: String, + override val sound: SoundConfig? = null, @field:JsonUnwrapped val barConfig: BarConfig? = null, - ) { + ) : Message { init { require(message.length <= 64) { "Boss Bar text must be <= 64 characters" } } @@ -90,7 +102,7 @@ data class SimpleAnnounceConfig( val holdTicks = hold.inTicks init { - hold.checkNullZeroOrPositive("hold") + hold.requireNullZeroOrPositive("hold") } } } @@ -99,6 +111,7 @@ data class SimpleAnnounceConfig( override val random: Boolean = false, override val delay: Duration = Duration.Companion.ZERO, override val repeat: Duration? = null, + override val sound: SoundConfig? = null, override val includesPermissions: List = emptyList(), override val excludesPermissions: List = emptyList(), @field:JsonAlias("message") override val messages: List, @@ -107,8 +120,9 @@ data class SimpleAnnounceConfig( data class TitleMessage( val title: String, val subtitle: String? = null, + override val sound: SoundConfig? = null, @field:JsonUnwrapped val titleConfig: TitleConfig? = null, - ) + ) : Message data class TitleConfig( val fadeIn: Duration = 500.milliseconds, @@ -125,11 +139,18 @@ data class SimpleAnnounceConfig( val fadeOutTicks = fadeOut.inTicks.toInt() init { - fadeIn.checkNullZeroOrPositive("fadeIn") - stay.checkNullZeroOrPositive("stay") - fadeOut.checkNullZeroOrPositive("fadeOut") + fadeIn.requireNullZeroOrPositive("fadeIn") + stay.requireNullZeroOrPositive("stay") + fadeOut.requireNullZeroOrPositive("fadeOut") } } } + + data class SoundConfig(val sound: Sound, val volume: Float = 1F, val pitch: Float = 1F) { + init { + require(volume >= 0 && volume <= 1) { "Sound volume must be between 0 and 1" } + require(pitch >= 0.5 && pitch <= 2) { "Sound pitch must be between 0.5 and 2" } + } + } } } diff --git a/src/main/kotlin/org/simplemc/simpleannounce/sender/AnnouncementSender.kt b/src/main/kotlin/org/simplemc/simpleannounce/sender/AnnouncementSender.kt index f0404a2..34d4d46 100644 --- a/src/main/kotlin/org/simplemc/simpleannounce/sender/AnnouncementSender.kt +++ b/src/main/kotlin/org/simplemc/simpleannounce/sender/AnnouncementSender.kt @@ -4,14 +4,14 @@ import io.github.oshai.kotlinlogging.KotlinLogging import org.bukkit.Bukkit import org.bukkit.entity.Player import org.bukkit.plugin.Plugin -import org.simplemc.simpleannounce.config.SimpleAnnounceConfig +import org.simplemc.simpleannounce.config.SimpleAnnounceConfig.AnnouncementConfig import org.simplemc.simpleannounce.inTicks import java.util.concurrent.atomic.AtomicInteger import kotlin.random.Random private val logger = KotlinLogging.logger("SimpleAnnounce AnnouncementSender") -abstract class AnnouncementSender>( +abstract class AnnouncementSender>( internal val plugin: Plugin, internal val announcement: ConfigType, ) : Runnable { @@ -36,7 +36,15 @@ abstract class AnnouncementSender Unit) { + val sound = message.sound ?: announcement.sound + Bukkit.getOnlinePlayers().filterNotNull().filter(this::shouldSendTo).forEach { + messageAction(it) + sound?.let { sound -> it.playSound(it.location, sound.sound, sound.volume, sound.pitch) } + } + } + + internal fun getNextMessage(): MessageType = when { announcement.messages.size == 1 -> announcement.messages[0] announcement.random -> announcement.messages[Random.nextInt(announcement.messages.size)] else -> { diff --git a/src/main/kotlin/org/simplemc/simpleannounce/sender/BossBarSender.kt b/src/main/kotlin/org/simplemc/simpleannounce/sender/BossBarSender.kt index 9bef14a..f81d790 100644 --- a/src/main/kotlin/org/simplemc/simpleannounce/sender/BossBarSender.kt +++ b/src/main/kotlin/org/simplemc/simpleannounce/sender/BossBarSender.kt @@ -11,7 +11,7 @@ class BossBarSender( announcement: Boss, ) : AnnouncementSender(plugin, announcement) { override fun run() { - val message = getNextAnnouncement() + val message = getNextMessage() val barConfig = message.barConfig ?: announcement.barConfig // create the bar @@ -19,7 +19,7 @@ class BossBarSender( bar.progress = if (barConfig.reverseAnimation) 1.0 else 0.0 // show bar to players - Bukkit.getOnlinePlayers().filterNotNull().filter(this::shouldSendTo).forEach(bar::addPlayer) + send(message, bar::addPlayer) bar.isVisible = true // set up animation diff --git a/src/main/kotlin/org/simplemc/simpleannounce/sender/ChatSender.kt b/src/main/kotlin/org/simplemc/simpleannounce/sender/ChatSender.kt index bfae817..8ba1c94 100644 --- a/src/main/kotlin/org/simplemc/simpleannounce/sender/ChatSender.kt +++ b/src/main/kotlin/org/simplemc/simpleannounce/sender/ChatSender.kt @@ -1,17 +1,14 @@ package org.simplemc.simpleannounce.sender -import org.bukkit.Bukkit import org.bukkit.plugin.Plugin import org.simplemc.simpleannounce.config.SimpleAnnounceConfig.AnnouncementConfig.Chat class ChatSender( plugin: Plugin, announcement: Chat, -) : AnnouncementSender(plugin, announcement) { +) : AnnouncementSender(plugin, announcement) { override fun run() { - Bukkit.getOnlinePlayers() - .filterNotNull() - .filter(this::shouldSendTo) - .forEach { it.sendMessage(getNextAnnouncement()) } + val message = getNextMessage() + send(message) { it.sendMessage(message.message) } } } diff --git a/src/main/kotlin/org/simplemc/simpleannounce/sender/TitleSender.kt b/src/main/kotlin/org/simplemc/simpleannounce/sender/TitleSender.kt index 5cb7862..0b74b8f 100644 --- a/src/main/kotlin/org/simplemc/simpleannounce/sender/TitleSender.kt +++ b/src/main/kotlin/org/simplemc/simpleannounce/sender/TitleSender.kt @@ -1,6 +1,5 @@ package org.simplemc.simpleannounce.sender -import org.bukkit.Bukkit import org.bukkit.plugin.Plugin import org.simplemc.simpleannounce.config.SimpleAnnounceConfig.AnnouncementConfig.Title @@ -9,20 +8,17 @@ class TitleSender( announcement: Title, ) : AnnouncementSender(plugin, announcement) { override fun run() { - val message = getNextAnnouncement() + val message = getNextMessage() val titleConfig = message.titleConfig ?: announcement.titleConfig - Bukkit.getOnlinePlayers() - .filterNotNull() - .filter(this::shouldSendTo) - .forEach { - it.sendTitle( - message.title, - message.subtitle, - titleConfig.fadeInTicks, - titleConfig.stayTicks, - titleConfig.fadeOutTicks, - ) - } + send(message) { + it.sendTitle( + message.title, + message.subtitle, + titleConfig.fadeInTicks, + titleConfig.stayTicks, + titleConfig.fadeOutTicks, + ) + } } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index c30eddc..187a004 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -14,6 +14,10 @@ # random(boolean, optional): # delay(duration, optional - default 0): # repeat(duration, optional):