diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 092b4cd..cb9a994 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -10,7 +10,7 @@ object Versions { const val ASSERTJ_CORE = "3.27.2" const val AWAITILITY = "4.2.2" - const val SPIGOT_API = "1.19.4-R0.1-SNAPSHOT" + const val SPIGOT_API = "1.21.4-R0.1-SNAPSHOT" const val JETBRAINS_ANNOTATIONS = "26.0.1" diff --git a/examples/bukkit/build.gradle.kts b/examples/bukkit/build.gradle.kts index 5526706..dd0e387 100644 --- a/examples/bukkit/build.gradle.kts +++ b/examples/bukkit/build.gradle.kts @@ -1,8 +1,8 @@ plugins { id("java") - id("com.github.johnrengelman.shadow") version "8.1.1" + id("com.gradleup.shadow") version "9.0.0-beta4" id("net.minecrell.plugin-yml.bukkit") version "0.6.0" - id("xyz.jpenilla.run-paper") version "2.3.0" + id("xyz.jpenilla.run-paper") version "2.3.1" } version = "1.0.0-SNAPSHOT" @@ -42,8 +42,8 @@ tasks.shadowJar { archiveFileName.set("$pluginName v${project.version}.jar") listOf( - "com.eternalcode.multification", - "net.dzikoysk.cdn", +// "com.eternalcode.multification", +// "net.dzikoysk.cdn", "panda.std", "panda.utilities", "net.kyori", @@ -56,5 +56,5 @@ sourceSets.test { } tasks.runServer { - minecraftVersion("1.20.4") + minecraftVersion("1.21.4") } diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/ExamplePlugin.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/ExamplePlugin.java index 91d7798..5939c19 100644 --- a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/ExamplePlugin.java +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/ExamplePlugin.java @@ -1,6 +1,7 @@ package com.eternalcode.example.bukkit; import com.eternalcode.example.bukkit.command.GiveCommand; +import com.eternalcode.example.bukkit.command.ReloadCommand; import com.eternalcode.example.bukkit.command.TeleportCommand; import com.eternalcode.example.bukkit.command.timer.TimerCommand; import com.eternalcode.example.bukkit.command.timer.TimerManager; @@ -39,6 +40,7 @@ public void onEnable() { new TeleportCommand(multification), new FlyCommand(multification), new GiveCommand(multification), + new ReloadCommand(configurationManager, multification), new TimerCommand(new TimerManager(this.getServer().getScheduler(), this, multification)) ) .build(); diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/ReloadCommand.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/ReloadCommand.java new file mode 100644 index 0000000..59d933c --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/ReloadCommand.java @@ -0,0 +1,40 @@ +package com.eternalcode.example.bukkit.command; + +import com.eternalcode.example.bukkit.config.ConfigurationManager; +import com.eternalcode.example.bukkit.multification.YourMultification; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@Command(name = "reload-config") +public class ReloadCommand { + + private final ConfigurationManager configurationManager; + private final YourMultification multification; + + public ReloadCommand(ConfigurationManager configurationManager, YourMultification multification) { + this.configurationManager = configurationManager; + this.multification = multification; + } + + @Execute + public void execute(@Context CommandSender sender) { + this.configurationManager.reload(); + + if (sender instanceof Player player) { + this.multification.create() + .player(player.getUniqueId()) + .notice(messagesConfig -> messagesConfig.reloadMessage) + .send(); + return; + } + + this.multification.create() + .console() + .notice(messagesConfig -> messagesConfig.reloadMessage) + .send(); + } + +} diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/ConfigurationManager.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/ConfigurationManager.java index ab2df30..5d98245 100644 --- a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/ConfigurationManager.java +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/ConfigurationManager.java @@ -4,6 +4,8 @@ import com.eternalcode.multification.notice.Notice; import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; import java.io.File; +import java.util.HashSet; +import java.util.Set; import net.dzikoysk.cdn.Cdn; import net.dzikoysk.cdn.CdnFactory; import net.dzikoysk.cdn.reflect.Visibility; @@ -12,6 +14,7 @@ public class ConfigurationManager { private final Cdn cdn; private final File dataFolder; + private final Set configs = new HashSet<>(); public ConfigurationManager(File dataFolder, NoticeResolverRegistry resolverRegistry) { this.dataFolder = dataFolder; @@ -30,7 +33,19 @@ public T load(T config, String fileName) { this.cdn.render(config, Source.of(this.dataFolder, fileName)) .orThrow(RuntimeException::new); + this.configs.add(new ReloadableConfig(fileName, config)); + return config; } + public void reload() { + for (ReloadableConfig config : configs) { + this.cdn.load(Source.of(this.dataFolder, config.fileName()), config.instance()) + .orThrow(RuntimeException::new); + } + } + + private record ReloadableConfig(String fileName, Object instance) { + } + } diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/MessagesConfig.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/MessagesConfig.java index 2054cc2..76bcbbf 100644 --- a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/MessagesConfig.java +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/MessagesConfig.java @@ -37,4 +37,9 @@ public class MessagesConfig { .bossBar(BossBar.Color.RED, Duration.ofSeconds(1), "Timer: {time}") .build(); + public Notice reloadMessage = Notice.builder() + .chat("Configuration has been reloaded!") + .sound("ambient.basalt_deltas.additions", 1.0F, 1.0F) + .build(); + } diff --git a/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundAccessor.java b/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundAccessor.java new file mode 100644 index 0000000..dd700e8 --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundAccessor.java @@ -0,0 +1,45 @@ +package com.eternalcode.multification.bukkit.notice.resolver.sound; + +import java.lang.reflect.Method; +import org.bukkit.Sound; + +public final class SoundAccessor { + + private static final Method VALUE_OF_METHOD; + private static final Method nameMethod; + + static { + try { + VALUE_OF_METHOD = Sound.class.getMethod("valueOf", String.class); + VALUE_OF_METHOD.setAccessible(true); + + nameMethod = Sound.class.getMethod("name"); + nameMethod.setAccessible(true); + } + catch (NoSuchMethodException noSuchMethodException) { + throw new RuntimeException(noSuchMethodException); + } + } + + private SoundAccessor() { + } + + public static Sound valueOf(String name) { + try { + return (Sound) VALUE_OF_METHOD.invoke(null, name); + } + catch (ReflectiveOperationException reflectiveOperationException) { + throw new RuntimeException(reflectiveOperationException); + } + } + + public static String name(Sound sound) { + try { + return (String) nameMethod.invoke(sound); + } + catch (ReflectiveOperationException reflectiveOperationException) { + throw new RuntimeException(reflectiveOperationException); + } + } + +} diff --git a/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundBukkitResolver.java b/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundBukkitResolver.java index 00858a1..5527a75 100644 --- a/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundBukkitResolver.java +++ b/multification-bukkit/src/com/eternalcode/multification/bukkit/notice/resolver/sound/SoundBukkitResolver.java @@ -4,6 +4,7 @@ import com.eternalcode.multification.notice.NoticeKey; import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; import com.eternalcode.multification.notice.resolver.NoticeResolver; +import java.util.Locale; import java.util.Optional; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.key.Key; @@ -40,24 +41,38 @@ public void send(Audience audience, ComponentSerializer deserialize(NoticeSerdesResult result) { Optional firstElement = result.firstElement(); @@ -69,14 +84,14 @@ public Optional deserialize(NoticeSerdesResult result) { String[] music = firstElement.get().split(" "); if (music.length == 1) { - return Optional.of(new SoundBukkit(org.bukkit.Sound.valueOf(music[0]), null, SoundBukkit.PITCH_UNSET, SoundBukkit.VOLUME_UNSET)); + return Optional.of(new SoundBukkit(SoundAccessor.valueOf(music[0]), null, SoundBukkit.PITCH_UNSET, SoundBukkit.VOLUME_UNSET)); } if (music.length != 4 && music.length != 3) { throw new IllegalArgumentException("Invalid sound format: " + firstElement.get()); } - org.bukkit.Sound sound = org.bukkit.Sound.valueOf(music[0]); + org.bukkit.Sound sound = SoundAccessor.valueOf(music[0]); SoundCategory category = music.length == 3 ? null : SoundCategory.valueOf(music[1]); float pitch = Float.parseFloat(music[music.length - 2]); float volume = Float.parseFloat(music[music.length - 1]); diff --git a/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java b/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java index 60f8a23..fb55317 100644 --- a/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java +++ b/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java @@ -1,8 +1,6 @@ package com.eternalcode.multification.cdn; import com.eternalcode.multification.bukkit.notice.BukkitNotice; -import com.eternalcode.multification.bukkit.notice.BukkitNoticeKey; -import com.eternalcode.multification.bukkit.notice.resolver.sound.SoundBukkit; import com.eternalcode.multification.bukkit.notice.resolver.sound.SoundBukkitResolver; import com.eternalcode.multification.notice.Notice; import com.eternalcode.multification.notice.NoticeKey; @@ -25,14 +23,20 @@ import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.key.Key; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import org.bukkit.Sound; import org.bukkit.SoundCategory; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; @SuppressWarnings("FieldMayBeFinal") class NoticeComposerTest { @@ -46,6 +50,28 @@ class NoticeComposerTest { .withMemberResolver(Visibility.PACKAGE_PRIVATE) .build(); + @BeforeAll + static void setup() { + MockedStatic bukkit = Mockito.mockStatic(Bukkit.class); + bukkit.when(() -> Bukkit.getRegistry(Mockito.any())).thenAnswer(bukkitInvocation -> { + Class type = bukkitInvocation.getArgument(0); + Registry mock = Mockito.mock(Registry.class); + + if (type == Sound.class) { + Mockito.when(mock.getOrThrow(Mockito.any())).thenAnswer(registryInvocation -> { + NamespacedKey key = registryInvocation.getArgument(0); + Sound sound = Mockito.mock(Sound.class); + Mockito.when(sound.name()) + .thenReturn(key.getKey()); + + return sound; + }); + } + + return mock; + }); + } + static class ConfigEmpty { Notice notice = Notice.empty(); } @@ -214,16 +240,16 @@ void serializeSoundNoticeWithSoundProperty() { ConfigSound configSound = assertRender(new ConfigSound(), """ notice: - sound: "BLOCK_ANVIL_LAND MASTER 1.0 1.0" + sound: "block.anvil.land MASTER 1.0 1.0" """); assertEquals(1, configSound.notice.parts().size()); NoticePart part = configSound.notice.parts().get(0); - SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); - assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); - assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); - assertEquals(SoundCategory.MASTER, sound.category()); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("block.anvil.land", sound.sound().value()); + assertEquals(net.kyori.adventure.sound.Sound.Source.MASTER, sound.category()); assertEquals(1.0f, sound.volume()); assertEquals(1.0f, sound.pitch()); } @@ -238,15 +264,15 @@ void serializeSoundNoticeWithoutCategoryProperty() { ConfigSoundWithoutCategory configSoundWithoutCategory = assertRender(new ConfigSoundWithoutCategory(), """ notice: - sound: "BLOCK_ANVIL_LAND 1.0 1.0" + sound: "block.anvil.land 1.0 1.0" """); assertEquals(1, configSoundWithoutCategory.notice.parts().size()); NoticePart part = configSoundWithoutCategory.notice.parts().get(0); - SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); - assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); - assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("block.anvil.land", sound.sound().value()); assertNull(sound.category()); assertEquals(1.0f, sound.volume()); assertEquals(1.0f, sound.pitch()); @@ -263,15 +289,15 @@ void serializeSoundNoticeWithoutVolumeAndPitch() { ConfigSoundShort configSoundShort = assertRender(new ConfigSoundShort(), """ notice: - sound: "BLOCK_ANVIL_LAND" + sound: "block.anvil.land" """); assertEquals(1, configSoundShort.notice.parts().size()); NoticePart part = configSoundShort.notice.parts().get(0); - SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); - assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); - assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("block.anvil.land", sound.sound().value()); assertNull(sound.category()); assertEquals(1.0f, sound.volumeOrDefault()); assertEquals(1.0f, sound.pitchOrDefault()); @@ -328,6 +354,32 @@ void serializeSoundNoticeWithoutCategoryAdventureProperty() { assertEquals(1.0f, sound.pitch()); } + public static class ConfigSoundAdventureShort { + Notice notice = Notice.sound("ambient.basalt_deltas.additions"); + } + + @Test + @DisplayName("Should serialize sound notice without volume and pitch") + void serializeSoundAdventureNoticeWithoutVolumeAndPitch() { + ConfigSoundAdventureShort config = assertRender(new ConfigSoundAdventureShort(), + """ + notice: + sound: "ambient.basalt_deltas.additions" + """); + + assertEquals(1, config.notice.parts().size()); + + NoticePart part = config.notice.parts().get(0); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("ambient.basalt_deltas.additions", sound.sound().value()); + assertNull(sound.category()); + assertEquals(1.0f, sound.volumeOrDefault()); + assertEquals(1.0f, sound.pitchOrDefault()); + assertEquals(-1.0f, sound.volume()); + assertEquals(-1.0f, sound.pitch()); + } + static class ConfigBossbarWOBuilder { Notice notice = Notice.bossBar(BossBar.Color.PINK, BossBar.Overlay.PROGRESS, Duration.ofSeconds(5), 0.9, "Example boss bar message"); } diff --git a/multification-core/src/com/eternalcode/multification/notice/Notice.java b/multification-core/src/com/eternalcode/multification/notice/Notice.java index f74d87f..bbaa65c 100644 --- a/multification-core/src/com/eternalcode/multification/notice/Notice.java +++ b/multification-core/src/com/eternalcode/multification/notice/Notice.java @@ -20,6 +20,7 @@ import com.eternalcode.multification.notice.resolver.title.TitleTimes; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.key.Key; +import net.kyori.adventure.key.KeyPattern; import net.kyori.adventure.sound.Sound; public class Notice { @@ -92,10 +93,20 @@ public static Notice hideTitle() { .build(); } - public static Notice sound(Key sound, Sound.Source category, float volume, float pitch) { - return Notice.builder() - .sound(sound, category, pitch, volume) - .build(); + public static Notice sound(@KeyPattern String sound) { + return sound(Key.key(sound), SoundAdventure.PITCH_UNSET, SoundAdventure.VOLUME_UNSET); + } + + public static Notice sound(@KeyPattern String sound, float volume, float pitch) { + return sound(Key.key(sound), volume, pitch); + } + + public static Notice sound(@KeyPattern String sound, Sound.Source category, float volume, float pitch) { + return sound(Key.key(sound), category, volume, pitch); + } + + public static Notice sound(Key sound) { + return sound(sound, SoundAdventure.PITCH_UNSET, SoundAdventure.VOLUME_UNSET); } public static Notice sound(Key sound, float volume, float pitch) { @@ -104,6 +115,12 @@ public static Notice sound(Key sound, float volume, float pitch) { .build(); } + public static Notice sound(Key sound, Sound.Source category, float volume, float pitch) { + return Notice.builder() + .sound(sound, category, pitch, volume) + .build(); + } + public static Notice bossBar(BossBar.Color color, BossBar.Overlay overlay, Duration duration, double progress, String message) { return Notice.builder() .bossBar(color, overlay, duration, progress, message) @@ -207,8 +224,24 @@ public B times(Duration in, Duration stay, Duration out) { return this.withPart(NoticeKey.TITLE_TIMES, new TitleTimes(in, stay, out)); } + public B sound(@KeyPattern String sound) { + return this.sound(Key.key(sound), SoundAdventure.PITCH_UNSET, SoundAdventure.VOLUME_UNSET); + } + + public B sound(@KeyPattern String sound, float pitch, float volume) { + return this.sound(Key.key(sound), pitch, volume); + } + + public B sound(@KeyPattern String sound, Sound.Source category, float pitch, float volume) { + return this.sound(Key.key(sound), category, pitch, volume); + } + + public B sound(Key sound) { + return this.sound(sound, SoundAdventure.PITCH_UNSET, SoundAdventure.VOLUME_UNSET); + } + public B sound(Key sound, float pitch, float volume) { - return this.withPart(NoticeKey.SOUND, new SoundAdventure(sound, null, pitch, volume)); + return this.sound(sound, null, pitch, volume); } public B sound(Key sound, Sound.Source category, float pitch, float volume) { diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java index 88ce7c7..cfb9ac0 100644 --- a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java @@ -46,7 +46,7 @@ public Optional> deserialize(String key, NoticeSerdes return Optional.of(deserializeResult); } } - catch (Exception exception) { + catch (Throwable exception) { illegalArgumentException.addSuppressed(exception); } } diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventure.java b/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventure.java index cb7e2bd..d0f9299 100644 --- a/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventure.java +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventure.java @@ -6,4 +6,23 @@ import org.jetbrains.annotations.Nullable; public record SoundAdventure(Key sound, @Nullable Sound.Source category, float pitch, float volume) implements NoticeContent { + + public static final Float PITCH_UNSET = -1.F; + public static final Float VOLUME_UNSET = -1.F; + + public static final float DEFAULT_PITCH = 1.F; + public static final float DEFAULT_VOLUME = 1.F; + + public float pitchOrDefault() { + return pitch == PITCH_UNSET ? DEFAULT_PITCH : pitch; + } + + public float volumeOrDefault() { + return volume == VOLUME_UNSET ? DEFAULT_VOLUME : volume; + } + + public Sound.Source categoryOrDefault() { + return category == null ? Sound.Source.MASTER : category; + } + } diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventureResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventureResolver.java index 274a217..5b59b43 100644 --- a/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventureResolver.java +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventureResolver.java @@ -12,6 +12,7 @@ public class SoundAdventureResolver implements NoticeResolver { + private static final String MUSIC = "%s"; private static final String MUSIC_WITH_CATEGORY = "%s %s %s %s"; private static final String MUSIC_WITHOUT_CATEGORY = "%s %s %s"; @@ -28,17 +29,16 @@ public NoticeKey noticeKey() { @Override public void send(Audience audience, ComponentSerializer componentSerializer, SoundAdventure content) { - if (content.category() == null) { - audience.playSound(Sound.sound(content.sound(), Sound.Source.MASTER, content.volume(), content.pitch())); - return; - } - - audience.playSound(Sound.sound(content.sound(), content.category(), content.volume(), content.pitch())); + audience.playSound(Sound.sound(content.sound(), content.categoryOrDefault(), content.volumeOrDefault(), content.pitchOrDefault())); } @Override public NoticeSerdesResult serialize(SoundAdventure content) { if (content.category() == null) { + if (content.pitch() == SoundAdventure.PITCH_UNSET || content.volume() == SoundAdventure.VOLUME_UNSET) { + return new NoticeSerdesResult.Single(String.format(MUSIC, content.sound().value())); + } + return new NoticeSerdesResult.Single(String.format(MUSIC_WITHOUT_CATEGORY, content.sound().value(), content.pitch(), @@ -64,6 +64,10 @@ public Optional deserialize(NoticeSerdesResult result) { String[] music = firstElement.get().split(" "); + if (music.length == 1) { + return Optional.of(new SoundAdventure(Key.key(Key.MINECRAFT_NAMESPACE, music[0]), null, SoundAdventure.PITCH_UNSET, SoundAdventure.VOLUME_UNSET)); + } + if (music.length < 3 || music.length > 4) { throw new IllegalStateException("Invalid music format: " + firstElement.get()); } diff --git a/multification-okaeri/src/com/eternalcode/multification/okaeri/MultificationNoticeSerializer.java b/multification-okaeri/src/com/eternalcode/multification/okaeri/MultificationNoticeSerializer.java index 7a1886a..99673d6 100644 --- a/multification-okaeri/src/com/eternalcode/multification/okaeri/MultificationNoticeSerializer.java +++ b/multification-okaeri/src/com/eternalcode/multification/okaeri/MultificationNoticeSerializer.java @@ -91,8 +91,7 @@ public Notice deserialize(DeserializationData data, @NotNull GenericsDeclaration if (value instanceof String stringValue) { NoticeDeserializeResult noticeResult = this.noticeRegistry.deserialize(key, new Single(stringValue)) - .orElseThrow(() -> new UnsupportedOperationException( - "Unsupported notice key: " + key + " with value: " + stringValue)); + .orElseThrow(() -> new UnsupportedOperationException("Unsupported notice key: " + key + " with value: " + stringValue)); this.withPart(builder, noticeResult); continue; @@ -102,8 +101,7 @@ public Notice deserialize(DeserializationData data, @NotNull GenericsDeclaration List messages = data.getAsList(key, String.class); NoticeDeserializeResult noticeResult = this.noticeRegistry.deserialize(key, new Multiple(messages)) - .orElseThrow(() -> new UnsupportedOperationException( - "Unsupported notice key: " + key + " with values: " + messages)); + .orElseThrow(() -> new UnsupportedOperationException("Unsupported notice key: " + key + " with values: " + messages)); this.withPart(builder, noticeResult); continue; @@ -111,16 +109,13 @@ public Notice deserialize(DeserializationData data, @NotNull GenericsDeclaration if (value instanceof Map mapValue) { NoticeDeserializeResult noticeResult = this.noticeRegistry.deserialize(key, new NoticeSerdesResult.Section((Map) mapValue)) - .orElseThrow(() -> new UnsupportedOperationException( - "Unsupported notice key: " + key + " with values: " + mapValue)); + .orElseThrow(() -> new UnsupportedOperationException("Unsupported notice key: " + key + " with values: " + mapValue)); this.withPart(builder, noticeResult); continue; } - - throw new UnsupportedOperationException( - "Unsupported notice type: " + value.getClass() + " for key: " + key); + throw new UnsupportedOperationException("Unsupported notice type: " + value.getClass() + " for key: " + key); } return builder.build(); diff --git a/multification-okaeri/test/com/eternalcode/multification/okaeri/NoticeSerializerTest.java b/multification-okaeri/test/com/eternalcode/multification/okaeri/NoticeSerializerTest.java index 91414f8..df8b303 100644 --- a/multification-okaeri/test/com/eternalcode/multification/okaeri/NoticeSerializerTest.java +++ b/multification-okaeri/test/com/eternalcode/multification/okaeri/NoticeSerializerTest.java @@ -1,13 +1,14 @@ package com.eternalcode.multification.okaeri; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import com.eternalcode.multification.bukkit.notice.BukkitNotice; -import com.eternalcode.multification.bukkit.notice.BukkitNoticeKey; -import com.eternalcode.multification.bukkit.notice.resolver.sound.SoundBukkit; import com.eternalcode.multification.bukkit.notice.resolver.sound.SoundBukkitResolver; import com.eternalcode.multification.notice.Notice; import com.eternalcode.multification.notice.NoticeKey; @@ -28,8 +29,11 @@ import net.kyori.adventure.key.Key; import org.bukkit.Sound; import org.bukkit.SoundCategory; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; @SuppressWarnings("FieldMayBeFinal") class NoticeSerializerTest { @@ -37,6 +41,28 @@ class NoticeSerializerTest { private static final NoticeResolverRegistry registry = NoticeResolverDefaults.createRegistry() .registerResolver(new SoundBukkitResolver()); + @BeforeAll + static void setup() { + MockedStatic bukkit = Mockito.mockStatic(Bukkit.class); + bukkit.when(() -> Bukkit.getRegistry(Mockito.any())).thenAnswer(bukkitInvocation -> { + Class type = bukkitInvocation.getArgument(0); + Registry mock = Mockito.mock(Registry.class); + + if (type == Sound.class) { + Mockito.when(mock.getOrThrow(Mockito.any())).thenAnswer(registryInvocation -> { + NamespacedKey key = registryInvocation.getArgument(0); + Sound sound = Mockito.mock(Sound.class); + Mockito.when(sound.name()) + .thenReturn(key.getKey()); + + return sound; + }); + } + + return mock; + }); + } + public static class ConfigEmpty extends OkaeriConfig { Notice notice = Notice.empty(); } @@ -205,16 +231,16 @@ void serializeSoundNoticeWithSoundProperty() { ConfigSound configSound = assertRender(new ConfigSound(), """ notice: - sound: "BLOCK_ANVIL_LAND MASTER 1.0 1.0" + sound: "block.anvil.land MASTER 1.0 1.0" """); assertEquals(1, configSound.notice.parts().size()); NoticePart part = configSound.notice.parts().get(0); - SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); - assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); - assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); - assertEquals(SoundCategory.MASTER, sound.category()); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("block.anvil.land", sound.sound().value()); + assertEquals(net.kyori.adventure.sound.Sound.Source.MASTER, sound.category()); assertEquals(1.0f, sound.volume()); assertEquals(1.0f, sound.pitch()); } @@ -229,15 +255,15 @@ void serializeSoundNoticeWithoutCategoryProperty() { ConfigSoundWithoutCategory configSoundWithoutCategory = assertRender(new ConfigSoundWithoutCategory(), """ notice: - sound: "BLOCK_ANVIL_LAND 1.0 1.0" + sound: "block.anvil.land 1.0 1.0" """); assertEquals(1, configSoundWithoutCategory.notice.parts().size()); NoticePart part = configSoundWithoutCategory.notice.parts().get(0); - SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); - assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); - assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("block.anvil.land", sound.sound().value()); assertNull(sound.category()); assertEquals(1.0f, sound.volume()); assertEquals(1.0f, sound.pitch()); @@ -254,15 +280,15 @@ void serializeSoundNoticeWithoutVolumeAndPitch() { ConfigSoundShort configSoundShort = assertRender(new ConfigSoundShort(), """ notice: - sound: "BLOCK_ANVIL_LAND" + sound: "block.anvil.land" """); assertEquals(1, configSoundShort.notice.parts().size()); NoticePart part = configSoundShort.notice.parts().get(0); - SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); - assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); - assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("block.anvil.land", sound.sound().value()); assertNull(sound.category()); assertEquals(1.0f, sound.volumeOrDefault()); assertEquals(1.0f, sound.pitchOrDefault()); @@ -319,6 +345,32 @@ void serializeSoundNoticeWithoutCategoryAdventureProperty() { assertEquals(1.0f, sound.pitch()); } + public static class ConfigSoundAdventureShort extends OkaeriConfig { + Notice notice = Notice.sound("ambient.basalt_deltas.additions"); + } + + @Test + @DisplayName("Should serialize sound notice without volume and pitch") + void serializeSoundAdventureNoticeWithoutVolumeAndPitch() { + ConfigSoundAdventureShort config = assertRender(new ConfigSoundAdventureShort(), + """ + notice: + sound: "ambient.basalt_deltas.additions" + """); + + assertEquals(1, config.notice.parts().size()); + + NoticePart part = config.notice.parts().get(0); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("ambient.basalt_deltas.additions", sound.sound().value()); + assertNull(sound.category()); + assertEquals(1.0f, sound.volumeOrDefault()); + assertEquals(1.0f, sound.pitchOrDefault()); + assertEquals(-1.0f, sound.volume()); + assertEquals(-1.0f, sound.pitch()); + } + public static class ConfigBossbarWOBuilder extends OkaeriConfig { Notice notice = Notice.bossBar(BossBar.Color.PINK, BossBar.Overlay.PROGRESS, Duration.ofSeconds(5), 0.9, "Example boss bar message"); }