From 7fbaaea2705c321709c49f1279a67b3a83bf4d92 Mon Sep 17 00:00:00 2001 From: rafal <63915083+rchomczyk@users.noreply.github.com> Date: Sat, 23 Nov 2024 13:05:29 +0100 Subject: [PATCH] Add extensive support for using dispatchers in configuration --- .idea/gradle.xml | 3 + .../main/kotlin/honey-repositories.gradle.kts | 1 + gradle/libs.versions.toml | 7 +- .../AdventureBatchMessageDispatcher.java | 79 +++++++++++ .../AdventureTitleMessageDispatcher.java | 18 +-- .../dispatcher/BatchMessageDispatcher.java | 11 ++ .../message/dispatcher/MessageDispatcher.java | 24 +--- .../dispatcher/MessagePolyDispatcher.java | 16 +++ .../dispatcher/TitleMessageDispatcher.java | 28 +--- .../honey-configs-common/build.gradle.kts | 14 ++ .../AdventureBatchMessageConfigurer.java | 48 +++++++ .../AdventureMessageConfigurer.java | 56 ++++++++ .../AdventureMessageConfigurers.java | 22 +++ .../dispatcher/AdventureMessageDelivery.java | 22 +++ .../AdventureTitleMessageConfigurer.java | 87 ++++++++++++ .../dispatcher/BatchMessageConfigurer.java | 10 ++ .../MessageConfigurationException.java | 12 ++ .../message/dispatcher/MessageConfigurer.java | 10 ++ .../dispatcher/MessagePolyConfigurer.java | 10 ++ .../dispatcher/TitleMessageConfigurer.java | 16 +++ .../honey-configs-okaeri/build.gradle.kts | 16 +++ ...ntureBatchMessageConfigurerSerializer.java | 55 ++++++++ ...entureMessagePolyConfigurerSerializer.java | 128 ++++++++++++++++++ .../AdventureMessageSerdesPack.java | 18 +++ .../dispatcher/StringToTimesTransformer.java | 52 +++++++ honey-test-plugin/build.gradle.kts | 2 + .../src/dev/shiza/honey/ExampleConfig.java | 17 +++ .../src/dev/shiza/honey/ExampleListener.java | 21 +++ .../src/dev/shiza/honey/ExamplePlugin.java | 24 +++- settings.gradle.kts | 5 +- 30 files changed, 778 insertions(+), 54 deletions(-) create mode 100644 honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageDispatcher.java create mode 100644 honey-common/src/dev/shiza/honey/message/dispatcher/BatchMessageDispatcher.java create mode 100644 honey-common/src/dev/shiza/honey/message/dispatcher/MessagePolyDispatcher.java create mode 100644 honey-configs/honey-configs-common/build.gradle.kts create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurer.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurer.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurers.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageDelivery.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageConfigurer.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/BatchMessageConfigurer.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurationException.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurer.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessagePolyConfigurer.java create mode 100644 honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/TitleMessageConfigurer.java create mode 100644 honey-configs/honey-configs-okaeri/build.gradle.kts create mode 100644 honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurerSerializer.java create mode 100644 honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessagePolyConfigurerSerializer.java create mode 100644 honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageSerdesPack.java create mode 100644 honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/StringToTimesTransformer.java create mode 100644 honey-test-plugin/src/dev/shiza/honey/ExampleConfig.java diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 4a3641f..554a120 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -22,6 +22,9 @@ diff --git a/buildSrc/src/main/kotlin/honey-repositories.gradle.kts b/buildSrc/src/main/kotlin/honey-repositories.gradle.kts index b9c8f62..f701671 100644 --- a/buildSrc/src/main/kotlin/honey-repositories.gradle.kts +++ b/buildSrc/src/main/kotlin/honey-repositories.gradle.kts @@ -4,4 +4,5 @@ plugins { repositories { mavenCentral() + maven("https://storehouse.okaeri.eu/repository/maven-public/") } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 25eb5c1..15ee728 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,12 +2,17 @@ guava = "33.3.1-jre" futures = "0.3.6" adventure = "4.17.0" +okaeri-configs = "5.0.5" [libraries] guava = { module = "com.google.guava:guava", version.ref = "guava" } futures = { module = "com.spotify:completable-futures", version.ref = "futures" } adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure" } adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" } +okaeri-configs = { module = "eu.okaeri:okaeri-configs-yaml-snakeyaml", version.ref = "okaeri-configs" } +okaeri-configs-serdes-commons = { module = "eu.okaeri:okaeri-configs-serdes-commons", version.ref = "okaeri-configs" } +okaeri-configs-yaml-bukkit = { module = "eu.okaeri:okaeri-configs-yaml-bukkit", version.ref = "okaeri-configs" } [bundles] -adventure = ["adventure-api", "adventure-minimessage"] \ No newline at end of file +adventure = ["adventure-api", "adventure-minimessage"] +okaeri-configs = ["okaeri-configs", "okaeri-configs-serdes-commons"] \ No newline at end of file diff --git a/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageDispatcher.java b/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageDispatcher.java new file mode 100644 index 0000000..0f7fb53 --- /dev/null +++ b/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageDispatcher.java @@ -0,0 +1,79 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import com.google.common.collect.ImmutableList; +import com.spotify.futures.CompletableFutures; +import dev.shiza.honey.message.dispatcher.BatchMessageDispatcher; +import dev.shiza.honey.message.dispatcher.MessagePolyDispatcher; +import dev.shiza.honey.message.dispatcher.MessageRenderer; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.UnaryOperator; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; + +public final class AdventureBatchMessageDispatcher + implements BatchMessageDispatcher { + + private final List> dispatchers; + + public AdventureBatchMessageDispatcher( + final List> dispatchers) { + this.dispatchers = ImmutableList.copyOf(dispatchers); + } + + public AdventureBatchMessageDispatcher() { + this(ImmutableList.of()); + } + + @Override + public BatchMessageDispatcher add( + final MessagePolyDispatcher dispatcher) { + return new AdventureBatchMessageDispatcher( + ImmutableList.>builder() + .addAll(dispatchers) + .add(dispatcher) + .build()); + } + + @SafeVarargs + @Override + public final BatchMessageDispatcher addAll( + final MessagePolyDispatcher... dispatchers) { + return new AdventureBatchMessageDispatcher( + ImmutableList.>builder() + .addAll(this.dispatchers) + .addAll(Arrays.asList(dispatchers)) + .build()); + } + + @Override + public MessagePolyDispatcher viewer(final Audience audience) { + return new AdventureBatchMessageDispatcher( + dispatchers.stream() + .map(dispatcher -> dispatcher.viewer(audience)) + .collect(ImmutableList.toImmutableList())); + } + + @Override + public MessagePolyDispatcher placeholders( + final UnaryOperator> consumer) { + return new AdventureBatchMessageDispatcher( + dispatchers.stream() + .map(dispatcher -> dispatcher.placeholders(consumer)) + .collect(ImmutableList.toImmutableList())); + } + + @Override + public void dispatch() { + dispatchers.forEach(MessagePolyDispatcher::dispatch); + } + + @Override + public CompletableFuture dispatchAsync() { + return dispatchers.stream() + .map(MessagePolyDispatcher::dispatchAsync) + .collect(CompletableFutures.joinList()) + .thenAccept(__ -> {}); + } +} diff --git a/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageDispatcher.java b/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageDispatcher.java index 01d2472..c4cce43 100644 --- a/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageDispatcher.java +++ b/honey-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageDispatcher.java @@ -7,7 +7,6 @@ import dev.shiza.honey.message.dispatcher.MessageRenderer; import dev.shiza.honey.message.dispatcher.TitleMessageDispatcher; import java.time.Duration; -import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.function.UnaryOperator; import net.kyori.adventure.audience.Audience; @@ -116,9 +115,9 @@ public TitleMessageDispatcher subtitle( public TitleMessageDispatcher placeholders( final UnaryOperator> consumer) { return new AdventureTitleMessageDispatcher( - times.placeholders(consumer), - title.placeholders(consumer), - subtitle.placeholders(consumer), + (MessageDispatcher) times.placeholders(consumer), + (MessageDispatcher) title.placeholders(consumer), + (MessageDispatcher) subtitle.placeholders(consumer), viewer); } @@ -148,11 +147,12 @@ public void dispatch() { * indicating when all dispatches are complete */ @Override - public CompletableFuture> dispatchAsync() { + public CompletableFuture dispatchAsync() { return CompletableFutures.allAsList( - ImmutableList.of( - times.viewer(viewer).dispatchAsync(), - title.viewer(viewer).dispatchAsync(), - subtitle.viewer(viewer).dispatchAsync())); + ImmutableList.of( + times.viewer(viewer).dispatchAsync(), + title.viewer(viewer).dispatchAsync(), + subtitle.viewer(viewer).dispatchAsync())) + .thenAccept(__ -> {}); } } diff --git a/honey-common/src/dev/shiza/honey/message/dispatcher/BatchMessageDispatcher.java b/honey-common/src/dev/shiza/honey/message/dispatcher/BatchMessageDispatcher.java new file mode 100644 index 0000000..a29042d --- /dev/null +++ b/honey-common/src/dev/shiza/honey/message/dispatcher/BatchMessageDispatcher.java @@ -0,0 +1,11 @@ +package dev.shiza.honey.message.dispatcher; + +public interface BatchMessageDispatcher + extends MessagePolyDispatcher { + + BatchMessageDispatcher add( + final MessagePolyDispatcher dispatcher); + + BatchMessageDispatcher addAll( + final MessagePolyDispatcher... dispatchers); +} diff --git a/honey-common/src/dev/shiza/honey/message/dispatcher/MessageDispatcher.java b/honey-common/src/dev/shiza/honey/message/dispatcher/MessageDispatcher.java index 2d49469..4477484 100644 --- a/honey-common/src/dev/shiza/honey/message/dispatcher/MessageDispatcher.java +++ b/honey-common/src/dev/shiza/honey/message/dispatcher/MessageDispatcher.java @@ -1,7 +1,6 @@ package dev.shiza.honey.message.dispatcher; import dev.shiza.honey.message.formatter.MessageFormatter; -import java.util.concurrent.CompletableFuture; import java.util.function.UnaryOperator; /** @@ -11,14 +10,9 @@ * @param the type of the viewer to whom the message will be sent * @param the type of the result or content of the message */ -public interface MessageDispatcher { +public interface MessageDispatcher extends MessagePolyDispatcher { - /** - * Sets the viewer of the message. - * - * @param viewer the viewer of the message - * @return the current instance of {@code MessageDispatcher} for method chaining - */ + @Override MessageDispatcher viewer(final VIEWER viewer); /** @@ -39,15 +33,7 @@ MessageDispatcher template( */ MessageDispatcher template(final RESULT message); - MessageDispatcher placeholders(final UnaryOperator> consumer); - - /** Dispatches the message synchronously. */ - void dispatch(); - - /** - * Dispatches the message asynchronously. - * - * @return a {@link CompletableFuture} that is completed when the dispatch is done - */ - CompletableFuture dispatchAsync(); + @Override + MessageDispatcher placeholders( + final UnaryOperator> consumer); } diff --git a/honey-common/src/dev/shiza/honey/message/dispatcher/MessagePolyDispatcher.java b/honey-common/src/dev/shiza/honey/message/dispatcher/MessagePolyDispatcher.java new file mode 100644 index 0000000..ad56ef4 --- /dev/null +++ b/honey-common/src/dev/shiza/honey/message/dispatcher/MessagePolyDispatcher.java @@ -0,0 +1,16 @@ +package dev.shiza.honey.message.dispatcher; + +import java.util.concurrent.CompletableFuture; +import java.util.function.UnaryOperator; + +public interface MessagePolyDispatcher { + + MessagePolyDispatcher viewer(final VIEWER viewer); + + MessagePolyDispatcher placeholders( + final UnaryOperator> consumer); + + void dispatch(); + + CompletableFuture dispatchAsync(); +} diff --git a/honey-common/src/dev/shiza/honey/message/dispatcher/TitleMessageDispatcher.java b/honey-common/src/dev/shiza/honey/message/dispatcher/TitleMessageDispatcher.java index 05f40bd..1604fc6 100644 --- a/honey-common/src/dev/shiza/honey/message/dispatcher/TitleMessageDispatcher.java +++ b/honey-common/src/dev/shiza/honey/message/dispatcher/TitleMessageDispatcher.java @@ -1,7 +1,5 @@ package dev.shiza.honey.message.dispatcher; -import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.function.UnaryOperator; /** @@ -11,7 +9,11 @@ * @param The type of the viewer for whom the messages are intended. * @param The result type for operations that are performed by the MessageDispatcher. */ -public interface TitleMessageDispatcher { +public interface TitleMessageDispatcher + extends MessagePolyDispatcher { + + @Override + TitleMessageDispatcher viewer(final VIEWER viewer); /** * Configures the display times of the title message. @@ -41,25 +43,7 @@ TitleMessageDispatcher title( TitleMessageDispatcher subtitle( final UnaryOperator> consumer); + @Override TitleMessageDispatcher placeholders( final UnaryOperator> consumer); - - /** - * Sets the viewer of the title message. - * - * @param viewer The viewer who will receive the message. - * @return An instance of TitleMessageDispatcher for method chaining. - */ - TitleMessageDispatcher viewer(final VIEWER viewer); - - /** Dispatches the title and subtitle to the viewer synchronously. */ - void dispatch(); - - /** - * Dispatches the title and subtitle to the viewer asynchronously. - * - * @return A CompletableFuture that completes with a list of results (normally empty) once the - * message dispatch operation is complete. - */ - CompletableFuture> dispatchAsync(); } diff --git a/honey-configs/honey-configs-common/build.gradle.kts b/honey-configs/honey-configs-common/build.gradle.kts new file mode 100644 index 0000000..a4e80d4 --- /dev/null +++ b/honey-configs/honey-configs-common/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + `honey-java` + `honey-publish` + `honey-repositories` +} + +dependencies { + compileOnly(project(":honey-common")) + compileOnly(libs.bundles.adventure) +} + +honeyPublish { + artifactId = "honey-configs-common" +} \ No newline at end of file diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurer.java new file mode 100644 index 0000000..604b548 --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurer.java @@ -0,0 +1,48 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import com.google.common.collect.ImmutableList; +import dev.shiza.honey.message.dispatcher.BatchMessageConfigurer; +import dev.shiza.honey.message.dispatcher.BatchMessageDispatcher; +import dev.shiza.honey.message.dispatcher.MessagePolyConfigurer; +import dev.shiza.honey.message.dispatcher.MessagePolyDispatcher; +import dev.shiza.honey.message.formatter.MessageFormatter; +import java.util.List; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; + +public final class AdventureBatchMessageConfigurer + implements BatchMessageConfigurer { + + private final List> configurers; + + AdventureBatchMessageConfigurer(final List> configurers) { + this.configurers = ImmutableList.copyOf(configurers); + } + + AdventureBatchMessageConfigurer() { + this(ImmutableList.of()); + } + + @Override + public AdventureBatchMessageConfigurer add(final MessagePolyConfigurer configurer) { + return new AdventureBatchMessageConfigurer( + ImmutableList.>builder() + .addAll(configurers) + .add(configurer) + .build()); + } + + @Override + public BatchMessageDispatcher dispatcher( + final MessageFormatter formatter) { + return new AdventureBatchMessageDispatcher() + .addAll( + configurers.stream() + .map(configurer -> configurer.dispatcher(formatter)) + .toArray(MessagePolyDispatcher[]::new)); + } + + List> configurers() { + return configurers; + } +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurer.java new file mode 100644 index 0000000..a313891 --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurer.java @@ -0,0 +1,56 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import dev.shiza.honey.message.dispatcher.MessageBaseDispatcher; +import dev.shiza.honey.message.dispatcher.MessageConfigurationException; +import dev.shiza.honey.message.dispatcher.MessageConfigurer; +import dev.shiza.honey.message.dispatcher.MessageDispatcher; +import dev.shiza.honey.message.formatter.MessageFormatter; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; + +public final class AdventureMessageConfigurer + implements MessageConfigurer { + + private final AdventureMessageDelivery delivery; + private final String template; + + AdventureMessageConfigurer(final AdventureMessageDelivery delivery, final String template) { + this.delivery = delivery; + this.template = template; + } + + AdventureMessageConfigurer(final AdventureMessageDelivery delivery) { + this(delivery, null); + } + + @Override + public MessageConfigurer delivery( + final AdventureMessageDelivery delivery) { + return new AdventureMessageConfigurer(delivery, template); + } + + @Override + public AdventureMessageConfigurer template(final String template) { + return new AdventureMessageConfigurer(delivery, template); + } + + @Override + public MessageDispatcher dispatcher( + final MessageFormatter formatter) { + if (template == null) { + throw new MessageConfigurationException( + "Could not get a message dispatcher without a template."); + } + + return new MessageBaseDispatcher<>(Audience.empty(), delivery.deliver()) + .template(formatter, template); + } + + AdventureMessageDelivery delivery() { + return delivery; + } + + String template() { + return template; + } +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurers.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurers.java new file mode 100644 index 0000000..7597f9a --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageConfigurers.java @@ -0,0 +1,22 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +public final class AdventureMessageConfigurers { + + private AdventureMessageConfigurers() {} + + public static AdventureMessageConfigurer createChat() { + return new AdventureMessageConfigurer(AdventureMessageDelivery.CHAT); + } + + public static AdventureMessageConfigurer createActionBar() { + return new AdventureMessageConfigurer(AdventureMessageDelivery.ACTION_BAR); + } + + public static AdventureTitleMessageConfigurer createTitle() { + return new AdventureTitleMessageConfigurer(); + } + + public static AdventureBatchMessageConfigurer createBatch() { + return new AdventureBatchMessageConfigurer(); + } +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageDelivery.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageDelivery.java new file mode 100644 index 0000000..32ccfb9 --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageDelivery.java @@ -0,0 +1,22 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import java.util.function.BiConsumer; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.TitlePart; + +public record AdventureMessageDelivery(String id, BiConsumer deliver) { + + static final AdventureMessageDelivery CHAT = + new AdventureMessageDelivery("chat", Audience::sendMessage); + static final AdventureMessageDelivery ACTION_BAR = + new AdventureMessageDelivery("actionbar", Audience::sendActionBar); + static final AdventureMessageDelivery TIMES = + new AdventureMessageDelivery("times", (audience, message) -> {}); + static final AdventureMessageDelivery TITLE = + new AdventureMessageDelivery( + "title", (audience, message) -> audience.sendTitlePart(TitlePart.TITLE, message)); + static final AdventureMessageDelivery SUBTITLE = + new AdventureMessageDelivery( + "subtitle", (audience, message) -> audience.sendTitlePart(TitlePart.SUBTITLE, message)); +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageConfigurer.java new file mode 100644 index 0000000..4ef828e --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/adventure/message/dispatcher/AdventureTitleMessageConfigurer.java @@ -0,0 +1,87 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import dev.shiza.honey.message.dispatcher.MessageBaseDispatcher; +import dev.shiza.honey.message.dispatcher.MessageConfigurer; +import dev.shiza.honey.message.dispatcher.MessageDispatcher; +import dev.shiza.honey.message.dispatcher.TitleMessageConfigurer; +import dev.shiza.honey.message.dispatcher.TitleMessageDispatcher; +import dev.shiza.honey.message.formatter.MessageFormatter; +import java.time.Duration; +import java.util.function.UnaryOperator; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; +import net.kyori.adventure.title.Title.Times; +import net.kyori.adventure.title.TitlePart; + +public final class AdventureTitleMessageConfigurer + implements TitleMessageConfigurer { + + private final Times times; + private final MessageConfigurer title; + private final MessageConfigurer subtitle; + + AdventureTitleMessageConfigurer( + final Times times, + final MessageConfigurer title, + final MessageConfigurer subtitle) { + this.times = times; + this.title = title; + this.subtitle = subtitle; + } + + AdventureTitleMessageConfigurer() { + this( + Title.DEFAULT_TIMES, + new AdventureMessageConfigurer(AdventureMessageDelivery.TITLE), + new AdventureMessageConfigurer(AdventureMessageDelivery.SUBTITLE)); + } + + @Override + public AdventureTitleMessageConfigurer times( + final int fadeIn, final int stay, final int fadeOut) { + final Times titleTimes = + Times.times( + Duration.ofSeconds(fadeIn), Duration.ofSeconds(stay), Duration.ofSeconds(fadeOut)); + return new AdventureTitleMessageConfigurer(titleTimes, title, subtitle); + } + + @Override + public AdventureTitleMessageConfigurer title( + final UnaryOperator> mutator) { + return new AdventureTitleMessageConfigurer(times, mutator.apply(title), subtitle); + } + + @Override + public AdventureTitleMessageConfigurer subtitle( + final UnaryOperator> mutator) { + return new AdventureTitleMessageConfigurer(times, title, mutator.apply(subtitle)); + } + + @Override + public TitleMessageDispatcher dispatcher( + final MessageFormatter formatter) { + final MessageDispatcher timesDispatcher = + new MessageBaseDispatcher( + Audience.empty(), + (audience, component) -> audience.sendTitlePart(TitlePart.TIMES, times)) + .template(Component.empty()); + return new AdventureTitleMessageDispatcher( + timesDispatcher, + (MessageDispatcher) title.dispatcher(formatter), + (MessageDispatcher) subtitle.dispatcher(formatter), + Audience.empty()); + } + + Times times() { + return times; + } + + MessageConfigurer title() { + return title; + } + + MessageConfigurer subtitle() { + return subtitle; + } +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/BatchMessageConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/BatchMessageConfigurer.java new file mode 100644 index 0000000..fe6157a --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/BatchMessageConfigurer.java @@ -0,0 +1,10 @@ +package dev.shiza.honey.message.dispatcher; + +import dev.shiza.honey.message.formatter.MessageFormatter; + +public interface BatchMessageConfigurer { + + BatchMessageConfigurer add(final MessagePolyConfigurer configurer); + + BatchMessageDispatcher dispatcher(final MessageFormatter formatter); +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurationException.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurationException.java new file mode 100644 index 0000000..88b85b6 --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurationException.java @@ -0,0 +1,12 @@ +package dev.shiza.honey.message.dispatcher; + +public final class MessageConfigurationException extends IllegalStateException { + + public MessageConfigurationException(final String message) { + super(message); + } + + public MessageConfigurationException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurer.java new file mode 100644 index 0000000..2c79390 --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessageConfigurer.java @@ -0,0 +1,10 @@ +package dev.shiza.honey.message.dispatcher; + +import dev.shiza.honey.adventure.message.dispatcher.AdventureMessageDelivery; + +public interface MessageConfigurer extends MessagePolyConfigurer { + + MessageConfigurer delivery(final AdventureMessageDelivery delivery); + + MessageConfigurer template(final MESSAGE template); +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessagePolyConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessagePolyConfigurer.java new file mode 100644 index 0000000..5ac335b --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/MessagePolyConfigurer.java @@ -0,0 +1,10 @@ +package dev.shiza.honey.message.dispatcher; + +import dev.shiza.honey.message.formatter.MessageFormatter; +import org.jetbrains.annotations.ApiStatus.Internal; + +@Internal +public interface MessagePolyConfigurer { + + MessagePolyDispatcher dispatcher(final MessageFormatter formatter); +} diff --git a/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/TitleMessageConfigurer.java b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/TitleMessageConfigurer.java new file mode 100644 index 0000000..bcca873 --- /dev/null +++ b/honey-configs/honey-configs-common/src/dev/shiza/honey/message/dispatcher/TitleMessageConfigurer.java @@ -0,0 +1,16 @@ +package dev.shiza.honey.message.dispatcher; + +import java.util.function.UnaryOperator; + +public interface TitleMessageConfigurer extends + MessagePolyConfigurer { + + TitleMessageConfigurer times( + final int fadeIn, final int stay, final int fadeOut); + + TitleMessageConfigurer title( + final UnaryOperator> mutator); + + TitleMessageConfigurer subtitle( + final UnaryOperator> mutator); +} diff --git a/honey-configs/honey-configs-okaeri/build.gradle.kts b/honey-configs/honey-configs-okaeri/build.gradle.kts new file mode 100644 index 0000000..412ba14 --- /dev/null +++ b/honey-configs/honey-configs-okaeri/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + `honey-java` + `honey-publish` + `honey-repositories` +} + +dependencies { + compileOnly(project(":honey-common")) + compileOnly(libs.bundles.adventure) + api(project(":honey-configs:honey-configs-common")) + api(libs.bundles.okaeri.configs) +} + +honeyPublish { + artifactId = "honey-configs-okaeri" +} \ No newline at end of file diff --git a/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurerSerializer.java b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurerSerializer.java new file mode 100644 index 0000000..838ce27 --- /dev/null +++ b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureBatchMessageConfigurerSerializer.java @@ -0,0 +1,55 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import com.google.common.collect.ImmutableList; +import dev.shiza.honey.message.dispatcher.MessagePolyConfigurer; +import eu.okaeri.configs.schema.GenericsDeclaration; +import eu.okaeri.configs.serdes.DeserializationData; +import eu.okaeri.configs.serdes.ObjectSerializer; +import eu.okaeri.configs.serdes.SerializationData; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.NotNull; + +final class AdventureBatchMessageConfigurerSerializer + implements ObjectSerializer { + + AdventureBatchMessageConfigurerSerializer() {} + + @Override + public boolean supports(final @NotNull Class type) { + return AdventureBatchMessageConfigurer.class.isAssignableFrom(type); + } + + @Override + public void serialize( + final @NotNull AdventureBatchMessageConfigurer object, + final @NotNull SerializationData data, + final @NotNull GenericsDeclaration generics) { + if (object.configurers().size() == 1) { + data.add(VALUE, object.configurers().get(0)); + } else { + data.addCollection(VALUE, object.configurers(), MessagePolyConfigurer.class); + } + } + + @Override + public AdventureBatchMessageConfigurer deserialize( + final @NotNull DeserializationData data, final @NotNull GenericsDeclaration generics) { + final boolean isPolyConfigurer = + data.getRaw(VALUE) instanceof MessagePolyConfigurer; + final boolean isSomeConfigurer = + data.getRaw(VALUE) instanceof Map map + && map.containsKey(AdventureMessageDelivery.ACTION_BAR.id()); + if (isPolyConfigurer || isSomeConfigurer) { + return new AdventureBatchMessageConfigurer() + .add(data.get(VALUE, MessagePolyConfigurer.class)); + } + + return new AdventureBatchMessageConfigurer( + ImmutableList.copyOf( + data.getAsCollection( + VALUE, + GenericsDeclaration.of(ArrayList.class, List.of(MessagePolyConfigurer.class))))); + } +} diff --git a/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessagePolyConfigurerSerializer.java b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessagePolyConfigurerSerializer.java new file mode 100644 index 0000000..b95b19d --- /dev/null +++ b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessagePolyConfigurerSerializer.java @@ -0,0 +1,128 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import dev.shiza.honey.message.dispatcher.MessageConfigurationException; +import dev.shiza.honey.message.dispatcher.MessageConfigurer; +import dev.shiza.honey.message.dispatcher.MessagePolyConfigurer; +import eu.okaeri.configs.schema.GenericsDeclaration; +import eu.okaeri.configs.serdes.DeserializationData; +import eu.okaeri.configs.serdes.ObjectSerializer; +import eu.okaeri.configs.serdes.SerializationData; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Supplier; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title.Times; +import org.jetbrains.annotations.NotNull; + +final class AdventureMessagePolyConfigurerSerializer + implements ObjectSerializer> { + + private final MessageConfigurerSerializer messageConfigurerSerializer; + private final TitleMessageConfigurerSerializer titleConfigurerSerializer; + + AdventureMessagePolyConfigurerSerializer() { + this.messageConfigurerSerializer = new MessageConfigurerSerializer(); + this.titleConfigurerSerializer = + new TitleMessageConfigurerSerializer(messageConfigurerSerializer); + } + + @Override + public boolean supports( + final @NotNull Class> type) { + return MessagePolyConfigurer.class.isAssignableFrom(type); + } + + @Override + public void serialize( + final @NotNull MessagePolyConfigurer object, + final @NotNull SerializationData data, + final @NotNull GenericsDeclaration generics) { + if (object instanceof AdventureMessageConfigurer messageConfigurer) { + messageConfigurerSerializer.serialize(messageConfigurer, data); + } else if (object instanceof AdventureTitleMessageConfigurer titleConfigurer) { + titleConfigurerSerializer.serialize(titleConfigurer, data); + } else { + throw new MessageConfigurationException("Could not serialize the given message configurer."); + } + } + + @Override + public MessagePolyConfigurer deserialize( + final @NotNull DeserializationData data, final @NotNull GenericsDeclaration generics) { + final Map dataAsMap = data.asMap(); + return dataAsMap.containsKey(AdventureMessageDelivery.TIMES.id()) + ? titleConfigurerSerializer.deserialize(data) + : messageConfigurerSerializer.deserialize(dataAsMap); + } + + private record MessageConfigurerSerializer() { + + private static final Map> FACTORIES = + Map.of( + AdventureMessageDelivery.CHAT.id(), + AdventureMessageConfigurers::createChat, + AdventureMessageDelivery.ACTION_BAR.id(), + AdventureMessageConfigurers::createActionBar); + + public void serialize(final AdventureMessageConfigurer object, final SerializationData data) { + data.add( + object.delivery().equals(AdventureMessageDelivery.CHAT) + ? VALUE + : object.delivery().id(), + object.template()); + } + + public AdventureMessageConfigurer deserialize(final Map data) { + if (data.containsKey(VALUE) + && data.get(VALUE) instanceof String template) { + return FACTORIES.get(AdventureMessageDelivery.CHAT.id()).get().template(template); + } + + for (final Entry entry : data.entrySet()) { + if (entry.getValue() instanceof String template && FACTORIES.containsKey(entry.getKey())) { + return FACTORIES.get(entry.getKey()).get().template(template); + } + } + + throw new MessageConfigurationException( + "Could not found a message delivery for the given data."); + } + } + + private record TitleMessageConfigurerSerializer( + MessageConfigurerSerializer messageConfigurerSerializer) { + + public void serialize( + final AdventureTitleMessageConfigurer object, final SerializationData data) { + data.add(AdventureMessageDelivery.TIMES.id(), object.times(), Times.class); + serialize(data, object.title(), AdventureMessageDelivery.TITLE); + serialize(data, object.subtitle(), AdventureMessageDelivery.SUBTITLE); + } + + public AdventureTitleMessageConfigurer deserialize(final DeserializationData data) { + return new AdventureTitleMessageConfigurer( + data.get(AdventureMessageDelivery.TIMES.id(), Times.class), + deserialize(data, AdventureMessageDelivery.TITLE), + deserialize(data, AdventureMessageDelivery.SUBTITLE)); + } + + private void serialize( + final SerializationData data, + final MessageConfigurer object, + final AdventureMessageDelivery delivery) { + data.add( + delivery.id(), + object.delivery(AdventureMessageDelivery.CHAT), + AdventureMessageConfigurer.class); + } + + private MessageConfigurer deserialize( + final DeserializationData data, final AdventureMessageDelivery delivery) { + return messageConfigurerSerializer + .deserialize( + Map.of(AdventureMessageDelivery.CHAT.id(), data.get(delivery.id(), String.class))) + .delivery(delivery); + } + } +} diff --git a/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageSerdesPack.java b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageSerdesPack.java new file mode 100644 index 0000000..b395be9 --- /dev/null +++ b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/AdventureMessageSerdesPack.java @@ -0,0 +1,18 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import eu.okaeri.configs.serdes.OkaeriSerdesPack; +import eu.okaeri.configs.serdes.SerdesRegistry; +import eu.okaeri.configs.serdes.commons.SerdesCommons; +import eu.okaeri.configs.serdes.commons.duration.DurationTransformer; +import org.jetbrains.annotations.NotNull; + +public final class AdventureMessageSerdesPack implements OkaeriSerdesPack { + + @Override + public void register(final @NotNull SerdesRegistry registry) { + registry.register(new SerdesCommons()); + registry.register(new StringToTimesTransformer(new DurationTransformer())); + registry.register(new AdventureMessagePolyConfigurerSerializer()); + registry.register(new AdventureBatchMessageConfigurerSerializer()); + } +} diff --git a/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/StringToTimesTransformer.java b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/StringToTimesTransformer.java new file mode 100644 index 0000000..37b6a4d --- /dev/null +++ b/honey-configs/honey-configs-okaeri/src/dev/shiza/honey/adventure/message/dispatcher/StringToTimesTransformer.java @@ -0,0 +1,52 @@ +package dev.shiza.honey.adventure.message.dispatcher; + +import dev.shiza.honey.message.dispatcher.MessageConfigurationException; +import eu.okaeri.configs.schema.GenericsPair; +import eu.okaeri.configs.serdes.BidirectionalTransformer; +import eu.okaeri.configs.serdes.SerdesContext; +import eu.okaeri.configs.serdes.commons.duration.DurationTransformer; +import net.kyori.adventure.title.Title.Times; +import org.jetbrains.annotations.NotNull; + +final class StringToTimesTransformer extends BidirectionalTransformer { + + private static final String DELIMITER = " "; + private final DurationTransformer durationTransformer; + + StringToTimesTransformer(final DurationTransformer durationTransformer) { + this.durationTransformer = durationTransformer; + } + + @Override + public GenericsPair getPair() { + return genericsPair(String.class, Times.class); + } + + @Override + public Times leftToRight(final @NotNull String data, final @NotNull SerdesContext serdesContext) { + final String[] parts = data.split(DELIMITER); + if (parts.length != 3) { + throw new MessageConfigurationException( + "Could not deserialize title times, because of invalid format."); + } + + try { + return Times.times( + durationTransformer.leftToRight(parts[0], serdesContext), + durationTransformer.leftToRight(parts[1], serdesContext), + durationTransformer.leftToRight(parts[2], serdesContext)); + } catch (final NumberFormatException exception) { + throw new MessageConfigurationException( + "Could not deserialize title times, because of invalid format.", exception); + } + } + + @Override + public String rightToLeft(final @NotNull Times data, final @NotNull SerdesContext serdesContext) { + return "%s %s %s" + .formatted( + durationTransformer.rightToLeft(data.fadeIn(), serdesContext), + durationTransformer.rightToLeft(data.stay(), serdesContext), + durationTransformer.rightToLeft(data.fadeOut(), serdesContext)); + } +} diff --git a/honey-test-plugin/build.gradle.kts b/honey-test-plugin/build.gradle.kts index 51de217..3b26f2e 100644 --- a/honey-test-plugin/build.gradle.kts +++ b/honey-test-plugin/build.gradle.kts @@ -16,6 +16,8 @@ dependencies { dependencies { implementation(project(":honey-common")) + implementation(project(":honey-configs:honey-configs-okaeri")) + implementation(libs.okaeri.configs.yaml.bukkit) } java { diff --git a/honey-test-plugin/src/dev/shiza/honey/ExampleConfig.java b/honey-test-plugin/src/dev/shiza/honey/ExampleConfig.java new file mode 100644 index 0000000..b03191d --- /dev/null +++ b/honey-test-plugin/src/dev/shiza/honey/ExampleConfig.java @@ -0,0 +1,17 @@ +package dev.shiza.honey; + +import dev.shiza.honey.adventure.message.dispatcher.AdventureBatchMessageConfigurer; +import dev.shiza.honey.adventure.message.dispatcher.AdventureMessageConfigurers; +import eu.okaeri.configs.OkaeriConfig; + +public final class ExampleConfig extends OkaeriConfig { + + public AdventureBatchMessageConfigurer greeting = + AdventureMessageConfigurers.createBatch() + .add(AdventureMessageConfigurers.createChat().template("Hello, {{player.getName}}!")) + .add( + AdventureMessageConfigurers.createTitle() + .title(it -> it.template("Welcome, {{player.getName}}!")) + .subtitle( + it -> it.template("It is a pleasure to see you there, {{player.getName}}"))); +} diff --git a/honey-test-plugin/src/dev/shiza/honey/ExampleListener.java b/honey-test-plugin/src/dev/shiza/honey/ExampleListener.java index 689454a..78c6a84 100644 --- a/honey-test-plugin/src/dev/shiza/honey/ExampleListener.java +++ b/honey-test-plugin/src/dev/shiza/honey/ExampleListener.java @@ -11,12 +11,18 @@ final class ExampleListener implements Listener { + private final ExamplePlugin examplePlugin; + private final ExampleConfig exampleConfig; private final AdventureMessageFormatter defaultMessageFormatter; private final AdventureMessageFormatter reflectMessageFormatter; ExampleListener( + final ExamplePlugin examplePlugin, + final ExampleConfig exampleConfig, final AdventureMessageFormatter defaultMessageFormatter, final AdventureMessageFormatter reflectMessageFormatter) { + this.examplePlugin = examplePlugin; + this.exampleConfig = exampleConfig; this.defaultMessageFormatter = defaultMessageFormatter; this.reflectMessageFormatter = reflectMessageFormatter; } @@ -50,5 +56,20 @@ public void onPlayerJoin(final PlayerJoinEvent event) { .viewer(Bukkit.getServer()) .template(Component.text("Somebody joined to the server!").color(NamedTextColor.RED)) .dispatch(); + + // 4) Using dispatcher configurers with configuration + examplePlugin + .getServer() + .getScheduler() + .runTaskLater( + examplePlugin, + () -> + exampleConfig + .greeting + .dispatcher(reflectMessageFormatter) + .viewer(event.getPlayer()) + .placeholders(mapping -> mapping.replace("player", event.getPlayer())) + .dispatch(), + 20 * 15L); } } diff --git a/honey-test-plugin/src/dev/shiza/honey/ExamplePlugin.java b/honey-test-plugin/src/dev/shiza/honey/ExamplePlugin.java index 6d2d2db..0e71d4c 100644 --- a/honey-test-plugin/src/dev/shiza/honey/ExamplePlugin.java +++ b/honey-test-plugin/src/dev/shiza/honey/ExamplePlugin.java @@ -1,6 +1,7 @@ package dev.shiza.honey; import dev.shiza.honey.adventure.message.compiler.AdventureMessageCompilerFactory; +import dev.shiza.honey.adventure.message.dispatcher.AdventureMessageSerdesPack; import dev.shiza.honey.adventure.message.formatter.AdventureMessageFormatter; import dev.shiza.honey.adventure.message.formatter.AdventureMessageFormatterFactory; import dev.shiza.honey.adventure.placeholder.sanitizer.AdventurePlaceholderSanitizerFactory; @@ -16,6 +17,8 @@ import dev.shiza.honey.placeholder.sanitizer.PlaceholderSanitizer; import dev.shiza.honey.processor.ProcessorRegistry; import dev.shiza.honey.processor.ProcessorRegistryFactory; +import eu.okaeri.configs.ConfigManager; +import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer; import net.kyori.adventure.text.Component; import org.bukkit.plugin.java.JavaPlugin; @@ -23,9 +26,26 @@ public final class ExamplePlugin extends JavaPlugin { @Override public void onEnable() { - final AdventureMessageFormatter defaultMessageFormatter = AdventureMessageFormatterFactory.create(); + final AdventureMessageFormatter defaultMessageFormatter = + AdventureMessageFormatterFactory.create(); final AdventureMessageFormatter reflectMessageFormatter = createReflectMessageFormatter(); - getServer().getPluginManager().registerEvents(new ExampleListener(defaultMessageFormatter, reflectMessageFormatter), this); + + final ExampleConfig exampleConfig = + ConfigManager.create( + ExampleConfig.class, + initializer -> + initializer + .withConfigurer(new YamlSnakeYamlConfigurer(), new AdventureMessageSerdesPack()) + .withBindFile(getDataPath().resolve("config.yml")) + .saveDefaults() + .load(true)); + + getServer() + .getPluginManager() + .registerEvents( + new ExampleListener( + this, exampleConfig, defaultMessageFormatter, reflectMessageFormatter), + this); } private AdventureMessageFormatter createReflectMessageFormatter() { diff --git a/settings.gradle.kts b/settings.gradle.kts index 6818302..d063314 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,9 @@ rootProject.name = "honey" include(":honey-common") +include(":honey-configs:honey-configs-common") +include(":honey-configs:honey-configs-okaeri") include(":honey-kt-extension") // Uncomment in case if you would like to run test plugin -// include(":honey-test-plugin") \ No newline at end of file +// include(":honey-test-plugin") +