From 491119f4aa69a2a5a2fca24ce97e47105dcc8832 Mon Sep 17 00:00:00 2001 From: Norbert Dejlich Date: Sat, 29 Jun 2024 13:55:50 +0200 Subject: [PATCH 1/3] GH-18 Refactor code base, add bukkit sub-module instead of static bukkit impl in core. (#35) * Remove bukkit. Refactor old legacy code. * Bump adventure. Add example. Update bukkit packages. * Add adventure dependency to unit tests. * Add canada stellardrift snapshot sus repository. --- .gitignore | 3 +- buildSrc/src/main/kotlin/Versions.kt | 8 + .../multification-repositories.gradle.kts | 1 + examples/bukkit/build.gradle.kts | 60 +++++++ .../example/bukkit/ExamplePlugin.java | 57 ++++++ .../example/bukkit/command/FlyCommand.java | 57 ++++++ .../example/bukkit/command/GiveCommand.java | 55 ++++++ .../bukkit/command/TeleportCommand.java | 32 ++++ .../bukkit/config/ConfigurationManager.java | 36 ++++ .../example/bukkit/config/MessagesConfig.java | 32 ++++ .../multification/YourMultification.java | 48 +++++ multification-bukkit/build.gradle.kts | 13 ++ .../example/bukkit/BukkitLocaleProvider.java | 16 ++ .../example/bukkit/BukkitMultification.java | 38 ++++ .../example/bukkit/BukkitViewerProvider.java | 34 ++++ .../example/bukkit/notice/BukkitNotice.java | 54 ++++++ .../bukkit/notice/BukkitNoticeKey.java | 11 ++ .../notice/resolver/sound/SoundBukkit.java | 44 +++++ .../resolver/sound/SoundBukkitResolver.java | 87 +++++++++ multification-cdn/build.gradle.kts | 5 +- .../cdn/MultificationNoticeCdnComposer.java | 165 +++++------------- .../multification/cdn/NoticeComposerTest.java | 158 +++++++++++++---- multification-core/build.gradle.kts | 6 +- .../multification/Multification.java | 34 +++- .../PlainComponentSerializer.java | 4 +- .../multification/notice/Notice.java | 101 ++++++----- .../multification/notice/NoticeBroadcast.java | 8 +- .../notice/NoticeBroadcastImpl.java | 45 +++-- .../multification/notice/NoticeContent.java | 24 --- .../multification/notice/NoticeKey.java | 29 +++ .../multification/notice/NoticeKeyImpl.java | 6 + .../multification/notice/NoticePart.java | 12 +- .../multification/notice/NoticeType.java | 57 ++++-- .../notice/resolver/NoticeContent.java | 5 + .../resolver/NoticeDeserializeResult.java | 6 + .../notice/resolver/NoticeResolver.java | 19 ++ .../resolver/NoticeResolverDefaults.java | 32 ++++ .../resolver/NoticeResolverRegistry.java | 107 ++++++++++++ .../notice/resolver/NoticeSerdesResult.java | 35 ++++ .../resolver/actionbar/ActionbarContent.java | 11 ++ .../resolver/actionbar/ActionbarResolver.java | 52 ++++++ .../notice/resolver/chat/ChatContent.java | 11 ++ .../notice/resolver/chat/ChatResolver.java | 71 ++++++++ .../notice/resolver/sound/SoundAdventure.java | 9 + .../sound/SoundAdventureResolver.java | 79 +++++++++ .../notice/resolver/text/TextContent.java | 10 ++ .../resolver/text/TextContentResolver.java | 15 ++ .../title/AbstractTitleContentResolver.java | 48 +++++ .../resolver/title/SubtitleResolver.java | 20 +++ .../title/SubtitleWithEmptyTitleResolver.java | 21 +++ .../notice/resolver/title/TimesResolver.java | 65 +++++++ .../notice/resolver/title/TitleContent.java | 11 ++ .../notice/resolver/title/TitleHide.java | 6 + .../resolver/title/TitleHideResolver.java | 38 ++++ .../notice/resolver/title/TitleResolver.java | 20 +++ .../notice/resolver/title/TitleTimes.java | 7 + .../title/TitleWithEmptySubtitleResolver.java | 21 +++ .../platform/PlatformBroadcaster.java | 20 ++- .../platform/PlatformBroadcasterImpl.java | 83 +-------- settings.gradle.kts | 3 + 60 files changed, 1808 insertions(+), 357 deletions(-) create mode 100644 buildSrc/src/main/kotlin/Versions.kt create mode 100644 examples/bukkit/build.gradle.kts create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/ExamplePlugin.java create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/FlyCommand.java create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/GiveCommand.java create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/TeleportCommand.java create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/ConfigurationManager.java create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/MessagesConfig.java create mode 100644 examples/bukkit/src/main/java/com/eternalcode/example/bukkit/multification/YourMultification.java create mode 100644 multification-bukkit/build.gradle.kts create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/BukkitLocaleProvider.java create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/BukkitMultification.java create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/BukkitViewerProvider.java create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNotice.java create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNoticeKey.java create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkit.java create mode 100644 multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkitResolver.java rename multification-core/src/com/eternalcode/multification/{platform => adventure}/PlainComponentSerializer.java (85%) delete mode 100644 multification-core/src/com/eternalcode/multification/notice/NoticeContent.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/NoticeKey.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/NoticeKeyImpl.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/NoticeContent.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/NoticeDeserializeResult.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverDefaults.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/NoticeSerdesResult.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarContent.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatContent.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventure.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventureResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContent.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContentResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/AbstractTitleContentResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleWithEmptyTitleResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TimesResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleContent.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHide.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHideResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleResolver.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleTimes.java create mode 100644 multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleWithEmptySubtitleResolver.java diff --git a/.gitignore b/.gitignore index 3060674..d1211d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .gradle build -.idea \ No newline at end of file +.idea +run \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt new file mode 100644 index 0000000..4a83d2c --- /dev/null +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -0,0 +1,8 @@ + +object Versions { + + const val ADVENTURE_PLATFORM_BUKKIT = "4.3.3-SNAPSHOT" + const val ADVENTURE_API = "4.18.0-SNAPSHOT" + const val CDN = "1.14.5" + +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/multification-repositories.gradle.kts b/buildSrc/src/main/kotlin/multification-repositories.gradle.kts index fe16b0b..0e8c70a 100644 --- a/buildSrc/src/main/kotlin/multification-repositories.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-repositories.gradle.kts @@ -8,4 +8,5 @@ repositories { maven("https://papermc.io/repo/repository/maven-public/") // paper, adventure, velocity maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // spigot maven("https://repo.panda-lang.org/releases/") // expressible + maven("https://repo.stellardrift.ca/repository/snapshots/") } \ No newline at end of file diff --git a/examples/bukkit/build.gradle.kts b/examples/bukkit/build.gradle.kts new file mode 100644 index 0000000..5526706 --- /dev/null +++ b/examples/bukkit/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + id("java") + id("com.github.johnrengelman.shadow") version "8.1.1" + id("net.minecrell.plugin-yml.bukkit") version "0.6.0" + id("xyz.jpenilla.run-paper") version "2.3.0" +} + +version = "1.0.0-SNAPSHOT" + +repositories { + mavenCentral() + maven("https://repo.panda-lang.org/releases/") + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + maven("https://repo.stellardrift.ca/repository/snapshots/") +} + +dependencies { + compileOnly("org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT") + + implementation("net.kyori:adventure-platform-bukkit:${Versions.ADVENTURE_PLATFORM_BUKKIT}") + implementation("net.kyori:adventure-text-minimessage:${Versions.ADVENTURE_API}") + implementation("dev.rollczi:litecommands-bukkit:3.4.1") + // implementation("com.eternalcode:multification-bukkit:1.0.3") // <-- uncomment in your project + // implementation("com.eternalcode:multification-cdn:1.0.3") // <-- uncomment in your project + + implementation(project(":multification-bukkit")) // don't use this line in your build.gradle + implementation(project(":multification-cdn")) // don't use this line in your build.gradle +} + +val pluginName = "ExamplePlugin" +val packageName = "com.eternalcode.example.bukkit" + +bukkit { + main = "$packageName.$pluginName" + apiVersion = "1.13" + author = "Rollczi" + name = pluginName + version = "${project.version}" +} + +tasks.shadowJar { + archiveFileName.set("$pluginName v${project.version}.jar") + + listOf( + "com.eternalcode.multification", + "net.dzikoysk.cdn", + "panda.std", + "panda.utilities", + "net.kyori", + ).forEach { relocate(it, "$packageName.libs.$it") } +} + +sourceSets.test { + java.setSrcDirs(emptyList()) + resources.setSrcDirs(emptyList()) +} + +tasks.runServer { + minecraftVersion("1.20.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 new file mode 100644 index 0000000..9613c2d --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/ExamplePlugin.java @@ -0,0 +1,57 @@ +package com.eternalcode.example.bukkit; + +import com.eternalcode.example.bukkit.command.GiveCommand; +import com.eternalcode.example.bukkit.command.TeleportCommand; +import com.eternalcode.example.bukkit.config.ConfigurationManager; +import com.eternalcode.example.bukkit.config.MessagesConfig; +import com.eternalcode.example.bukkit.multification.YourMultification; +import com.eternalcode.example.bukkit.command.FlyCommand; +import dev.rollczi.litecommands.bukkit.LiteBukkitFactory; +import dev.rollczi.litecommands.LiteCommands; +import net.kyori.adventure.platform.AudienceProvider; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; + +public class ExamplePlugin extends JavaPlugin { + + private LiteCommands liteCommands; + private AudienceProvider audienceProvider; + + @Override + public void onEnable() { + // Kyori Adventure platform + audienceProvider = BukkitAudiences.create(this); + MiniMessage miniMessage = MiniMessage.miniMessage(); + + // Config & Multification + MessagesConfig messagesConfig = new MessagesConfig(); + YourMultification multification = new YourMultification(messagesConfig, audienceProvider, miniMessage); + + ConfigurationManager configurationManager = new ConfigurationManager(this.getDataFolder(), multification.getNoticeRegistry()); + configurationManager.load(messagesConfig, "messages.yml"); + + this.liteCommands = LiteBukkitFactory.builder() + .commands( + new TeleportCommand(multification), + new FlyCommand(multification), + new GiveCommand(multification) + ) + .build(); + } + + @Override + public void onDisable() { + // unregister all commands from bukkit + if (this.liteCommands != null) { + this.liteCommands.unregister(); + } + + // close audience provider + if (this.audienceProvider != null) { + this.audienceProvider.close(); + } + } + +} diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/FlyCommand.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/FlyCommand.java new file mode 100644 index 0000000..cddf1a6 --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/FlyCommand.java @@ -0,0 +1,57 @@ +package com.eternalcode.example.bukkit.command; + +import com.eternalcode.example.bukkit.multification.YourMultification; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.permission.Permission; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@Command(name = "fly", aliases = {"f"}) +@Permission("example.fly") +public class FlyCommand { + + private final YourMultification multification; + + public FlyCommand(YourMultification multification) { + this.multification = multification; + } + + @Execute + void execute(@Context Player sender) { + sender.setAllowFlight(!sender.getAllowFlight()); + multification.create() + .player(sender.getUniqueId()) + .notice(messages -> messages.selfFlyCommandMessage) + .placeholder("{state}", sender.getAllowFlight() ? "enabled" : "disabled") + .send(); + } + + @Execute + void executeOther(@Context CommandSender sender, @Arg Player target) { + target.setAllowFlight(!target.getAllowFlight()); + + multification.create() + .player(target.getUniqueId()) + .notice(messages -> messages.selfFlyCommandMessage) + .placeholder("{state}", target.getAllowFlight() ? "enabled" : "disabled") + .send(); + + multification.create() + .viewer(sender) + .notice(messages -> messages.otherFlyCommandMessage) + .placeholder("{state}", target.getAllowFlight() ? "enabled" : "disabled") + .placeholder("{player}", target.getName()) + .send(); + + multification.create() + .player(target.getUniqueId()) + .notice(messages -> messages.otherFlyCommandMessageTarget) + .placeholder("{state}", target.getAllowFlight() ? "enabled" : "disabled") + .placeholder("{player}", sender.getName()) + .send(); + } + +} diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/GiveCommand.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/GiveCommand.java new file mode 100644 index 0000000..dce237c --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/GiveCommand.java @@ -0,0 +1,55 @@ +package com.eternalcode.example.bukkit.command; + +import com.eternalcode.example.bukkit.multification.YourMultification; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.command.Command; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.optional.OptionalArg; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@Command(name = "give") +public class GiveCommand { + + private final YourMultification multification; + + public GiveCommand(YourMultification multification) { + this.multification = multification; + } + + @Execute + public void execute( + @Context CommandSender commandSender, + @Arg("nick") Player target, + @Arg("item") Material item, + @OptionalArg("amount") Integer amount + ) { + if (amount == null) { + amount = 1; + } + + multification.create() + .player(target.getUniqueId()) + .notice(messages -> messages.receiverGiveCommandMessage) + .placeholder("{player}", commandSender.getName()) + .placeholder("{amount}", amount.toString()) + .placeholder("{item}", item.name()) + .send(); + + + multification + .create() + .viewer(commandSender) + .notice(messages -> messages.senderGiveCommandMessage) + .placeholder("{player}", target.getName()) + .placeholder("{amount}", amount.toString()) + .placeholder("{item}", item.name()) + .send(); + + target.getInventory().addItem(new ItemStack(item, amount)); + } + +} diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/TeleportCommand.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/TeleportCommand.java new file mode 100644 index 0000000..9b9dd70 --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/command/TeleportCommand.java @@ -0,0 +1,32 @@ +package com.eternalcode.example.bukkit.command; + +import com.eternalcode.example.bukkit.multification.YourMultification; +import dev.rollczi.litecommands.annotations.argument.Arg; +import dev.rollczi.litecommands.annotations.context.Context; +import dev.rollczi.litecommands.annotations.execute.Execute; +import dev.rollczi.litecommands.annotations.permission.Permission; +import dev.rollczi.litecommands.annotations.command.Command; +import org.bukkit.entity.Player; + +@Command(name = "teleport", aliases = "tp") +@Permission("dev.rollczi.teleport") +public class TeleportCommand { + + private final YourMultification multification; + + public TeleportCommand(YourMultification multification) { + this.multification = multification; + } + + @Execute + public void teleportSelf(@Context Player sender, @Arg Player to) { + multification.create() + .player(sender.getUniqueId()) + .notice(messages -> messages.selfTeleportCommandMessage) + .placeholder("{player}", to.getName()) + .send(); + + sender.teleport(to.getLocation()); + } + +} 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 new file mode 100644 index 0000000..ab2df30 --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/ConfigurationManager.java @@ -0,0 +1,36 @@ +package com.eternalcode.example.bukkit.config; + +import com.eternalcode.multification.cdn.MultificationNoticeCdnComposer; +import com.eternalcode.multification.notice.Notice; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; +import java.io.File; +import net.dzikoysk.cdn.Cdn; +import net.dzikoysk.cdn.CdnFactory; +import net.dzikoysk.cdn.reflect.Visibility; +import net.dzikoysk.cdn.source.Source; +public class ConfigurationManager { + + private final Cdn cdn; + private final File dataFolder; + + public ConfigurationManager(File dataFolder, NoticeResolverRegistry resolverRegistry) { + this.dataFolder = dataFolder; + this.cdn = CdnFactory + .createYamlLike() + .getSettings() + .withComposer(Notice.class, new MultificationNoticeCdnComposer(resolverRegistry)) + .withMemberResolver(Visibility.PACKAGE_PRIVATE) + .build(); + } + + public T load(T config, String fileName) { + this.cdn.load(Source.of(this.dataFolder, fileName), config) + .orThrow(RuntimeException::new); + + this.cdn.render(config, Source.of(this.dataFolder, fileName)) + .orThrow(RuntimeException::new); + + return config; + } + +} 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 new file mode 100644 index 0000000..07e2c33 --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/config/MessagesConfig.java @@ -0,0 +1,32 @@ +package com.eternalcode.example.bukkit.config; + +import com.eternalcode.example.bukkit.notice.BukkitNotice; +import com.eternalcode.multification.notice.Notice; +import net.dzikoysk.cdn.entity.Description; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; + +public class MessagesConfig { + @Description("# Fly command message") + public Notice selfFlyCommandMessage = Notice.chat("You have toggled fly mode. It is now {state}."); + public Notice otherFlyCommandMessage = Notice.chat("You have toggled fly mode for {player}. It is now {state}."); + public Notice otherFlyCommandMessageTarget = BukkitNotice.builder() + .chat("Your fly mode has been toggled by {player}. It is now {state}.") + .sound(Sound.ENTITY_ITEM_PICKUP) + .build(); + + @Description("# Give command message") + public Notice senderGiveCommandMessage = Notice.title("You have given {amount}x {item} to {player}."); + public Notice receiverGiveCommandMessage = BukkitNotice.builder() + .title("You have received {amount}x {item} from {player}.") + .subtitle("Remember to say thank you!") + .sound(Sound.ENTITY_ITEM_PICKUP) + .build(); + + @Description("# Teleport command message") + public Notice selfTeleportCommandMessage = BukkitNotice.builder() + .actionBar("You have been teleported to {player}.") + .sound(Sound.ENTITY_ENDERMAN_TELEPORT, SoundCategory.PLAYERS, 2.0F, 2.0F) + .build(); + +} diff --git a/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/multification/YourMultification.java b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/multification/YourMultification.java new file mode 100644 index 0000000..13ca12e --- /dev/null +++ b/examples/bukkit/src/main/java/com/eternalcode/example/bukkit/multification/YourMultification.java @@ -0,0 +1,48 @@ +package com.eternalcode.example.bukkit.multification; + +import com.eternalcode.multification.adventure.AudienceConverter; +import com.eternalcode.example.bukkit.BukkitMultification; +import com.eternalcode.multification.translation.TranslationProvider; +import com.eternalcode.example.bukkit.config.MessagesConfig; +import net.kyori.adventure.platform.AudienceProvider; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class YourMultification extends BukkitMultification { + + private final MessagesConfig messagesConfig; + private final AudienceProvider audienceProvider; + private final MiniMessage miniMessage; + + public YourMultification(MessagesConfig messagesConfig, AudienceProvider audienceProvider, MiniMessage miniMessage) { + this.messagesConfig = messagesConfig; + this.audienceProvider = audienceProvider; + this.miniMessage = miniMessage; + } + + @Override + protected @NotNull TranslationProvider translationProvider() { + return locale -> this.messagesConfig; + } + + @Override + protected @NotNull ComponentSerializer serializer() { + return this.miniMessage; + } + + @Override + protected @NotNull AudienceConverter audienceConverter() { + return commandSender -> { + if (commandSender instanceof Player player) { + return this.audienceProvider.player(player.getUniqueId()); + } + + return this.audienceProvider.console(); + }; + } + +} diff --git a/multification-bukkit/build.gradle.kts b/multification-bukkit/build.gradle.kts new file mode 100644 index 0000000..fa0cee9 --- /dev/null +++ b/multification-bukkit/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + `multification-java` + `multification-java-17` + `multification-repositories` + `multification-publish` +} + +dependencies { + api(project(":multification-core")) + + compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + testImplementation("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") +} \ No newline at end of file diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitLocaleProvider.java b/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitLocaleProvider.java new file mode 100644 index 0000000..6178a1f --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitLocaleProvider.java @@ -0,0 +1,16 @@ +package com.eternalcode.example.bukkit; + +import com.eternalcode.multification.locate.LocaleProvider; +import java.util.Locale; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class BukkitLocaleProvider implements LocaleProvider { + + @Override + public @NotNull Locale provide(CommandSender commandSender) { + return commandSender instanceof Player player ? new Locale(player.getLocale()) : Locale.getDefault(); + } + +} diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitMultification.java b/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitMultification.java new file mode 100644 index 0000000..4e96ad4 --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitMultification.java @@ -0,0 +1,38 @@ +package com.eternalcode.example.bukkit; + +import com.eternalcode.multification.Multification; +import com.eternalcode.example.bukkit.notice.resolver.sound.SoundBukkitResolver; +import com.eternalcode.multification.executor.AsyncExecutor; +import com.eternalcode.multification.locate.LocaleProvider; +import com.eternalcode.multification.viewer.ViewerProvider; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; + +public abstract class BukkitMultification extends Multification { + + public static final ViewerProvider DEFAULT_VIEWER_PROVIDER = new BukkitViewerProvider(); + public static final LocaleProvider DEFAULT_LOCALE_PROVIDER = new BukkitLocaleProvider(); + + protected BukkitMultification() { + super(); + this.noticeRegistry.registerResolver(new SoundBukkitResolver()); + } + + @Override + protected @NotNull ViewerProvider viewerProvider() { + return DEFAULT_VIEWER_PROVIDER; + } + + @Override + protected @NotNull LocaleProvider localeProvider() { + return DEFAULT_LOCALE_PROVIDER; + } + + @Override + protected @NotNull AsyncExecutor asyncExecutor() { + return (runnable) -> Bukkit.getScheduler().runTaskAsynchronously(JavaPlugin.getProvidingPlugin(BukkitMultification.class), runnable); + } + +} diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitViewerProvider.java b/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitViewerProvider.java new file mode 100644 index 0000000..bb78181 --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/BukkitViewerProvider.java @@ -0,0 +1,34 @@ +package com.eternalcode.example.bukkit; + +import com.eternalcode.multification.viewer.ViewerProvider; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; + +class BukkitViewerProvider implements ViewerProvider { + @Override + public CommandSender console() { + return Bukkit.getConsoleSender(); + } + + @Override + public CommandSender player(UUID uuid) { + return Bukkit.getPlayer(uuid); + } + + @Override + public Collection onlinePlayers() { + return Collections.unmodifiableCollection(Bukkit.getOnlinePlayers()); + } + + @Override + public Collection all() { + Collection viewers = new ArrayList<>(this.onlinePlayers()); + viewers.add(this.console()); + + return Collections.unmodifiableCollection(viewers); + } +} diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNotice.java b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNotice.java new file mode 100644 index 0000000..76b8b99 --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNotice.java @@ -0,0 +1,54 @@ +package com.eternalcode.example.bukkit.notice; + +import com.eternalcode.example.bukkit.notice.resolver.sound.SoundBukkit; +import com.eternalcode.multification.notice.Notice; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; + +public class BukkitNotice { + + public static Notice sound(Sound sound, SoundCategory category, float volume, float pitch) { + return BukkitNotice.builder() + .sound(sound, category, pitch, volume) + .build(); + } + + public static Notice sound(Sound sound, float volume, float pitch) { + return BukkitNotice.builder() + .sound(sound, pitch, volume) + .build(); + } + + public static Notice sound(Sound sound) { + return BukkitNotice.builder() + .sound(sound) + .build(); + } + + public static BukkitNotice.Builder builder() { + return new Builder(); + } + + public static class Builder extends Notice.BaseBuilder { + + public Builder sound(Sound sound, SoundCategory category, float volume, float pitch) { + return this.withPart(BukkitNoticeKey.SOUND, new SoundBukkit(sound, category, pitch, volume)); + } + + public Builder sound(Sound sound, float volume, float pitch) { + return this.withPart(BukkitNoticeKey.SOUND, new SoundBukkit(sound, null, pitch, volume)); + } + + public Builder sound(Sound sound) { + return this.withPart(BukkitNoticeKey.SOUND, new SoundBukkit(sound, null, SoundBukkit.PITCH_UNSET, SoundBukkit.VOLUME_UNSET)); + } + + @Override + protected Builder getThis() { + return this; + } + + } + + +} diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNoticeKey.java b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNoticeKey.java new file mode 100644 index 0000000..6d0c91e --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/BukkitNoticeKey.java @@ -0,0 +1,11 @@ +package com.eternalcode.example.bukkit.notice; + +import com.eternalcode.example.bukkit.notice.resolver.sound.SoundBukkit; +import com.eternalcode.multification.notice.resolver.NoticeContent; +import com.eternalcode.multification.notice.NoticeKey; + +public interface BukkitNoticeKey extends NoticeKey { + + NoticeKey SOUND = NoticeKey.of("sound", SoundBukkit.class); + +} diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkit.java b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkit.java new file mode 100644 index 0000000..017669e --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkit.java @@ -0,0 +1,44 @@ +package com.eternalcode.example.bukkit.notice.resolver.sound; + +import com.eternalcode.multification.notice.resolver.NoticeContent; +import net.kyori.adventure.sound.Sound.Source; +import org.bukkit.Sound; +import org.bukkit.SoundCategory; +import org.jetbrains.annotations.Nullable; + +public record SoundBukkit(Sound sound, @Nullable SoundCategory 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 Source toKyoriCategory() { + if (category == null) { + return Source.MASTER; + } + + return switch (category) { + case MASTER -> Source.MASTER; + case MUSIC -> Source.MUSIC; + case RECORDS -> Source.RECORD; + case WEATHER -> Source.WEATHER; + case BLOCKS -> Source.BLOCK; + case HOSTILE -> Source.HOSTILE; + case NEUTRAL -> Source.NEUTRAL; + case PLAYERS -> Source.PLAYER; + case AMBIENT -> Source.AMBIENT; + case VOICE -> Source.VOICE; + }; + } + +} diff --git a/multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkitResolver.java b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkitResolver.java new file mode 100644 index 0000000..efafa87 --- /dev/null +++ b/multification-bukkit/src/com/eternalcode/example/bukkit/notice/resolver/sound/SoundBukkitResolver.java @@ -0,0 +1,87 @@ +package com.eternalcode.example.bukkit.notice.resolver.sound; + +import com.eternalcode.example.bukkit.notice.BukkitNoticeKey; +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import com.eternalcode.multification.notice.resolver.NoticeResolver; +import java.util.Optional; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.bukkit.SoundCategory; + +public class SoundBukkitResolver implements NoticeResolver { + + private static final String MUSIC = "%s"; + private static final String MUSIC_WITH_PITCH_VOLUME = "%s %s %s"; + private static final String MUSIC_FULL = "%s %s %s %s"; + + @Override + public NoticeKey noticeKey() { + return BukkitNoticeKey.SOUND; + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, SoundBukkit content) { + String soundKey = content.sound().getKey().getKey(); + Sound sound = Sound.sound( + Key.key(soundKey), + Sound.Source.valueOf(content.toKyoriCategory().name()), + content.volumeOrDefault(), + content.pitchOrDefault() + ); + + audience.playSound(sound); + } + + @Override + public NoticeSerdesResult serialize(SoundBukkit content) { + if (content.category() == null) { + if (content.pitch() == SoundBukkit.PITCH_UNSET || content.volume() == SoundBukkit.VOLUME_UNSET) { + return new NoticeSerdesResult.Single(String.format(MUSIC, content.sound().name())); + } + + return new NoticeSerdesResult.Single(String.format(MUSIC_WITH_PITCH_VOLUME, + content.sound().name(), + content.pitch(), + content.volume() + )); + } + + return new NoticeSerdesResult.Single(String.format(MUSIC_FULL, + content.sound().name(), + content.category().name(), + content.pitch(), + content.volume() + )); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + Optional firstElement = result.firstElement(); + + if (firstElement.isEmpty()) { + return Optional.empty(); + } + + 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)); + } + + 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]); + 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]); + + return Optional.of(new SoundBukkit(sound, category, pitch, volume)); + } + +} diff --git a/multification-cdn/build.gradle.kts b/multification-cdn/build.gradle.kts index ef083e7..4980587 100644 --- a/multification-cdn/build.gradle.kts +++ b/multification-cdn/build.gradle.kts @@ -9,8 +9,9 @@ plugins { dependencies { api(project(":multification-core")) - api("net.dzikoysk:cdn:1.14.4") + api("net.dzikoysk:cdn:${Versions.CDN}") - compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + testImplementation(project(":multification-bukkit")) testImplementation("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + testImplementation("net.kyori:adventure-api:${Versions.ADVENTURE_API}") } diff --git a/multification-cdn/src/com/eternalcode/multification/cdn/MultificationNoticeCdnComposer.java b/multification-cdn/src/com/eternalcode/multification/cdn/MultificationNoticeCdnComposer.java index 6cdea0d..2b91859 100644 --- a/multification-cdn/src/com/eternalcode/multification/cdn/MultificationNoticeCdnComposer.java +++ b/multification-cdn/src/com/eternalcode/multification/cdn/MultificationNoticeCdnComposer.java @@ -1,18 +1,17 @@ package com.eternalcode.multification.cdn; +import com.eternalcode.multification.Multification; import com.eternalcode.multification.notice.Notice; -import static com.eternalcode.multification.notice.NoticeContent.Music; -import static com.eternalcode.multification.notice.NoticeContent.None; -import static com.eternalcode.multification.notice.NoticeContent.Text; -import static com.eternalcode.multification.notice.NoticeContent.Times; +import com.eternalcode.multification.notice.resolver.NoticeDeserializeResult; +import com.eternalcode.multification.notice.resolver.NoticeContent; +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import com.eternalcode.multification.notice.resolver.chat.ChatContent; -import com.eternalcode.multification.notice.NoticeContent; import com.eternalcode.multification.notice.NoticePart; -import com.eternalcode.multification.notice.NoticeType; -import com.eternalcode.multification.time.DurationParser; -import com.eternalcode.multification.time.TemporalAmountParser; -import java.time.Duration; import java.util.List; +import java.util.Optional; import net.dzikoysk.cdn.CdnSettings; import net.dzikoysk.cdn.CdnUtils; import net.dzikoysk.cdn.model.Element; @@ -22,18 +21,21 @@ import net.dzikoysk.cdn.module.standard.StandardOperators; import net.dzikoysk.cdn.reflect.TargetType; import net.dzikoysk.cdn.serdes.Composer; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; import panda.std.Result; public class MultificationNoticeCdnComposer implements Composer { private static final String EMPTY_NOTICE = "[]"; - private static final String TIMES = "%s %s %s"; - private static final String MUSIC_WITH_CATEGORY = "%s %s %s %s"; - private static final String MUSIC_WITHOUT_CATEGORY = "%s %s %s"; - private static final TemporalAmountParser DURATION_PARSER = DurationParser.TIME_UNITS; + private final NoticeResolverRegistry noticeRegistry; + + public MultificationNoticeCdnComposer(Multification multification) { + this.noticeRegistry = multification.getNoticeRegistry(); + } + + public MultificationNoticeCdnComposer(NoticeResolverRegistry noticeRegistry) { + this.noticeRegistry = noticeRegistry; + } @Override public Result, ? extends Exception> serialize(CdnSettings settings, List description, String key, TargetType type, Notice entity) { @@ -61,15 +63,15 @@ private Result, Exception> serializerUndisclosedChat(SerializeContext NoticePart part = parts.get(0); - if (part.type() != NoticeType.CHAT) { + if (!NoticeKey.CHAT.equals(part.noticeKey())) { return Result.error(new IllegalStateException("Notice is not chat")); } - if (!(part.content() instanceof Text text)) { + if (!(part.content() instanceof ChatContent chat)) { return Result.error(new IllegalStateException("Notice is not text")); } - List messages = text.messages(); + List messages = chat.messages(); if (messages.isEmpty()) { return Result.ok(empty(context)); @@ -87,70 +89,20 @@ private Result, Exception> serializeAll(SerializeContext context) { Section section = new Section(context.description, context.key); for (NoticePart part : parts) { - String key = part.type().getKey(); - - if (part.content() instanceof Text text) { - List messages = text.messages(); - - if (messages.isEmpty()) { - continue; - } - - if (messages.size() == 1) { - section.append(oneLine(key, List.of(), messages.get(0))); - continue; - } - - section.append(toSection(key, List.of(), messages)); - continue; - } - - if (part.content() instanceof Times times) { - String textTimes = TIMES.formatted( - DURATION_PARSER.format(times.fadeIn()), - DURATION_PARSER.format(times.stay()), - DURATION_PARSER.format(times.fadeOut()) - ); - - section.append(new Entry(context.description, key, new Piece(textTimes))); - continue; - } - - if (part.content() instanceof None) { - for (NoticeType type : NoticeType.values()) { - if (type != part.type()) { - continue; - } - - section.append(new Entry(context.description, key, "true")); - } + String key = part.noticeKey().key(); + NoticeSerdesResult result = noticeRegistry.serialize(part); + if (result instanceof NoticeSerdesResult.Single single) { + section.append(new Entry(context.description, key, single.element())); continue; } - if (part.content() instanceof Music music) { - SoundCategory category = music.category(); - - Entry entry = category == null - ? - new Entry(context.description, key, new Piece(MUSIC_WITHOUT_CATEGORY.formatted( - music.sound().name(), - String.valueOf(music.pitch()), - String.valueOf(music.volume()) - ))) - : - new Entry(context.description, key, new Piece(MUSIC_WITH_CATEGORY.formatted( - music.sound().name(), - category.name(), - String.valueOf(music.pitch()), - String.valueOf(music.volume()) - ))); - - section.append(entry); + if (result instanceof NoticeSerdesResult.Multi multi) { + section.append(toSection(key, context.description, multi.elements())); continue; } - return Result.error(new UnsupportedOperationException("Unsupported notice type: " + part.type())); + return Result.error(new UnsupportedOperationException("Unsupported notice type: " + part.noticeKey() + ": " + part.content())); } return Result.ok(section); @@ -199,14 +151,21 @@ private Result deserializeEmpty(DeserializeContext context) { } private Result deserializeAll(DeserializeContext context) { + // - "Hello, world!" if (context.source() instanceof Piece piece) { return Result.ok(Notice.chat(CdnUtils.destringify(piece.getValue()))); } + // example: "Hello, world!" if (context.source() instanceof Entry entry) { return Result.ok(Notice.chat(CdnUtils.destringify(entry.getPieceValue()))); } + /* + example: + - "Hello, world!" + action-bar: "Hello, world!" + */ if (context.source() instanceof Section section) { return this.deserializeSection(section); } @@ -218,57 +177,26 @@ private Result deserializeSection(Section section) { Notice.Builder builder = Notice.builder(); for (Element element : section.getValue()) { + // - "Hello, world!" if (element instanceof Piece piece) { builder.chat(this.deserializePiece(piece)); continue; } + // actionbar: "Hello, world!" if (element instanceof Entry entry) { String value = this.deserializePiece(entry.getValue()); - NoticeType noticeType = NoticeType.fromKey(entry.getName()); - - if (noticeType.contentType() == Text.class) { - builder.withPart(new NoticePart<>(noticeType, new Text(List.of(value)))); - continue; - } - - if (noticeType.contentType() == Times.class) { - String[] times = value.split(" "); - - if (times.length != 3) { - return Result.error(new IllegalStateException("Invalid times format")); - } - - Duration fadeIn = DURATION_PARSER.parse(times[0]); - Duration stay = DURATION_PARSER.parse(times[1]); - Duration fadeOut = DURATION_PARSER.parse(times[2]); - - builder.withPart(new NoticePart<>(noticeType, new Times(fadeIn, stay, fadeOut))); - continue; - } - - if (noticeType.contentType() == Music.class) { - String[] music = value.split(" "); + String key = entry.getName(); - if (music.length < 3 || music.length > 4) { - return Result.error(new IllegalStateException("Invalid music format")); - } + NoticeSerdesResult.Single result = new NoticeSerdesResult.Single(value); + Optional> optional = this.noticeRegistry.deserialize(key, result); - Sound sound = Sound.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]); - - builder.withPart(new NoticePart<>(noticeType, new Music(sound, category, pitch, volume))); - continue; + if (optional.isEmpty()) { + return Result.error(new IllegalStateException("Unsupported notice type: " + key + ": " + value)); } - if (noticeType.contentType() == None.class) { - builder.withPart(new NoticePart<>(noticeType, None.INSTANCE)); - continue; - } - - return Result.error(new UnsupportedOperationException("Unsupported notice type: " + noticeType)); + this.withPart(builder, optional.get()); + continue; } if (element instanceof Section subSection) { @@ -289,6 +217,10 @@ private Result deserializeSection(Section section) { return Result.ok(builder.build()); } + private void withPart(Notice.Builder builder, NoticeDeserializeResult result) { + builder.withPart(result.noticeKey(), result.content()); + } + private String deserializePiece(Piece piece) { String value = piece.getValue(); @@ -299,8 +231,7 @@ private String deserializePiece(Piece piece) { return CdnUtils.destringify(value.trim()); } - record DeserializeContext(CdnSettings settings, Element source, TargetType type, Notice defaultValue, - boolean entryAsRecord) { + record DeserializeContext(CdnSettings settings, Element source, TargetType type, Notice defaultValue, boolean entryAsRecord) { } } diff --git a/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java b/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java index 84c8511..c3d0c30 100644 --- a/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java +++ b/multification-cdn/test/com/eternalcode/multification/cdn/NoticeComposerTest.java @@ -1,11 +1,19 @@ package com.eternalcode.multification.cdn; +import com.eternalcode.example.bukkit.notice.BukkitNotice; +import com.eternalcode.example.bukkit.notice.BukkitNoticeKey; +import com.eternalcode.example.bukkit.notice.resolver.sound.SoundBukkit; +import com.eternalcode.example.bukkit.notice.resolver.sound.SoundBukkitResolver; import com.eternalcode.multification.notice.Notice; -import com.eternalcode.multification.notice.NoticeType; -import static com.eternalcode.multification.notice.NoticeContent.Music; -import static com.eternalcode.multification.notice.NoticeContent.None; -import static com.eternalcode.multification.notice.NoticeContent.Text; -import static com.eternalcode.multification.notice.NoticeContent.Times; +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeResolverDefaults; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; +import com.eternalcode.multification.notice.resolver.actionbar.ActionbarContent; +import com.eternalcode.multification.notice.resolver.chat.ChatContent; +import com.eternalcode.multification.notice.resolver.title.TitleContent; +import com.eternalcode.multification.notice.resolver.title.TitleHide; +import com.eternalcode.multification.notice.resolver.sound.SoundAdventure; +import com.eternalcode.multification.notice.resolver.title.TitleTimes; import com.eternalcode.multification.notice.NoticePart; import java.time.Duration; import net.dzikoysk.cdn.Cdn; @@ -13,6 +21,7 @@ import net.dzikoysk.cdn.reflect.Visibility; import net.dzikoysk.cdn.source.Source; +import net.kyori.adventure.key.Key; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; @@ -25,13 +34,15 @@ @SuppressWarnings("FieldMayBeFinal") class NoticeComposerTest { + private static final NoticeResolverRegistry registry = NoticeResolverDefaults.createRegistry() + .registerResolver(new SoundBukkitResolver()); + private static final Cdn cdn = CdnFactory.createYamlLike() .getSettings() - .withComposer(Notice.class, new MultificationNoticeCdnComposer()) + .withComposer(Notice.class, new MultificationNoticeCdnComposer(registry)) .withMemberResolver(Visibility.PACKAGE_PRIVATE) .build(); - static class ConfigEmpty { Notice notice = Notice.empty(); } @@ -64,9 +75,9 @@ void serializeSimpleChatNoticeToOneLineEntry() { assertEquals(1, oneLineChat.notice.parts().size()); NoticePart part = oneLineChat.notice.parts().get(0); - Text text = assertInstanceOf(Text.class, part.content()); - assertEquals(NoticeType.CHAT, part.type()); - assertEquals("Hello world", text.messages().get(0)); + ChatContent chat = assertInstanceOf(ChatContent.class, part.content()); + assertEquals(NoticeKey.CHAT, part.noticeKey()); + assertEquals("Hello world", chat.messages().get(0)); } @@ -86,10 +97,10 @@ void serializeSimpleChatNoticeToMultilineEntry() { assertEquals(1, configMultiLineChat.notice.parts().size()); NoticePart part = configMultiLineChat.notice.parts().get(0); - Text text = assertInstanceOf(Text.class, part.content()); - assertEquals(NoticeType.CHAT, part.type()); - assertEquals("First line", text.messages().get(0)); - assertEquals("Second line", text.messages().get(1)); + ChatContent chat = assertInstanceOf(ChatContent.class, part.content()); + assertEquals(NoticeKey.CHAT, part.noticeKey()); + assertEquals("First line", chat.messages().get(0)); + assertEquals("Second line", chat.messages().get(1)); } static class ConfigSimpleTitle { @@ -107,9 +118,9 @@ void serializeSimpleTitleNoticeToOneLineEntry() { assertEquals(1, configSimpleTitle.notice.parts().size()); NoticePart part = configSimpleTitle.notice.parts().get(0); - Text title = assertInstanceOf(Text.class, part.content()); - assertEquals(NoticeType.TITLE, part.type()); - assertEquals("Hello world", title.messages().get(0)); + TitleContent title = assertInstanceOf(TitleContent.class, part.content()); + assertEquals(NoticeKey.TITLE, part.noticeKey()); + assertEquals("Hello world", title.content()); } static class ConfigFullTitle { @@ -129,18 +140,18 @@ void serializeTitleSubtitleWithDelayNoticeToOneLineEntry() { assertEquals(3, configFullTitle.notice.parts().size()); NoticePart titlePart = configFullTitle.notice.parts().get(0); - Text title = assertInstanceOf(Text.class, titlePart.content()); - assertEquals(NoticeType.TITLE, titlePart.type()); - assertEquals("Title", title.messages().get(0)); + TitleContent title = assertInstanceOf(TitleContent.class, titlePart.content()); + assertEquals(NoticeKey.TITLE, titlePart.noticeKey()); + assertEquals("Title", title.content()); NoticePart subtitlePart = configFullTitle.notice.parts().get(1); - Text subtitle = assertInstanceOf(Text.class, subtitlePart.content()); - assertEquals(NoticeType.SUBTITLE, subtitlePart.type()); - assertEquals("Subtitle", subtitle.messages().get(0)); + TitleContent subtitle = assertInstanceOf(TitleContent.class, subtitlePart.content()); + assertEquals(NoticeKey.SUBTITLE, subtitlePart.noticeKey()); + assertEquals("Subtitle", subtitle.content()); NoticePart timesPart = configFullTitle.notice.parts().get(2); - Times times = assertInstanceOf(Times.class, timesPart.content()); - assertEquals(NoticeType.TITLE_TIMES, timesPart.type()); + TitleTimes times = assertInstanceOf(TitleTimes.class, timesPart.content()); + assertEquals(NoticeKey.TITLE_TIMES, timesPart.noticeKey()); assertEquals(1, times.fadeIn().getSeconds()); assertEquals(2, times.stay().getSeconds()); assertEquals(1, times.fadeOut().getSeconds()); @@ -161,9 +172,9 @@ void serializeSimpleActionBarNoticeToOneLineEntry() { assertEquals(1, configSimpleActionBar.notice.parts().size()); NoticePart part = configSimpleActionBar.notice.parts().get(0); - Text text = assertInstanceOf(Text.class, part.content()); - assertEquals(NoticeType.ACTION_BAR, part.type()); - assertEquals("Hello world", text.messages().get(0)); + ActionbarContent actionbarContent = assertInstanceOf(ActionbarContent.class, part.content()); + assertEquals(NoticeKey.ACTION_BAR, part.noticeKey()); + assertEquals("Hello world", actionbarContent.content()); } static class ConfigHideTitle { @@ -175,18 +186,18 @@ void serializeHideTitleNoticeWithHideTitleProperty() { ConfigHideTitle configHideTitle = assertRender(new ConfigHideTitle(), """ notice: - titleHide: true + hideTitle: true """); assertEquals(1, configHideTitle.notice.parts().size()); NoticePart part = configHideTitle.notice.parts().get(0); - assertInstanceOf(None.class, part.content()); - assertEquals(NoticeType.TITLE_HIDE, part.type()); + assertInstanceOf(TitleHide.class, part.content()); + assertEquals(NoticeKey.TITLE_HIDE, part.noticeKey()); } static class ConfigSound { - Notice notice = Notice.sound(Sound.BLOCK_ANVIL_LAND, SoundCategory.MASTER, 1.0f, 1.0f); + Notice notice = BukkitNotice.sound(Sound.BLOCK_ANVIL_LAND, SoundCategory.MASTER, 1.0f, 1.0f); } @Test @DisplayName("Should serialize sound notice with sound property") @@ -200,8 +211,8 @@ void serializeSoundNoticeWithSoundProperty() { assertEquals(1, configSound.notice.parts().size()); NoticePart part = configSound.notice.parts().get(0); - Music sound = assertInstanceOf(Music.class, part.content()); - assertEquals(NoticeType.SOUND, part.type()); + SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); + assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); assertEquals(SoundCategory.MASTER, sound.category()); assertEquals(1.0f, sound.volume()); @@ -209,7 +220,7 @@ void serializeSoundNoticeWithSoundProperty() { } static class ConfigSoundWithoutCategory { - Notice notice = Notice.sound(Sound.BLOCK_ANVIL_LAND, 1.0f, 1.0f); + Notice notice = BukkitNotice.sound(Sound.BLOCK_ANVIL_LAND, 1.0f, 1.0f); } @Test @DisplayName("Should serialize sound notice without category property") @@ -223,14 +234,87 @@ void serializeSoundNoticeWithoutCategoryProperty() { assertEquals(1, configSoundWithoutCategory.notice.parts().size()); NoticePart part = configSoundWithoutCategory.notice.parts().get(0); - Music sound = assertInstanceOf(Music.class, part.content()); - assertEquals(NoticeType.SOUND, part.type()); + SoundBukkit sound = assertInstanceOf(SoundBukkit.class, part.content()); + assertEquals(BukkitNoticeKey.SOUND, part.noticeKey()); assertEquals(Sound.BLOCK_ANVIL_LAND, sound.sound()); assertNull(sound.category()); assertEquals(1.0f, sound.volume()); assertEquals(1.0f, sound.pitch()); } + + static class ConfigSoundShort { + Notice notice = BukkitNotice.sound(Sound.BLOCK_ANVIL_LAND); + } + @Test + @DisplayName("Should serialize sound notice without volume and pitch") + void serializeSoundNoticeWithoutVolumeAndPitch() { + ConfigSoundShort configSoundShort = assertRender(new ConfigSoundShort(), + """ + notice: + 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()); + 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 ConfigSoundAdventure { + Notice notice = Notice.sound(Key.key(Key.MINECRAFT_NAMESPACE, "entity.experience_orb.pickup"), net.kyori.adventure.sound.Sound.Source.MASTER, 1.0f, 1.0f); + } + @Test + @DisplayName("Should serialize adventure sound notice with sound property") + void serializeSoundNoticeWithSoundAdventureProperty() { + ConfigSoundAdventure configSound = assertRender(new ConfigSoundAdventure(), + """ + notice: + sound: "entity.experience_orb.pickup MASTER 1.0 1.0" + """); + + assertEquals(1, configSound.notice.parts().size()); + + NoticePart part = configSound.notice.parts().get(0); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("entity.experience_orb.pickup", sound.sound().value()); + assertEquals(net.kyori.adventure.sound.Sound.Source.MASTER, sound.category()); + assertEquals(1.0f, sound.volume()); + assertEquals(1.0f, sound.pitch()); + } + + static class ConfigSoundAdventureWithoutCategory { + Notice notice = Notice.sound(Key.key(Key.MINECRAFT_NAMESPACE, "entity.experience_orb.pickup"), 1.0f, 1.0f); + } + @Test + @DisplayName("Should serialize adventure sound notice without category property") + void serializeSoundNoticeWithoutCategoryAdventureProperty() { + ConfigSoundAdventureWithoutCategory configSound = assertRender(new ConfigSoundAdventureWithoutCategory(), + """ + notice: + sound: "entity.experience_orb.pickup 1.0 1.0" + """); + + assertEquals(1, configSound.notice.parts().size()); + + NoticePart part = configSound.notice.parts().get(0); + SoundAdventure sound = assertInstanceOf(SoundAdventure.class, part.content()); + assertEquals(NoticeKey.SOUND, part.noticeKey()); + assertEquals("entity.experience_orb.pickup", sound.sound().value()); + assertNull(sound.category()); + assertEquals(1.0f, sound.volume()); + assertEquals(1.0f, sound.pitch()); + } + @SuppressWarnings("unchecked") private T assertRender(T entity, String expected) { String actual = cdn.render(entity).orThrow(exception -> new RuntimeException(exception)); diff --git a/multification-core/build.gradle.kts b/multification-core/build.gradle.kts index 895dee3..3f1e70b 100644 --- a/multification-core/build.gradle.kts +++ b/multification-core/build.gradle.kts @@ -7,10 +7,8 @@ plugins { } dependencies { - api("net.kyori:adventure-api:4.17.0") + compileOnlyApi("net.kyori:adventure-api:${Versions.ADVENTURE_API}") api("org.jetbrains:annotations:24.1.0") - // TODO: Remove Spigot API, and use kyori in core. for Bukkit, create bukkit-platform. - compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") - testImplementation("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + testImplementation("net.kyori:adventure-api:${Versions.ADVENTURE_API}") } \ No newline at end of file diff --git a/multification-core/src/com/eternalcode/multification/Multification.java b/multification-core/src/com/eternalcode/multification/Multification.java index 72d7317..64211a0 100644 --- a/multification-core/src/com/eternalcode/multification/Multification.java +++ b/multification-core/src/com/eternalcode/multification/Multification.java @@ -1,29 +1,36 @@ package com.eternalcode.multification; +import com.eternalcode.multification.adventure.PlainComponentSerializer; import com.eternalcode.multification.locate.LocaleProvider; +import com.eternalcode.multification.notice.resolver.NoticeResolverDefaults; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; +import com.eternalcode.multification.platform.PlatformBroadcaster; import com.eternalcode.multification.translation.TranslationProvider; import com.eternalcode.multification.viewer.ViewerProvider; import com.eternalcode.multification.adventure.AudienceConverter; import com.eternalcode.multification.executor.AsyncExecutor; import com.eternalcode.multification.notice.NoticeBroadcast; import com.eternalcode.multification.notice.NoticeBroadcastImpl; -import com.eternalcode.multification.platform.PlatformBroadcaster; import com.eternalcode.multification.notice.provider.NoticeProvider; import com.eternalcode.multification.shared.Formatter; import com.eternalcode.multification.shared.Replacer; import java.util.Locale; import java.util.UUID; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.CheckReturnValue; import org.jetbrains.annotations.NotNull; -@SuppressWarnings("UnstableApiUsage") public abstract class Multification { - public static final PlatformBroadcaster DEFAULT_BROADCASTER = PlatformBroadcaster.withPlainSerializer(); + public static final ComponentSerializer DEFAULT_COMPONENT_SERIALIZER = new PlainComponentSerializer(); public static final AsyncExecutor DEFAULT_EXECUTOR = runnable -> runnable.run(); public static final Replacer DEFAULT_REPLACER = (v, text) -> text; public static final LocaleProvider LOCALE_PROVIDER = v -> Locale.ROOT; + protected final NoticeResolverRegistry noticeRegistry = NoticeResolverDefaults.createRegistry(); + @CheckReturnValue public NoticeBroadcast create() { return new NoticeBroadcastImpl<>( @@ -33,7 +40,8 @@ public abstract class Multification { this.platformBroadcaster(), this.localeProvider(), this.audienceConverter(), - this.globalReplacer() + this.globalReplacer(), + noticeRegistry ); } @@ -46,9 +54,18 @@ public abstract class Multification { @NotNull protected abstract AudienceConverter audienceConverter(); - @NotNull + /** + * @deprecated Use {@link #serializer()} instead to configure serializer or use {@link #getNoticeRegistry()} to configure notice resolvers. + */ + @ApiStatus.Internal + @ApiStatus.NonExtendable protected PlatformBroadcaster platformBroadcaster() { - return DEFAULT_BROADCASTER; + return PlatformBroadcaster.create(serializer(), noticeRegistry); + } + + @NotNull + protected ComponentSerializer serializer() { + return DEFAULT_COMPONENT_SERIALIZER; } @SuppressWarnings("unchecked") @@ -109,4 +126,9 @@ public void all(NoticeProvider extractor, Formatter... formatters) .send(); } + @ApiStatus.Internal + public NoticeResolverRegistry getNoticeRegistry() { + return noticeRegistry; + } + } diff --git a/multification-core/src/com/eternalcode/multification/platform/PlainComponentSerializer.java b/multification-core/src/com/eternalcode/multification/adventure/PlainComponentSerializer.java similarity index 85% rename from multification-core/src/com/eternalcode/multification/platform/PlainComponentSerializer.java rename to multification-core/src/com/eternalcode/multification/adventure/PlainComponentSerializer.java index 0243fa1..4f2b93f 100644 --- a/multification-core/src/com/eternalcode/multification/platform/PlainComponentSerializer.java +++ b/multification-core/src/com/eternalcode/multification/adventure/PlainComponentSerializer.java @@ -1,11 +1,11 @@ -package com.eternalcode.multification.platform; +package com.eternalcode.multification.adventure; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.serializer.ComponentSerializer; import org.jetbrains.annotations.NotNull; -class PlainComponentSerializer implements ComponentSerializer { +public class PlainComponentSerializer implements ComponentSerializer { @Override public @NotNull Component deserialize(@NotNull String input) { return Component.text(input); diff --git a/multification-core/src/com/eternalcode/multification/notice/Notice.java b/multification-core/src/com/eternalcode/multification/notice/Notice.java index f75be7a..eafdbf8 100644 --- a/multification-core/src/com/eternalcode/multification/notice/Notice.java +++ b/multification-core/src/com/eternalcode/multification/notice/Notice.java @@ -1,5 +1,10 @@ package com.eternalcode.multification.notice; +import com.eternalcode.multification.notice.resolver.NoticeContent; +import com.eternalcode.multification.notice.resolver.actionbar.ActionbarContent; +import com.eternalcode.multification.notice.resolver.chat.ChatContent; +import com.eternalcode.multification.notice.resolver.title.TitleContent; +import com.eternalcode.multification.notice.resolver.title.TitleHide; import java.time.Duration; import java.util.ArrayList; import java.util.Collection; @@ -8,21 +13,16 @@ import java.util.List; import java.util.Map; -import static com.eternalcode.multification.notice.NoticeContent.Music; -import static com.eternalcode.multification.notice.NoticeContent.None; -import static com.eternalcode.multification.notice.NoticeContent.Text; -import static com.eternalcode.multification.notice.NoticeContent.Times; +import com.eternalcode.multification.notice.resolver.sound.SoundAdventure; +import com.eternalcode.multification.notice.resolver.title.TitleTimes; import net.kyori.adventure.key.Key; -import net.kyori.adventure.key.KeyPattern; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; -import org.bukkit.block.data.type.Bed.Part; +import net.kyori.adventure.sound.Sound; public class Notice { - private final Map> parts = new LinkedHashMap<>(); + private final Map, NoticePart> parts = new LinkedHashMap<>(); - private Notice(Map> parts) { + protected Notice(Map, NoticePart> parts) { this.parts.putAll(parts); } @@ -31,9 +31,9 @@ public List> parts() { .toList(); } - public static Notice of(NoticeType type, T content) { + public static Notice of(NoticeKey key, T content) { return Notice.builder() - .withPart(new NoticePart<>(type, content)) + .withPart(key, content) .build(); } @@ -88,13 +88,13 @@ public static Notice hideTitle() { .build(); } - public static Notice sound(Sound sound, SoundCategory category, float volume, float pitch) { + 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(Sound sound, float volume, float pitch) { + public static Notice sound(Key sound, float volume, float pitch) { return Notice.builder() .sound(sound, pitch, volume) .build(); @@ -108,66 +108,83 @@ public static Builder builder() { return new Builder(); } - public static class Builder { - private final Map> parts = new LinkedHashMap<>(); - - public Builder withPart(NoticePart part) { - this.parts.put(part.type(), part); + public static class Builder extends BaseBuilder { + @Override + protected Builder getThis() { return this; } + } + + public abstract static class BaseBuilder> { + protected final Map, NoticePart> parts = new LinkedHashMap<>(); + + public B withPart(NoticePart part) { + this.parts.put(part.noticeKey(), part); + return this.getThis(); + } + + public B withPart(NoticeKey key, T content) { + return this.withPart(new NoticePart<>(key, content)); + } + + abstract protected B getThis(); public Notice build() { return new Notice(this.parts); } - public Builder chat(String... messages) { + public B chat(String... messages) { return this.chat(List.of(messages)); } - public Builder chat(Collection messages) { - NoticePart removed = this.parts.remove(NoticeType.CHAT); + public B chat(Collection messages) { + NoticePart removed = this.parts.remove(NoticeKey.CHAT); List newMessages = new ArrayList<>(); - if (removed != null && removed.content() instanceof Text text) { - newMessages.addAll(text.messages()); + if (removed != null && removed.content() instanceof ChatContent chat) { + newMessages.addAll(chat.messages()); } newMessages.addAll(messages); - return this.withPart(new NoticePart<>(NoticeType.CHAT, new Text(Collections.unmodifiableList(newMessages)))); + return this.withPart(NoticeKey.CHAT, new ChatContent(Collections.unmodifiableList(newMessages))); + } + + public B actionBar(String message) { + return this.withPart(NoticeKey.ACTION_BAR, new ActionbarContent(message)); } - public Builder actionBar(String message) { - return this.withPart(new NoticePart<>(NoticeType.ACTION_BAR, new Text(List.of(message)))); + public B title(String title) { + return this.withPart(NoticeKey.TITLE, new TitleContent(title)); } - public Builder title(String title) { - return this.withPart(new NoticePart<>(NoticeType.TITLE, new Text(List.of(title)))); + public B title(String title, String subtitle) { + return this.withPart(NoticeKey.TITLE, new TitleContent(title)) + .withPart(NoticeKey.SUBTITLE, new TitleContent(subtitle)); } - public Builder title(String title, String subtitle) { - return this.withPart(new NoticePart<>(NoticeType.TITLE, new Text(List.of(title)))) - .withPart(new NoticePart<>(NoticeType.SUBTITLE, new Text(List.of(subtitle)))); + public B subtitle(String subtitle) { + return this.withPart(NoticeKey.SUBTITLE, new TitleContent(subtitle)); } - public Builder subtitle(String subtitle) { - return this.withPart(new NoticePart<>(NoticeType.SUBTITLE, new Text(List.of(subtitle)))); + public B hideTitle() { + return this.withPart(NoticeKey.TITLE_HIDE, new TitleHide(true)); } - public Builder hideTitle() { - return this.withPart(new NoticePart<>(NoticeType.TITLE_HIDE, None.INSTANCE)); + public B hideTitle(boolean hide) { + return this.withPart(NoticeKey.TITLE_HIDE, new TitleHide(hide)); } - public Builder times(Duration in, Duration stay, Duration out) { - return this.withPart(new NoticePart<>(NoticeType.TITLE_TIMES, new Times(in, stay, out))); + public B times(Duration in, Duration stay, Duration out) { + return this.withPart(NoticeKey.TITLE_TIMES, new TitleTimes(in, stay, out)); } - public Builder sound(Sound sound, float pitch, float volume) { - return this.withPart(new NoticePart<>(NoticeType.SOUND, new Music(sound, null, pitch, volume))); + public B sound(Key sound, float pitch, float volume) { + return this.withPart(NoticeKey.SOUND, new SoundAdventure(sound, null, pitch, volume)); } - public Builder sound(Sound sound, SoundCategory category, float pitch, float volume) { - return this.withPart(new NoticePart<>(NoticeType.SOUND, new Music(sound, category, pitch, volume))); + public B sound(Key sound, Sound.Source category, float pitch, float volume) { + return this.withPart(NoticeKey.SOUND, new SoundAdventure(sound, category, pitch, volume)); } } diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcast.java b/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcast.java index 7ab3ed9..9cc67f6 100644 --- a/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcast.java +++ b/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcast.java @@ -1,6 +1,7 @@ package com.eternalcode.multification.notice; import com.eternalcode.multification.notice.provider.TextMessageProvider; +import com.eternalcode.multification.notice.resolver.text.TextContent; import com.eternalcode.multification.shared.Formatter; import com.eternalcode.multification.notice.provider.NoticeProvider; import com.eternalcode.multification.notice.provider.OptionalNoticeProvider; @@ -12,7 +13,6 @@ import java.util.function.Supplier; import org.jetbrains.annotations.CheckReturnValue; -@SuppressWarnings("UnstableApiUsage") public interface NoticeBroadcast> { @CheckReturnValue @@ -40,13 +40,13 @@ public interface NoticeBroadcast extractor); @CheckReturnValue - B notice(NoticeType type, String... text); + B notice(NoticeKey type, String... text); @CheckReturnValue - B notice(NoticeType type, Collection text); + B notice(NoticeKey type, Collection text); @CheckReturnValue - B notice(NoticeType type, TextMessageProvider extractor); + B notice(NoticeKey type, TextMessageProvider extractor); @CheckReturnValue B noticeChat(TextMessageProvider extractor); diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcastImpl.java b/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcastImpl.java index 9c61c17..f08d3cf 100644 --- a/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcastImpl.java +++ b/multification-core/src/com/eternalcode/multification/notice/NoticeBroadcastImpl.java @@ -1,7 +1,10 @@ package com.eternalcode.multification.notice; import com.eternalcode.multification.locate.LocaleProvider; +import com.eternalcode.multification.notice.resolver.NoticeContent; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; import com.eternalcode.multification.notice.provider.TextMessageProvider; +import com.eternalcode.multification.notice.resolver.text.TextContent; import com.eternalcode.multification.shared.Formatter; import com.eternalcode.multification.translation.TranslationProvider; import com.eternalcode.multification.viewer.ViewerProvider; @@ -29,7 +32,6 @@ import net.kyori.adventure.audience.Audience; import org.jetbrains.annotations.CheckReturnValue; -@SuppressWarnings("UnstableApiUsage") public class NoticeBroadcastImpl> implements NoticeBroadcast { protected final AsyncExecutor asyncExecutor; @@ -39,6 +41,7 @@ public class NoticeBroadcastImpl localeProvider; protected final AudienceConverter audienceConverter; protected final Replacer globalReplacer; + protected final NoticeResolverRegistry noticeRegistry; protected final List viewers = new ArrayList<>(); protected final List> notifications = new ArrayList<>(); @@ -47,12 +50,13 @@ public class NoticeBroadcastImpl formatters = new ArrayList<>(); public NoticeBroadcastImpl( - AsyncExecutor asyncExecutor, - TranslationProvider translationProvider, - ViewerProvider viewerProvider, - PlatformBroadcaster platformBroadcaster, - LocaleProvider localeProvider, - AudienceConverter audienceConverter, Replacer replacer + AsyncExecutor asyncExecutor, + TranslationProvider translationProvider, + ViewerProvider viewerProvider, + PlatformBroadcaster platformBroadcaster, + LocaleProvider localeProvider, + AudienceConverter audienceConverter, Replacer replacer, + NoticeResolverRegistry noticeRegistry ) { this.asyncExecutor = asyncExecutor; this.translationProvider = translationProvider; @@ -61,6 +65,7 @@ public NoticeBroadcastImpl( this.localeProvider = localeProvider; this.audienceConverter = audienceConverter; this.globalReplacer = replacer; + this.noticeRegistry = noticeRegistry; } @Override @@ -161,25 +166,25 @@ public B notice(NoticeProvider extractor) { } @Override - public B notice(NoticeType type, String... text) { - NoticeContent.Text content = new NoticeContent.Text(List.of(text)); + public B notice(NoticeKey type, String... text) { + TextContent content = noticeRegistry.createTextNotice(type, List.of(text)); this.notifications.add(translation -> Notice.of(type, content)); return this.getThis(); } @Override - public B notice(NoticeType type, Collection text) { - NoticeContent.Text content = new NoticeContent.Text(new ArrayList<>(text)); + public B notice(NoticeKey type, Collection text) { + TextContent content = noticeRegistry.createTextNotice(type, new ArrayList<>(text)); this.notifications.add(translation -> Notice.of(type, content)); return this.getThis(); } @Override - public B notice(NoticeType type, TextMessageProvider extractor) { + public B notice(NoticeKey type, TextMessageProvider extractor) { this.notifications.add(translation -> { List list = Collections.singletonList(extractor.extract(translation)); - NoticeContent.Text content = new NoticeContent.Text(list); + TextContent content = noticeRegistry.createTextNotice(type, list); return Notice.of(type, content); }); @@ -255,7 +260,7 @@ private void sendTranslatedMessages(LanguageViewersIndex viewersIndex, T Audience audience = audienceConverter.convert(viewer); for (NoticePart part : notice.parts()) { - part = this.formatter(part, message -> translatedFormatter.format(message, viewer)); + part = this.applyText(part, message -> translatedFormatter.format(message, viewer)); this.platformBroadcaster.announce(audience, part); } @@ -265,15 +270,9 @@ private void sendTranslatedMessages(LanguageViewersIndex viewersIndex, T } @SuppressWarnings("unchecked") - protected NoticePart formatter(NoticePart part, UnaryOperator function) { - if (part.content() instanceof NoticeContent.Text text) { - List messages = text.messages().stream() - .map(function) - .toList(); - - NoticeContent.Text context = new NoticeContent.Text(messages); - - part = new NoticePart<>(part.type(), (T) context); + protected NoticePart applyText(NoticePart part, UnaryOperator function) { + if (part.content() instanceof TextContent) { + return (NoticePart) noticeRegistry.applyText((NoticePart) part, function); } return part; diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticeContent.java b/multification-core/src/com/eternalcode/multification/notice/NoticeContent.java deleted file mode 100644 index ef0f8ca..0000000 --- a/multification-core/src/com/eternalcode/multification/notice/NoticeContent.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.eternalcode.multification.notice; - -import java.time.Duration; -import java.util.List; -import org.bukkit.Sound; -import org.bukkit.SoundCategory; -import org.jetbrains.annotations.Nullable; - -public sealed interface NoticeContent { - - record Text(List messages) implements NoticeContent { - } - - record Times(Duration fadeIn, Duration stay, Duration fadeOut) implements NoticeContent { - } - - record Music(Sound sound, @Nullable SoundCategory category, float pitch, float volume) implements NoticeContent { - } - - record None() implements NoticeContent { - - public static final None INSTANCE = new None(); - } -} diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticeKey.java b/multification-core/src/com/eternalcode/multification/notice/NoticeKey.java new file mode 100644 index 0000000..08395c8 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/NoticeKey.java @@ -0,0 +1,29 @@ +package com.eternalcode.multification.notice; + +import com.eternalcode.multification.notice.resolver.NoticeContent; +import com.eternalcode.multification.notice.resolver.actionbar.ActionbarContent; +import com.eternalcode.multification.notice.resolver.chat.ChatContent; +import com.eternalcode.multification.notice.resolver.sound.SoundAdventure; +import com.eternalcode.multification.notice.resolver.title.TitleContent; +import com.eternalcode.multification.notice.resolver.title.TitleHide; +import com.eternalcode.multification.notice.resolver.title.TitleTimes; + +public interface NoticeKey { + + NoticeKey CHAT = NoticeKey.of("chat", ChatContent.class); + NoticeKey ACTION_BAR = NoticeKey.of("actionbar", ActionbarContent.class); + NoticeKey TITLE = NoticeKey.of("title", TitleContent.class); + NoticeKey SUBTITLE = NoticeKey.of("subtitle", TitleContent.class); + NoticeKey TITLE_TIMES = NoticeKey.of("times", TitleTimes.class); + NoticeKey TITLE_HIDE = NoticeKey.of("hideTitle", TitleHide.class); + NoticeKey SOUND = NoticeKey.of("sound", SoundAdventure.class); + + String key(); + + Class type(); + + static NoticeKey of(String text, Class type) { + return new NoticeKeyImpl<>(text, type); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticeKeyImpl.java b/multification-core/src/com/eternalcode/multification/notice/NoticeKeyImpl.java new file mode 100644 index 0000000..5e7a14c --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/NoticeKeyImpl.java @@ -0,0 +1,6 @@ +package com.eternalcode.multification.notice; + +import com.eternalcode.multification.notice.resolver.NoticeContent; + +record NoticeKeyImpl(String key, Class type) implements NoticeKey { +} diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticePart.java b/multification-core/src/com/eternalcode/multification/notice/NoticePart.java index f98cb1d..6610d70 100644 --- a/multification-core/src/com/eternalcode/multification/notice/NoticePart.java +++ b/multification-core/src/com/eternalcode/multification/notice/NoticePart.java @@ -1,4 +1,14 @@ package com.eternalcode.multification.notice; -public record NoticePart(NoticeType type, T content) { +import com.eternalcode.multification.notice.resolver.NoticeContent; +import org.jetbrains.annotations.ApiStatus; + +public record NoticePart(NoticeKey noticeKey, T content) { + + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") + public NoticeType type() { + return NoticeType.fromKey(noticeKey.key()); + } + } diff --git a/multification-core/src/com/eternalcode/multification/notice/NoticeType.java b/multification-core/src/com/eternalcode/multification/notice/NoticeType.java index 118064e..af601d0 100644 --- a/multification-core/src/com/eternalcode/multification/notice/NoticeType.java +++ b/multification-core/src/com/eternalcode/multification/notice/NoticeType.java @@ -1,22 +1,29 @@ package com.eternalcode.multification.notice; -import static com.eternalcode.multification.notice.NoticeContent.Music; -import static com.eternalcode.multification.notice.NoticeContent.None; -import static com.eternalcode.multification.notice.NoticeContent.Text; -import static com.eternalcode.multification.notice.NoticeContent.Times; +import com.eternalcode.multification.notice.resolver.NoticeContent; +import com.eternalcode.multification.notice.resolver.actionbar.ActionbarContent; +import com.eternalcode.multification.notice.resolver.sound.SoundAdventure; +import com.eternalcode.multification.notice.resolver.chat.ChatContent; +import com.eternalcode.multification.notice.resolver.title.TitleContent; +import com.eternalcode.multification.notice.resolver.title.TitleHide; +import com.eternalcode.multification.notice.resolver.title.TitleTimes; +import org.jetbrains.annotations.ApiStatus; -public enum NoticeType { - CHAT(Text.class, "chat"), - ACTION_BAR(Text.class, "actionbar"), - TITLE(Text.class, "title"), - SUBTITLE(Text.class, "subtitle"), +@Deprecated +@ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") +public enum NoticeType implements NoticeKey { + CHAT(ChatContent.class, NoticeKey.CHAT.key()), + ACTION_BAR(ActionbarContent.class, NoticeKey.ACTION_BAR.key()), + TITLE(TitleContent.class, NoticeKey.TITLE.key()), + SUBTITLE(TitleContent.class, NoticeKey.SUBTITLE.key()), // TODO: Find a better sotultion, minecraft sends subtitle/title only when the second part also contains some part - TITLE_WITH_EMPTY_SUBTITLE(Text.class, "titleWithEmptySubtitle"), - SUBTITLE_WITH_EMPTY_TITLE(Text.class, "subtitleWithEmptyTitle"), - TITLE_TIMES(Times.class, "times"), - TITLE_HIDE(None.class, "titleHide"), - SOUND(Music.class, "sound"); + TITLE_WITH_EMPTY_SUBTITLE(TitleContent.class, "titleWithEmptySubtitle"), + SUBTITLE_WITH_EMPTY_TITLE(TitleContent.class, "subtitleWithEmptyTitle"), + + TITLE_TIMES(TitleTimes.class, NoticeKey.TITLE_TIMES.key()), + TITLE_HIDE(TitleHide.class, NoticeKey.TITLE_HIDE.key()), + SOUND(SoundAdventure.class, NoticeKey.SOUND.key()); private final Class inputType; private final String name; @@ -26,14 +33,26 @@ public enum NoticeType { this.name = name; } + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") public String getKey() { return this.name; } + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") public Class contentType() { return this.inputType; } + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") + public NoticeKey toNoticeKey() { + return NoticeKey.of(this.getKey(), (Class) contentType()); + } + + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") public static NoticeType fromKey(String key) { for (NoticeType type : values()) { if (type.getKey().equals(key)) { @@ -44,4 +63,14 @@ public static NoticeType fromKey(String key) { throw new IllegalArgumentException("Unknown notice type: " + key); } + @Override + public String key() { + return this.getKey(); + } + + @Override + public Class type() { + return this.contentType(); + } + } diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeContent.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeContent.java new file mode 100644 index 0000000..ea8fcdc --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeContent.java @@ -0,0 +1,5 @@ +package com.eternalcode.multification.notice.resolver; + +public interface NoticeContent { + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeDeserializeResult.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeDeserializeResult.java new file mode 100644 index 0000000..9b46295 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeDeserializeResult.java @@ -0,0 +1,6 @@ +package com.eternalcode.multification.notice.resolver; + +import com.eternalcode.multification.notice.NoticeKey; + +public record NoticeDeserializeResult(NoticeKey noticeKey, T content) { +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolver.java new file mode 100644 index 0000000..1e9c820 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolver.java @@ -0,0 +1,19 @@ +package com.eternalcode.multification.notice.resolver; + +import com.eternalcode.multification.notice.NoticeKey; +import java.util.Optional; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; + +public interface NoticeResolver { + + NoticeKey noticeKey(); + + void send(Audience audience, ComponentSerializer componentSerializer, T content); + + NoticeSerdesResult serialize(T content); + + Optional deserialize(NoticeSerdesResult result); + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverDefaults.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverDefaults.java new file mode 100644 index 0000000..c2933fc --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverDefaults.java @@ -0,0 +1,32 @@ +package com.eternalcode.multification.notice.resolver; + +import com.eternalcode.multification.notice.resolver.actionbar.ActionbarResolver; +import com.eternalcode.multification.notice.resolver.chat.ChatResolver; +import com.eternalcode.multification.notice.resolver.sound.SoundAdventureResolver; +import com.eternalcode.multification.notice.resolver.title.SubtitleResolver; +import com.eternalcode.multification.notice.resolver.title.SubtitleWithEmptyTitleResolver; +import com.eternalcode.multification.notice.resolver.title.TimesResolver; +import com.eternalcode.multification.notice.resolver.title.TitleHideResolver; +import com.eternalcode.multification.notice.resolver.title.TitleResolver; +import com.eternalcode.multification.notice.resolver.title.TitleWithEmptySubtitleResolver; + +public final class NoticeResolverDefaults { + + private NoticeResolverDefaults() { + } + + public static NoticeResolverRegistry createRegistry() { + return new NoticeResolverRegistry() + .registerResolver(new ChatResolver()) + .registerResolver(new TitleResolver()) + .registerResolver(new SubtitleResolver()) + .registerResolver(new TitleWithEmptySubtitleResolver()) + .registerResolver(new SubtitleWithEmptyTitleResolver()) + .registerResolver(new TimesResolver()) + .registerResolver(new TitleHideResolver()) + .registerResolver(new SoundAdventureResolver()) + .registerResolver(new ActionbarResolver()) + ; + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java new file mode 100644 index 0000000..88ce7c7 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeResolverRegistry.java @@ -0,0 +1,107 @@ +package com.eternalcode.multification.notice.resolver; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.NoticePart; +import com.eternalcode.multification.notice.resolver.text.TextContent; +import com.eternalcode.multification.notice.resolver.text.TextContentResolver; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.UnaryOperator; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.jetbrains.annotations.Nullable; + +public class NoticeResolverRegistry { + + private final Map, NoticeResolver> resolvers = new HashMap<>(); + private final Map>> resolversByKey = new HashMap<>(); + + public NoticeResolverRegistry registerResolver(NoticeResolver resolver) { + resolvers.put(resolver.noticeKey(), resolver); + resolversByKey.computeIfAbsent(resolver.noticeKey().key(), key -> new HashSet<>()).add(resolver); + return this; + } + + public NoticeSerdesResult serialize(NoticePart noticePart) { + NoticeResolver resolver = this.getNoticeResolverOrThrow(noticePart.noticeKey()); + + return resolver.serialize(noticePart.content()); + } + + public Optional> deserialize(String key, NoticeSerdesResult result) { + Set> resolvers = this.resolversByKey.get(key); + + IllegalArgumentException illegalArgumentException = new IllegalArgumentException("Could not deserialize " + result); + + for (NoticeResolver resolver : resolvers) { + try { + NoticeDeserializeResult deserializeResult = this.deserialize(resolver, result); + + if (deserializeResult != null) { + return Optional.of(deserializeResult); + } + } + catch (Exception exception) { + illegalArgumentException.addSuppressed(exception); + } + } + + if (illegalArgumentException.getSuppressed().length > 0) { + throw illegalArgumentException; + } + + return Optional.empty(); + } + + @Nullable + private NoticeDeserializeResult deserialize(NoticeResolver resolver, NoticeSerdesResult result) { + return resolver.deserialize(result) + .map(content -> new NoticeDeserializeResult<>(resolver.noticeKey(), content)) + .orElse(null); + } + + public void send(Audience audience, ComponentSerializer serializer, NoticePart noticePart) { + NoticeResolver resolver = this.getNoticeResolverOrThrow(noticePart.noticeKey()); + + resolver.send(audience, serializer, noticePart.content()); + } + + @SuppressWarnings("unchecked") + private NoticeResolver getNoticeResolverOrThrow(NoticeKey key) { + NoticeResolver resolver = (NoticeResolver) resolvers.get(key); + + if (resolver == null) { + throw new IllegalArgumentException("No resolver found for " + key + " - " + key); + } + + return resolver; + } + + public T createTextNotice(NoticeKey noticeKey, List contents) { + NoticeResolver resolver = this.getNoticeResolverOrThrow(noticeKey); + + if (!(resolver instanceof TextContentResolver textContentResolver)) { + throw new IllegalArgumentException("Resolver for " + noticeKey + " is not a text resolver"); + } + + return textContentResolver.createFromText(contents); + } + + public NoticePart applyText(NoticePart part, UnaryOperator function) { + NoticeResolver resolver = this.getNoticeResolverOrThrow(part.noticeKey()); + + if (resolver instanceof TextContentResolver textContentResolver) { + T t = textContentResolver.applyText(part.content(), function); + + return new NoticePart<>(part.noticeKey(), t); + } + + return part; + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeSerdesResult.java b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeSerdesResult.java new file mode 100644 index 0000000..9115dd6 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/NoticeSerdesResult.java @@ -0,0 +1,35 @@ +package com.eternalcode.multification.notice.resolver; + +import java.util.List; +import java.util.Optional; + +public sealed interface NoticeSerdesResult { + + List anyElements(); + + default Optional firstElement() { + return anyElements().stream().findFirst(); + } + + record Empty() implements NoticeSerdesResult { + @Override + public List anyElements() { + return List.of(); + } + } + + record Single(String element) implements NoticeSerdesResult { + @Override + public List anyElements() { + return List.of(element); + } + } + + record Multi(List elements) implements NoticeSerdesResult { + @Override + public List anyElements() { + return elements; + } + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarContent.java b/multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarContent.java new file mode 100644 index 0000000..83ac914 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarContent.java @@ -0,0 +1,11 @@ +package com.eternalcode.multification.notice.resolver.actionbar; + +import com.eternalcode.multification.notice.resolver.text.TextContent; +import java.util.List; + +public record ActionbarContent(String content) implements TextContent { + @Override + public List contents() { + return List.of(content); + } +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarResolver.java new file mode 100644 index 0000000..92db627 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/actionbar/ActionbarResolver.java @@ -0,0 +1,52 @@ +package com.eternalcode.multification.notice.resolver.actionbar; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import com.eternalcode.multification.notice.resolver.text.TextContentResolver; +import java.util.List; +import java.util.Optional; +import java.util.function.UnaryOperator; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; + +public class ActionbarResolver implements TextContentResolver { + + @Override + public NoticeKey noticeKey() { + return NoticeKey.ACTION_BAR; + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, ActionbarContent content) { + audience.sendActionBar(componentSerializer.deserialize(content.content())); + } + + @Override + public ActionbarContent applyText(ActionbarContent content, UnaryOperator function) { + return new ActionbarContent(function.apply(content.content())); + } + + @Override + public NoticeSerdesResult serialize(ActionbarContent content) { + return new NoticeSerdesResult.Single(content.content()); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + Optional firstElement = result.firstElement(); + + return firstElement + .map(ActionbarContent::new); + } + + @Override + public ActionbarContent createFromText(List contents) { + if (contents.isEmpty()) { + return new ActionbarContent(""); + } + + return new ActionbarContent(String.join(" ", contents)); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatContent.java b/multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatContent.java new file mode 100644 index 0000000..73d4657 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatContent.java @@ -0,0 +1,11 @@ +package com.eternalcode.multification.notice.resolver.chat; + +import com.eternalcode.multification.notice.resolver.text.TextContent; +import java.util.List; + +public record ChatContent(List messages) implements TextContent { + @Override + public List contents() { + return messages; + } +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatResolver.java new file mode 100644 index 0000000..e3eae3c --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/chat/ChatResolver.java @@ -0,0 +1,71 @@ +package com.eternalcode.multification.notice.resolver.chat; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import com.eternalcode.multification.notice.resolver.text.TextContentResolver; +import java.util.List; +import java.util.Optional; +import java.util.function.UnaryOperator; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; + +public class ChatResolver implements TextContentResolver { + + private final NoticeKey key; + + public ChatResolver() { + this.key = NoticeKey.CHAT; + } + + @Override + public NoticeKey noticeKey() { + return key; + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, ChatContent content) { + for (String message : content.messages()) { + audience.sendMessage(componentSerializer.deserialize(message)); + } + } + + @Override + public ChatContent applyText(ChatContent content, UnaryOperator function) { + return new ChatContent(content.messages().stream() + .map(function) + .toList() + ); + } + + @Override + public NoticeSerdesResult serialize(ChatContent content) { + List messages = content.messages(); + + if (messages.isEmpty()) { + return new NoticeSerdesResult.Empty(); + } + + if (messages.size() == 1) { + return new NoticeSerdesResult.Single(messages.get(0)); + } + + return new NoticeSerdesResult.Multi(messages); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + List messages = result.anyElements(); + + if (messages.isEmpty()) { + return Optional.empty(); + } + + return Optional.of(new ChatContent(messages)); + } + + @Override + public ChatContent createFromText(List contents) { + return new ChatContent(contents); + } +} 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 new file mode 100644 index 0000000..cb7e2bd --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventure.java @@ -0,0 +1,9 @@ +package com.eternalcode.multification.notice.resolver.sound; + +import com.eternalcode.multification.notice.resolver.NoticeContent; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import org.jetbrains.annotations.Nullable; + +public record SoundAdventure(Key sound, @Nullable Sound.Source category, float pitch, float volume) implements NoticeContent { +} 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 new file mode 100644 index 0000000..274a217 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/sound/SoundAdventureResolver.java @@ -0,0 +1,79 @@ +package com.eternalcode.multification.notice.resolver.sound; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeResolver; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import java.util.Optional; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; + +public class SoundAdventureResolver implements NoticeResolver { + + private static final String MUSIC_WITH_CATEGORY = "%s %s %s %s"; + private static final String MUSIC_WITHOUT_CATEGORY = "%s %s %s"; + + private final NoticeKey key; + + public SoundAdventureResolver() { + this.key = NoticeKey.SOUND; + } + + @Override + public NoticeKey noticeKey() { + return key; + } + + @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())); + } + + @Override + public NoticeSerdesResult serialize(SoundAdventure content) { + if (content.category() == null) { + return new NoticeSerdesResult.Single(String.format(MUSIC_WITHOUT_CATEGORY, + content.sound().value(), + content.pitch(), + content.volume() + )); + } + + return new NoticeSerdesResult.Single(String.format(MUSIC_WITH_CATEGORY, + content.sound().value(), + content.category().name(), + content.pitch(), + content.volume() + )); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + Optional firstElement = result.firstElement(); + + if (firstElement.isEmpty()) { + return Optional.empty(); + } + + String[] music = firstElement.get().split(" "); + + if (music.length < 3 || music.length > 4) { + throw new IllegalStateException("Invalid music format: " + firstElement.get()); + } + + Key sound = Key.key(Key.MINECRAFT_NAMESPACE, music[0]); + Sound.Source category = music.length == 3 ? null : Sound.Source.valueOf(music[1]); + float pitch = Float.parseFloat(music[music.length - 2]); + float volume = Float.parseFloat(music[music.length - 1]); + + return Optional.of(new SoundAdventure(sound, category, pitch, volume)); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContent.java b/multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContent.java new file mode 100644 index 0000000..714e336 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContent.java @@ -0,0 +1,10 @@ +package com.eternalcode.multification.notice.resolver.text; + +import com.eternalcode.multification.notice.resolver.NoticeContent; +import java.util.List; + +public interface TextContent extends NoticeContent { + + List contents(); + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContentResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContentResolver.java new file mode 100644 index 0000000..4e5f107 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/text/TextContentResolver.java @@ -0,0 +1,15 @@ +package com.eternalcode.multification.notice.resolver.text; + +import com.eternalcode.multification.notice.resolver.NoticeResolver; +import java.util.List; +import java.util.function.UnaryOperator; + +public interface TextContentResolver extends NoticeResolver { + + T createFromText(List contents); + + default T applyText(T content, UnaryOperator function) { + return content; + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/AbstractTitleContentResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/AbstractTitleContentResolver.java new file mode 100644 index 0000000..6f43634 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/AbstractTitleContentResolver.java @@ -0,0 +1,48 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import com.eternalcode.multification.notice.resolver.text.TextContentResolver; +import java.util.List; +import java.util.Optional; +import java.util.function.UnaryOperator; + +public abstract class AbstractTitleContentResolver implements TextContentResolver { + + private final NoticeKey key; + + protected AbstractTitleContentResolver(NoticeKey key) { + this.key = key; + } + + @Override + public NoticeKey noticeKey() { + return key; + } + + @Override + public TitleContent applyText(TitleContent content, UnaryOperator function) { + return new TitleContent(function.apply(content.content())); + } + + @Override + public NoticeSerdesResult serialize(TitleContent content) { + return new NoticeSerdesResult.Single(content.content()); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + return result.firstElement() + .map(content -> new TitleContent(content)); + } + + @Override + public TitleContent createFromText(List contents) { + if (contents.isEmpty()) { + return new TitleContent(""); + } + + return new TitleContent(String.join(" ", contents)); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleResolver.java new file mode 100644 index 0000000..d52dd4b --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleResolver.java @@ -0,0 +1,20 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeKey; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.title.TitlePart; + +public class SubtitleResolver extends AbstractTitleContentResolver { + + public SubtitleResolver() { + super(NoticeKey.SUBTITLE); + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, TitleContent content) { + audience.sendTitlePart(TitlePart.SUBTITLE, componentSerializer.deserialize(content.content())); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleWithEmptyTitleResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleWithEmptyTitleResolver.java new file mode 100644 index 0000000..9e247e8 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/SubtitleWithEmptyTitleResolver.java @@ -0,0 +1,21 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeType; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.title.TitlePart; + +public class SubtitleWithEmptyTitleResolver extends AbstractTitleContentResolver { + + public SubtitleWithEmptyTitleResolver() { + super(NoticeType.SUBTITLE_WITH_EMPTY_TITLE.toNoticeKey()); + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, TitleContent content) { + audience.sendTitlePart(TitlePart.SUBTITLE, componentSerializer.deserialize(content.content())); + audience.sendTitlePart(TitlePart.TITLE, Component.empty()); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TimesResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TimesResolver.java new file mode 100644 index 0000000..3da1091 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TimesResolver.java @@ -0,0 +1,65 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeResolver; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import com.eternalcode.multification.time.DurationParser; +import com.eternalcode.multification.time.TemporalAmountParser; +import java.time.Duration; +import java.util.Optional; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.title.Title; +import net.kyori.adventure.title.TitlePart; + +public class TimesResolver implements NoticeResolver { + + private static final TemporalAmountParser DURATION_PARSER = DurationParser.TIME_UNITS; + private static final String TIMES_FORMAT = "%s %s %s"; + + @Override + public NoticeKey noticeKey() { + return NoticeKey.TITLE_TIMES; + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, TitleTimes content) { + audience.sendTitlePart(TitlePart.TIMES, Title.Times.times( + content.fadeIn(), + content.stay(), + content.fadeOut() + )); + } + + @Override + public NoticeSerdesResult serialize(TitleTimes content) { + return new NoticeSerdesResult.Single(String.format(TIMES_FORMAT, + DURATION_PARSER.format(content.fadeIn()), + DURATION_PARSER.format(content.stay()), + DURATION_PARSER.format(content.fadeOut()) + )); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + Optional firstElement = result.firstElement(); + + if (firstElement.isEmpty()) { + return Optional.empty(); + } + + String[] parts = firstElement.get().split(" "); + + if (parts.length != 3) { + throw new IllegalArgumentException("Invalid times format " + firstElement.get()); + } + + return Optional.of(new TitleTimes( + DURATION_PARSER.parse(parts[0]), + DURATION_PARSER.parse(parts[1]), + DURATION_PARSER.parse(parts[2]) + )); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleContent.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleContent.java new file mode 100644 index 0000000..584f7aa --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleContent.java @@ -0,0 +1,11 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.resolver.text.TextContent; +import java.util.List; + +public record TitleContent(String content) implements TextContent { + @Override + public List contents() { + return List.of(content); + } +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHide.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHide.java new file mode 100644 index 0000000..9cd657f --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHide.java @@ -0,0 +1,6 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.resolver.NoticeContent; + +public record TitleHide(boolean isHided) implements NoticeContent { +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHideResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHideResolver.java new file mode 100644 index 0000000..543cbc4 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleHideResolver.java @@ -0,0 +1,38 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeKey; +import com.eternalcode.multification.notice.resolver.NoticeResolver; +import com.eternalcode.multification.notice.resolver.NoticeSerdesResult; +import java.util.Optional; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; + +public class TitleHideResolver implements NoticeResolver { + + @Override + public NoticeKey noticeKey() { + return NoticeKey.TITLE_HIDE; + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, TitleHide content) { + if (content.isHided()) { + audience.clearTitle(); + } + } + + @Override + public NoticeSerdesResult serialize(TitleHide content) { + return new NoticeSerdesResult.Single(String.valueOf(content.isHided())); + } + + @Override + public Optional deserialize(NoticeSerdesResult result) { + Optional firstElement = result.firstElement(); + + return firstElement + .map(value -> new TitleHide(Boolean.parseBoolean(value))); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleResolver.java new file mode 100644 index 0000000..21fed89 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleResolver.java @@ -0,0 +1,20 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeKey; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.title.TitlePart; + +public class TitleResolver extends AbstractTitleContentResolver { + + public TitleResolver() { + super(NoticeKey.TITLE); + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, TitleContent content) { + audience.sendTitlePart(TitlePart.TITLE, componentSerializer.deserialize(content.content())); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleTimes.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleTimes.java new file mode 100644 index 0000000..725b102 --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleTimes.java @@ -0,0 +1,7 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.resolver.NoticeContent; +import java.time.Duration; + +public record TitleTimes(Duration fadeIn, Duration stay, Duration fadeOut) implements NoticeContent { +} diff --git a/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleWithEmptySubtitleResolver.java b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleWithEmptySubtitleResolver.java new file mode 100644 index 0000000..b2efadc --- /dev/null +++ b/multification-core/src/com/eternalcode/multification/notice/resolver/title/TitleWithEmptySubtitleResolver.java @@ -0,0 +1,21 @@ +package com.eternalcode.multification.notice.resolver.title; + +import com.eternalcode.multification.notice.NoticeType; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; +import net.kyori.adventure.title.TitlePart; + +public class TitleWithEmptySubtitleResolver extends AbstractTitleContentResolver { + + public TitleWithEmptySubtitleResolver() { + super(NoticeType.TITLE_WITH_EMPTY_SUBTITLE.toNoticeKey()); + } + + @Override + public void send(Audience audience, ComponentSerializer componentSerializer, TitleContent content) { + audience.sendTitlePart(TitlePart.TITLE, componentSerializer.deserialize(content.content())); + audience.sendTitlePart(TitlePart.SUBTITLE, Component.empty()); + } + +} diff --git a/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcaster.java b/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcaster.java index 0bcf3e5..8b3aaf7 100644 --- a/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcaster.java +++ b/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcaster.java @@ -1,22 +1,36 @@ package com.eternalcode.multification.platform; -import com.eternalcode.multification.notice.NoticeContent; +import com.eternalcode.multification.adventure.PlainComponentSerializer; +import com.eternalcode.multification.notice.resolver.NoticeContent; import com.eternalcode.multification.notice.NoticePart; +import com.eternalcode.multification.notice.resolver.NoticeResolverDefaults; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.ComponentSerializer; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Internal public interface PlatformBroadcaster { void announce(Audience viewer, NoticePart notice); + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") static PlatformBroadcaster withPlainSerializer() { - return new PlatformBroadcasterImpl(new PlainComponentSerializer()); + return new PlatformBroadcasterImpl(new PlainComponentSerializer(), NoticeResolverDefaults.createRegistry()); } + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.1.0") static PlatformBroadcaster withSerializer(ComponentSerializer componentSerializer) { - return new PlatformBroadcasterImpl(componentSerializer); + return new PlatformBroadcasterImpl(componentSerializer, NoticeResolverDefaults.createRegistry()); + } + + @ApiStatus.Internal + static PlatformBroadcaster create(ComponentSerializer componentSerializer, NoticeResolverRegistry noticeRegistry) { + return new PlatformBroadcasterImpl(componentSerializer, noticeRegistry); } } diff --git a/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcasterImpl.java b/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcasterImpl.java index d5c912e..d58ce21 100644 --- a/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcasterImpl.java +++ b/multification-core/src/com/eternalcode/multification/platform/PlatformBroadcasterImpl.java @@ -1,96 +1,25 @@ package com.eternalcode.multification.platform; -import com.eternalcode.multification.notice.NoticeType; -import com.eternalcode.multification.notice.NoticeContent; +import com.eternalcode.multification.notice.resolver.NoticeResolverRegistry; +import com.eternalcode.multification.notice.resolver.NoticeContent; import com.eternalcode.multification.notice.NoticePart; -import java.util.Map; -import java.util.function.BiConsumer; import net.kyori.adventure.audience.Audience; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; -import net.kyori.adventure.sound.Sound.Source; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.ComponentSerializer; -import net.kyori.adventure.title.Title; -import net.kyori.adventure.title.TitlePart; class PlatformBroadcasterImpl implements PlatformBroadcaster { - private final Map> announcers = Map.of( - NoticeType.CHAT, this.text((audience, message) -> audience.sendMessage(message)), - NoticeType.ACTION_BAR, this.text((audience, message) -> audience.sendActionBar(message)), - NoticeType.TITLE, this.text((audience, title) -> { - audience.sendTitlePart(TitlePart.SUBTITLE, Component.empty()); - }), - NoticeType.SUBTITLE, this.text((audience, subtitle) -> { - audience.sendTitlePart(TitlePart.SUBTITLE, subtitle); - }), - NoticeType.TITLE_WITH_EMPTY_SUBTITLE, this.text((audience, title) -> { - audience.sendTitlePart(TitlePart.TITLE, title); - audience.sendTitlePart(TitlePart.SUBTITLE, Component.empty()); - }), - NoticeType.SUBTITLE_WITH_EMPTY_TITLE, this.text((audience, subtitle) -> { - audience.sendTitlePart(TitlePart.TITLE, Component.empty()); - audience.sendTitlePart(TitlePart.SUBTITLE, subtitle); - }), - NoticeType.TITLE_TIMES, new TimesNoticePartAnnouncer(), - NoticeType.TITLE_HIDE, (audience, input) -> audience.clearTitle(), - NoticeType.SOUND, new SoundNoticePartAnnouncer() - ); - private final ComponentSerializer componentSerializer; + private final NoticeResolverRegistry noticeRegistry; - public PlatformBroadcasterImpl(ComponentSerializer componentSerializer) { + public PlatformBroadcasterImpl(ComponentSerializer componentSerializer, NoticeResolverRegistry noticeRegistry) { this.componentSerializer = componentSerializer; + this.noticeRegistry = noticeRegistry; } @Override - @SuppressWarnings("unchecked") public void announce(Audience audience, NoticePart part) { - NoticePartAnnouncer announcer = (NoticePartAnnouncer) this.announcers.get(part.type()); - - if (announcer == null) { - throw new IllegalStateException("No announcer for " + part.type()); - } - - announcer.announce(audience, part.content()); - } - - interface NoticePartAnnouncer { - void announce(Audience audience, T input); - } - - private NoticePartAnnouncer text(BiConsumer consumer) { - return (audience, input) -> { - for (String text : input.messages()) { - consumer.accept(audience, this.componentSerializer.deserialize(text)); - } - }; - } - - static class TimesNoticePartAnnouncer implements NoticePartAnnouncer { - @Override - public void announce(Audience audience, NoticeContent.Times timed) { - Title.Times times = Title.Times.times( - timed.fadeIn(), - timed.stay(), - timed.fadeOut() - ); - - audience.sendTitlePart(TitlePart.TIMES, times); - } - } - - static class SoundNoticePartAnnouncer implements NoticePartAnnouncer { - @Override - public void announce(Audience audience, NoticeContent.Music music) { - String soundKey = music.sound().getKey().getKey(); - Sound sound = music.category() != null - ? Sound.sound(Key.key(soundKey), Source.valueOf(music.category().name()), music.volume(), music.pitch()) - : Sound.sound(Key.key(soundKey), Source.MASTER, music.volume(), music.pitch()); - - audience.playSound(sound); - } + noticeRegistry.send(audience, componentSerializer, part); } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 40ddd0b..d4a3c0c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,3 +2,6 @@ rootProject.name = "multification" include("multification-core") include("multification-cdn") +include("multification-bukkit") + +include("examples:bukkit") From c6096919d6d0a3831c2ee5726d14b69357c96e3f Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 29 Jun 2024 14:07:54 +0200 Subject: [PATCH 2/3] Release v1.1.0 --- buildSrc/src/main/kotlin/multification-publish.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/multification-publish.gradle.kts b/buildSrc/src/main/kotlin/multification-publish.gradle.kts index b705bd2..932f066 100644 --- a/buildSrc/src/main/kotlin/multification-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-publish.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "com.eternalcode" -version = "1.0.3-SNAPSHOT" +version = "1.1.0" java { withSourcesJar() From d844e924750f8a645b0dd9f5009c47f8ced85602 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 29 Jun 2024 14:16:40 +0200 Subject: [PATCH 3/3] Simplify gradle. --- buildSrc/src/main/kotlin/Versions.kt | 9 +++++++++ .../main/kotlin/multification-java-17.gradle.kts | 2 +- .../kotlin/multification-java-unit-test.gradle.kts | 14 +++++++------- .../src/main/kotlin/multification-java.gradle.kts | 2 +- .../main/kotlin/multification-publish.gradle.kts | 4 ++-- .../kotlin/multification-repositories.gradle.kts | 2 +- multification-bukkit/build.gradle.kts | 4 ++-- multification-cdn/build.gradle.kts | 2 +- multification-core/build.gradle.kts | 2 +- 9 files changed, 25 insertions(+), 16 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 4a83d2c..ca89bb4 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -5,4 +5,13 @@ object Versions { const val ADVENTURE_API = "4.18.0-SNAPSHOT" const val CDN = "1.14.5" + const val MOCKITO_CORE = "5.12.0" + const val JUNIT_JUPITER = "5.10.2" + const val ASSERTJ_CORE = "3.26.0" + const val AWAITILITY = "4.2.1" + + const val SPIGOT_API = "1.19.4-R0.1-SNAPSHOT" + + const val JETBRAINS_ANNOTATIONS = "24.1.0" + } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/multification-java-17.gradle.kts b/buildSrc/src/main/kotlin/multification-java-17.gradle.kts index b42d870..1edf928 100644 --- a/buildSrc/src/main/kotlin/multification-java-17.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-java-17.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("java-library") + `java-library` } java { diff --git a/buildSrc/src/main/kotlin/multification-java-unit-test.gradle.kts b/buildSrc/src/main/kotlin/multification-java-unit-test.gradle.kts index 1972ded..9598054 100644 --- a/buildSrc/src/main/kotlin/multification-java-unit-test.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-java-unit-test.gradle.kts @@ -1,14 +1,14 @@ plugins { - id("java-library") + `java-library` } dependencies { - testImplementation("org.mockito:mockito-core:5.12.0") - testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2") - testImplementation("org.junit.jupiter:junit-jupiter-params:5.10.2") - testImplementation("org.assertj:assertj-core:3.26.0") - testImplementation("org.awaitility:awaitility:4.2.1") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.2") + testImplementation("org.mockito:mockito-core:${Versions.MOCKITO_CORE}") + testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT_JUPITER}") + testImplementation("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT_JUPITER}") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT_JUPITER}") + testImplementation("org.assertj:assertj-core:${Versions.ASSERTJ_CORE}") + testImplementation("org.awaitility:awaitility:${Versions.AWAITILITY}") } tasks.getByName("test") { diff --git a/buildSrc/src/main/kotlin/multification-java.gradle.kts b/buildSrc/src/main/kotlin/multification-java.gradle.kts index 7a924e6..3faf415 100644 --- a/buildSrc/src/main/kotlin/multification-java.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-java.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("java-library") + `java-library` } java { diff --git a/buildSrc/src/main/kotlin/multification-publish.gradle.kts b/buildSrc/src/main/kotlin/multification-publish.gradle.kts index 932f066..ec95582 100644 --- a/buildSrc/src/main/kotlin/multification-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-publish.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("java-library") - id("maven-publish") + `java-library` + `maven-publish` } group = "com.eternalcode" diff --git a/buildSrc/src/main/kotlin/multification-repositories.gradle.kts b/buildSrc/src/main/kotlin/multification-repositories.gradle.kts index 0e8c70a..aedf61f 100644 --- a/buildSrc/src/main/kotlin/multification-repositories.gradle.kts +++ b/buildSrc/src/main/kotlin/multification-repositories.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("java-library") + `java-library` } repositories { diff --git a/multification-bukkit/build.gradle.kts b/multification-bukkit/build.gradle.kts index fa0cee9..9b0e0d5 100644 --- a/multification-bukkit/build.gradle.kts +++ b/multification-bukkit/build.gradle.kts @@ -8,6 +8,6 @@ plugins { dependencies { api(project(":multification-core")) - compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") - testImplementation("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + compileOnly("org.spigotmc:spigot-api:${Versions.SPIGOT_API}") + testImplementation("org.spigotmc:spigot-api:${Versions.SPIGOT_API}") } \ No newline at end of file diff --git a/multification-cdn/build.gradle.kts b/multification-cdn/build.gradle.kts index 4980587..a133343 100644 --- a/multification-cdn/build.gradle.kts +++ b/multification-cdn/build.gradle.kts @@ -12,6 +12,6 @@ dependencies { api("net.dzikoysk:cdn:${Versions.CDN}") testImplementation(project(":multification-bukkit")) - testImplementation("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + testImplementation("org.spigotmc:spigot-api:${Versions.SPIGOT_API}") testImplementation("net.kyori:adventure-api:${Versions.ADVENTURE_API}") } diff --git a/multification-core/build.gradle.kts b/multification-core/build.gradle.kts index 3f1e70b..a4a20f7 100644 --- a/multification-core/build.gradle.kts +++ b/multification-core/build.gradle.kts @@ -8,7 +8,7 @@ plugins { dependencies { compileOnlyApi("net.kyori:adventure-api:${Versions.ADVENTURE_API}") - api("org.jetbrains:annotations:24.1.0") + api("org.jetbrains:annotations:${Versions.JETBRAINS_ANNOTATIONS}") testImplementation("net.kyori:adventure-api:${Versions.ADVENTURE_API}") } \ No newline at end of file