diff --git a/.gitignore b/.gitignore index 75c1a8ad..2e7a81c5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ latest.log.lck config/messageSystem.properties= Core/src/main/java/net/juligames/core/Test.java config/messageSystem.properties +VelocityCore/src/main/java/test/ConversationTest.java diff --git a/.run/CoreMaster.run.xml b/.run/CoreMaster.run.xml new file mode 100644 index 00000000..e7c386b6 --- /dev/null +++ b/.run/CoreMaster.run.xml @@ -0,0 +1,19 @@ + + + + \ No newline at end of file diff --git a/API/pom.xml b/API/pom.xml index ddf41928..9fd80cd9 100644 --- a/API/pom.xml +++ b/API/pom.xml @@ -1,12 +1,18 @@ + JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 diff --git a/API/src/main/java/net/juligames/core/api/API.java b/API/src/main/java/net/juligames/core/api/API.java index bbdef188..d21192cf 100644 --- a/API/src/main/java/net/juligames/core/api/API.java +++ b/API/src/main/java/net/juligames/core/api/API.java @@ -1,6 +1,7 @@ package net.juligames.core.api; import de.bentzin.tools.logging.Logger; +import de.bentzin.tools.logging.Logging; import de.bentzin.tools.misc.SubscribableType; import net.juligames.core.api.cacheing.CacheApi; import net.juligames.core.api.cluster.ClusterApi; @@ -22,13 +23,14 @@ import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.logging.LogManager; /** * @author Ture Bentzin * 16.11.2022 * @implNote Main class to be used to get the different parts of the api */ -public interface API { +public interface API extends Logging { /** * This is the primary way for getting your API Instance. With this Instance you have access to all build in features in the core @@ -148,6 +150,16 @@ public interface API { @NotNull Collection supplyOnlineRecipients(); + /** + * When using a {@link LogManager} you should use {@link API#getJavaLogManager()} to get hold of it + * + * @return an {@link LogManager} instance that is or acts like {@link LogManager#getLogManager()} + * @apiNote This is not associated with the {@link de.bentzin.tools.logging} package and environment + */ + @ApiStatus.Experimental + @ApiStatus.AvailableSince("1.6") + @NotNull LogManager getJavaLogManager(); + /* * * @return if the core is connected and ready for operation diff --git a/API/src/main/java/net/juligames/core/api/TODO.java b/API/src/main/java/net/juligames/core/api/TODO.java index bc7364ba..1c407060 100644 --- a/API/src/main/java/net/juligames/core/api/TODO.java +++ b/API/src/main/java/net/juligames/core/api/TODO.java @@ -8,5 +8,5 @@ */ @SuppressWarnings("JavadocDeclaration") public @interface TODO { - boolean doNotcall(); + boolean doNotcall() default false; } diff --git a/API/src/main/java/net/juligames/core/api/config/ConfigurationAPI.java b/API/src/main/java/net/juligames/core/api/config/ConfigurationAPI.java index e73287ec..ff32427c 100644 --- a/API/src/main/java/net/juligames/core/api/config/ConfigurationAPI.java +++ b/API/src/main/java/net/juligames/core/api/config/ConfigurationAPI.java @@ -3,6 +3,7 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import java.util.Collection; import java.util.Comparator; @@ -140,4 +141,10 @@ default void spiltAndWrite(@NotNull Collection collection, @NotNull Inter */ @ApiStatus.AvailableSince("1.5") @NotNull Configuration createSectionClone(@NotNull Configuration root, @NotNull String section); + + @ApiStatus.AvailableSince("1.6") + @NotNull @Unmodifiable Collection getAll(); + + @ApiStatus.AvailableSince("1.6") + @NotNull @Unmodifiable Collection getAllHazels(); } diff --git a/API/src/main/java/net/juligames/core/api/config/EnumInterpreter.java b/API/src/main/java/net/juligames/core/api/config/EnumInterpreter.java index cd3e9b52..b7b279e1 100644 --- a/API/src/main/java/net/juligames/core/api/config/EnumInterpreter.java +++ b/API/src/main/java/net/juligames/core/api/config/EnumInterpreter.java @@ -4,6 +4,33 @@ import org.jetbrains.annotations.NotNull; /** + * Here is an exmaple on how to use the {@link EnumInterpreter} with the fictional enum "Color" + *
+ * {@code
+ * public enum Color {
+ *     RED,
+ *     GREEN,
+ *     BLUE
+ * }
+ *
+ * public class Main {
+ *     public static void main(String[] args) {
+ *         EnumInterpreter colorInterpreter = new EnumInterpreter<>(Color.class);
+ *
+ *         // Reverse an enum value to a string
+ *         Color color = Color.RED;
+ *         String reversed = colorInterpreter.reverse(color);
+ *         System.out.println("Reversed: " + reversed); // Output: Reversed: RED
+ *
+ *         // Interpret a string as an enum value
+ *         String input = "GREEN";
+ *         Color interpreted = colorInterpreter.interpret(input);
+ *         System.out.println("Interpreted: " + interpreted); // Output: Interpreted: GREEN
+ *     }
+ * }
+ * }
+ * 
+ * * @author Ture Bentzin * 15.03.2023 */ @@ -13,21 +40,46 @@ public class EnumInterpreter> implements Interpreter { private final Class enumClazz; + /** + * Constructs a new {@link EnumInterpreter} instance for the specified enum class. + * + * @param enumClazz the class object for the enum type + */ public EnumInterpreter(Class enumClazz) { this.enumClazz = enumClazz; } + /** + * Interprets the input string as an enum value of type T. + * + * @param input the input string to be interpreted + * @return the interpreted enum value + * @throws IllegalArgumentException if the input string is not a valid name of an enum constant belonging to type T + */ @Override - public @NotNull T interpret(@NotNull String input) throws Exception { + public @NotNull T interpret(@NotNull String input) throws IllegalArgumentException { return T.valueOf(enumClazz, input); } + /** + * Returns the name of the specified enum value. + * + * @param t the enum value to be reversed + * @return the name of the enum value + */ @Override public @NotNull String reverse(@NotNull T t) { return t.name(); } + /** + * Returns the class object for the enum type. + * + * @return the class object for the enum type + */ public Class getEnumClazz() { return enumClazz; } } + + diff --git a/API/src/main/java/net/juligames/core/api/config/Interpreter.java b/API/src/main/java/net/juligames/core/api/config/Interpreter.java index 996faabc..7f06ceb0 100644 --- a/API/src/main/java/net/juligames/core/api/config/Interpreter.java +++ b/API/src/main/java/net/juligames/core/api/config/Interpreter.java @@ -1,25 +1,124 @@ package net.juligames.core.api.config; +import de.bentzin.tools.DoNotOverride; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; /** + * This interface defines methods for interpreting a string as an instance of a certain type and for reversing the + * interpretation back to a string. + *

+ * This is an example implementation for interpreting Integers + *

+ * {@code
+ *
+ * public final class IntegerInterpreter implements Interpreter {
+ *
+ *     @Override
+ *     public Integer interpret(String input) throws Exception {
+ *         return Integer.parseInt(input);
+ *     }
+ *
+ *     @Override
+ *     public String reverse(Integer integer) {
+ *         return Integer.toString(integer);
+ *     }
+ *
+ *     public Class getType() {
+ *         return Integer.class;
+ *     }
+ * }
+ * }
+ * 
+ * + * @param the type of object that this Interpreter can interpret * @author Ture Bentzin * 26.11.2022 * @see BuildInInterpreters */ +public non-sealed interface Interpreter extends Reverser, PrimitiveInterpreter { -public interface Interpreter { + /** + * Converts the given input according to this implementation to a given Object of the provided Type. + * This process is known as "interpreting the input as an instance of Type T" + * + * @param input the input to be interpreted + * @return the instance of T that results from the interpretation + * @throws Exception will be thrown if the input can't be interpreted according to this implementation + */ @NotNull T interpret(final String input) throws Exception; + /** + * Converts the given instance of T according to this implementation to a String with the intention that this string + * can be interpreted back to the instance of T with the correct implementation of {@link Interpreter} that + * implements this {@link Reverser}. + * It should always be possible to convert t to a string! + * This process is known as "reversing the interpretation of a string back to the string" + * + * @param t the object to be reversed + * @return the string that was reversed from the instance of T + */ @NotNull String reverse(T t); + /** + * Applies the given function to the result of {@link #reverse(T)} and returns its result. + * + * @param t the object to be reversed + * @param function the function to apply to the result of {@link #reverse(T)} + * @param the type of the result of the function + * @return the result of applying the function to the result of {@link #reverse(T)} + */ default R reverseAndThen(@NotNull T t, @NotNull Function function) { return function.apply(reverse(t)); } - default R interpretAndThen(@NotNull String input, @NotNull Function function) throws Exception { - return function.apply(interpret(input)); + /** + * Completes the given CompletableFuture with the result of {@link #reverse(T)}. + * + * @param t the object to be reversed + * @param completableFuture the CompletableFuture to complete with the result of {@link #reverse(T)} + * @param the type of the CompletableFuture + */ + @ApiStatus.AvailableSince("1.6") + @ApiStatus.Experimental + default void reverseAndThenComplete(@NotNull T t, @NotNull CompletableFuture completableFuture) { + completableFuture.complete(reverse(t)); } + + /** + * Interprets the given input according to this implementation to an instance of T and completes the given + * CompletableFuture with the result of the interpretation. This process is known as "interpreting the input as an + * instance of Type T". If the interpretation fails, an Exception will be thrown. + * + * @param input the input to be interpreted + * @param completableFuture the CompletableFuture that should be completed with the result of the interpretation + * @throws Exception will be thrown if the input cant be interpreted according to this implementation + */ + @ApiStatus.AvailableSince("1.6") + @ApiStatus.Experimental + default void interpretAndThenComplete(@NotNull String input, @NotNull CompletableFuture completableFuture) throws Exception { + completableFuture.complete(interpret(input)); + } + + /** + * This will return a cast of this to a {@link Reverser} + */ + @DoNotOverride + @ApiStatus.AvailableSince("1.6") + default Reverser asReverser() { + return this; + } + + /** + * This will return a cast of this to a {@link PrimitiveInterpreter} + */ + @DoNotOverride + @ApiStatus.AvailableSince("1.6") + default PrimitiveInterpreter asIInterpreter() { + return this; + } + } diff --git a/API/src/main/java/net/juligames/core/api/config/PrimitiveInterpreter.java b/API/src/main/java/net/juligames/core/api/config/PrimitiveInterpreter.java new file mode 100644 index 00000000..2fdecf0c --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/config/PrimitiveInterpreter.java @@ -0,0 +1,26 @@ +package net.juligames.core.api.config; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * This might be used later outside of this package + * + * @author Ture Bentzin + * 12.04.2023 + */ +@ApiStatus.Internal +@ApiStatus.AvailableSince("1.6") +public sealed interface PrimitiveInterpreter permits Interpreter { + + /** + * Converts the given input according to this implementation to a given Object of the provided Type. + * This process is known as "interpreting the input as an instance of Type T" + * + * @param input the input to be interpreted + * @return the instance of T that results the interpretation + * @throws Exception will be thrown if the input cant be interpreted according to this implementation + */ + @SuppressWarnings("override") + @NotNull T interpret(final String input) throws Exception; +} diff --git a/API/src/main/java/net/juligames/core/api/config/Reverser.java b/API/src/main/java/net/juligames/core/api/config/Reverser.java new file mode 100644 index 00000000..a718505a --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/config/Reverser.java @@ -0,0 +1,97 @@ +package net.juligames.core.api.config; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +/** + * This might be used later outside of this package + * + * @author Ture Bentzin + * 12.04.2023 + */ +@ApiStatus.Internal +@ApiStatus.AvailableSince("1.6") +public sealed interface Reverser permits Interpreter, Reverser.PrivateReverser { + + /** + * The function#apply should never return null + * + * @param function the function + * @param Type + * @return a reverser based on the function + */ + @Contract(pure = true) + static @NotNull Reverser ofFunctional(@NotNull Function function) { + return (PrivateReverser) function::apply; + } + + /** + * This will return a new {@link Reverser} that will execute the {@link #reverse(Object)} method from the given + * tReverser. This only benefits of you want to prevent casting this {@link Reverser} to a {@link Interpreter}! + * + * @param tReverser the reverser + * @param T + * @return a new reverser + */ + @ApiStatus.Experimental + @Contract(pure = true) + static @NotNull Reverser linkOf(@NotNull Reverser tReverser) { + return ofFunctional(tReverser::reverse); + } + + /** + * This {@link Reverser} will reverse the given object ONLY based on its toString! + * Should only be used with EXTREME caution! + * This does not respect {@link CustomInterpretable} + * + * @param T + * @return a new reverser + */ + @ApiStatus.Experimental + @Contract(pure = true) + static @NotNull Reverser fromToString() { + return (PrivateReverser) Object::toString; + } + + /** + * This {@link Reverser} will reverse the given object ONLY based on its {@link String#valueOf(Object)}! + * Should only be used with EXTREME caution! + * This does not respect {@link CustomInterpretable} + * + * @param T + * @return a new reverser + */ + @ApiStatus.Experimental + @Contract(pure = true) + static @NotNull Reverser fromValueOf() { + return (PrivateReverser) String::valueOf; + } + + /** + * Converts the given instance of T according to this implementation to a String with the intention that this string + * can be interpreted back to the instance of T with the correct implementation of {@link Interpreter} that + * implements this {@link Reverser}. + * It should always be possible to convert t to a string! + * This process is known as "reversing the interpretation of a string back to the string" + * + * @param t the object to be reversed + * @return the string that was reversed from the instance of T + */ + @SuppressWarnings("override") + @NotNull String reverse(T t); + + /** + * Meant ONLY for use with {@link #ofFunctional(Function)} + * + * @param the type + */ + @ApiStatus.Internal + @ApiStatus.Experimental + non-sealed interface PrivateReverser extends Reverser { + @Override + public abstract @NotNull String reverse(T t); + } +} diff --git a/API/src/main/java/net/juligames/core/api/config/mapbacked/DictionaryFeedInterpreter.java b/API/src/main/java/net/juligames/core/api/config/mapbacked/DictionaryFeedInterpreter.java index 6142d7b4..7bef683e 100644 --- a/API/src/main/java/net/juligames/core/api/config/mapbacked/DictionaryFeedInterpreter.java +++ b/API/src/main/java/net/juligames/core/api/config/mapbacked/DictionaryFeedInterpreter.java @@ -19,53 +19,109 @@ public class DictionaryFeedInterpreter implements Interpreter> { private final @NotNull Supplier> dictionarySupplier; private final @NotNull BiFunction>, ? extends MapPart> dictionaryPartFactory; - + /** + * Constructs a new {@link DictionaryFeedInterpreter} instance with the given dictionary supplier. + * + * @param dictionarySupplier the supplier to use to provide the backing dictionary + */ public DictionaryFeedInterpreter(@NotNull Supplier> dictionarySupplier) { this.dictionarySupplier = dictionarySupplier; this.dictionaryPartFactory = DictionaryPart::new; } + /** + * Constructs a new {@link DictionaryFeedInterpreter} instance with the given dictionary supplier and map part factory. + * + * @param dictionarySupplier the supplier to use to provide the backing dictionary + * @param dictionaryPartFactory the function to use to create map parts based on the dictionary and the key + */ public DictionaryFeedInterpreter(@NotNull Supplier> dictionarySupplier, @NotNull BiFunction>, MapPart> dictionaryPartFactory) { this.dictionarySupplier = dictionarySupplier; this.dictionaryPartFactory = dictionaryPartFactory; } - + /** + * Interprets the given input string as a map part key and returns a corresponding map part. + * + * @param input the input string to interpret as a key + * @return the corresponding map part + */ @Override public @NotNull MapPart interpret(@NotNull String input) { return fabricate(input); } + /** + * Returns the factory function used to create map parts based on the dictionary and the key. + * + * @return the factory function used to create map parts + */ public BiFunction>, ? extends MapPart> getDictionaryPartFactory() { return dictionaryPartFactory; } + /** + * Creates a new map part with the given key and dictionary supplier using the factory function. + * + * @param key the key of the new map part + * @param dictionarySupplier the dictionary supplier to use for the new map part + * @return the newly created map part + */ public @NotNull MapPart fabricate(@NotNull String key, @NotNull Supplier> dictionarySupplier) { return dictionaryPartFactory.apply(key, dictionarySupplier); } + /** + * Creates a new {@code MapPart} instance with the given key and using the current dictionary supplier and factory. + * + * @param key the key for the new {@code MapPart} instance + * @return a new {@code MapPart} instance + */ protected @NotNull MapPart fabricate(@NotNull String key) { return dictionaryPartFactory.apply(key, dictionarySupplier); } + /** + * Returns the key for the given {@code MapPart} instance. + * + * @param eMapPart the {@code MapPart} instance to get the key for + * @return the key for the given {@code MapPart} instance + */ @Override public @NotNull String reverse(@NotNull MapPart eMapPart) { return eMapPart.key(); } - - private record DictionaryPart(String key, - Supplier> dictionarySupplier) implements MapPart { - + /** + * A private record that represents a map part backed by a dictionary. + * + * @param the type of values stored in the dictionary + */ + private record DictionaryPart( + String key, + Supplier> dictionarySupplier + ) implements MapPart { + + /** + * Returns the key for this map part. + * + * @return the key for this map part + */ @Override public @NotNull String key() { return key; } + /** + * Returns the value for this map part by looking up the key in the backing dictionary. + * + * @return the value for this map part + */ @Override public @NotNull E get() { return dictionarySupplier.get().get(key()); } } + } diff --git a/API/src/main/java/net/juligames/core/api/config/mapbacked/MapFeedInterpreter.java b/API/src/main/java/net/juligames/core/api/config/mapbacked/MapFeedInterpreter.java index 71851f69..93a41305 100644 --- a/API/src/main/java/net/juligames/core/api/config/mapbacked/MapFeedInterpreter.java +++ b/API/src/main/java/net/juligames/core/api/config/mapbacked/MapFeedInterpreter.java @@ -49,11 +49,11 @@ public MapFeedInterpreter(@NotNull Supplier> mapSupplier, } public @NotNull MapPart fabricate(@NotNull String key, @NotNull Supplier> mapSupplier) { - return mapPartFactory.apply(key,mapSupplier); + return mapPartFactory.apply(key, mapSupplier); } protected @NotNull MapPart fabricate(@NotNull String key) { - return mapPartFactory.apply(key,mapSupplier); + return mapPartFactory.apply(key, mapSupplier); } private record MapPartImpl(String key, Supplier> mapSupplier) implements MapPart { diff --git a/API/src/main/java/net/juligames/core/api/config/property/PropertyInterpreter.java b/API/src/main/java/net/juligames/core/api/config/property/PropertyInterpreter.java index ba267a2d..d76f9f2f 100644 --- a/API/src/main/java/net/juligames/core/api/config/property/PropertyInterpreter.java +++ b/API/src/main/java/net/juligames/core/api/config/property/PropertyInterpreter.java @@ -11,6 +11,27 @@ * The {@link PropertyInterpreter} allows to store a reference to a property that is set. The value that is read will * be used as the "key" for getting the property. If you {@link #reverse(Object)} then {@link String#valueOf(Object)} * will be used + *

+ * This is an example on how to use the {@link PropertyInterpreter} specifically the {@link PropertyInterpreter#stringPropertyInterpreter()} + *

+ *
+ *     {@code
+ *         // Create a PropertyInterpreter for String properties
+ *         PropertyInterpreter stringPropertyInterpreter
+ *              = PropertyInterpreter.stringPropertyInterpreter();
+ *
+ *         // Get the value of a String property
+ *         String propertyValue = stringPropertyInterpreter.interpret("my.property");
+ *         System.out.println("Property value: " + propertyValue);
+ *
+ *         // Reverse a String value to a property
+ *         String reversedValue = "Hello, World!";
+ *         String propertyKey = stringPropertyInterpreter.reverse(reversedValue);
+ *         System.out.println("Property key: " + propertyKey);
+ *
+ *
+ *     }
+ * 
* * @author Ture Bentzin * 07.03.2023 diff --git a/API/src/main/java/net/juligames/core/api/config/representations/Interpretation.java b/API/src/main/java/net/juligames/core/api/config/representations/Interpretation.java new file mode 100644 index 00000000..5a84a7f7 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/config/representations/Interpretation.java @@ -0,0 +1,30 @@ +package net.juligames.core.api.config.representations; + +import net.juligames.core.api.config.PrimitiveInterpreter; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 17.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class Interpretation implements Representation { + + public final @NotNull PrimitiveInterpreter interpreter; + + public Interpretation(@NotNull PrimitiveInterpreter interpreter) { + this.interpreter = interpreter; + } + + @Override + public final @NotNull T represent() { + try { + return interpreter.interpret(getRepresentation()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public abstract String getRepresentation(); +} diff --git a/API/src/main/java/net/juligames/core/api/config/representations/Representation.java b/API/src/main/java/net/juligames/core/api/config/representations/Representation.java new file mode 100644 index 00000000..270f7a16 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/config/representations/Representation.java @@ -0,0 +1,48 @@ +package net.juligames.core.api.config.representations; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Supplier; + +/** + * The `Representation` interface defines a representation of a value of type `T`. + * + * @param The type of the value to represent. + * @author Ture Bentzin + * 17.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +@FunctionalInterface +public interface Representation { + + /** + * Creates a representation of the provided value. + * + * @param t The value to represent. + * @param The type of the value. + * @return The representation of the value. + */ + static @NotNull Representation of(T t) { + return () -> t; + } + + /** + * Creates a representation based on the provided supplier. + * + * @param supplier The supplier that provides the value. + * @param The type of the value. + * @return The representation based on the supplier. + */ + static @NotNull Representation of(@NotNull Supplier supplier) { + return supplier::get; + } + + /** + * Retrieves the represented value. + * + * @return The represented value. + */ + T represent(); +} + diff --git a/API/src/main/java/net/juligames/core/api/data/HazelDataApi.java b/API/src/main/java/net/juligames/core/api/data/HazelDataApi.java index a75ff20f..130b5d1a 100644 --- a/API/src/main/java/net/juligames/core/api/data/HazelDataApi.java +++ b/API/src/main/java/net/juligames/core/api/data/HazelDataApi.java @@ -1,6 +1,7 @@ package net.juligames.core.api.data; import de.bentzin.tools.Hardcode; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -55,5 +56,12 @@ default Map getMasterInformation() { return getMap("master_information"); } + /** + * This method trys to guess if {@link #getMasterInformation()} will provide a populated map. + * It provides no information about the last update + */ + @ApiStatus.AvailableSince("1.6") + boolean isMasterInformationAvailable(); + } diff --git a/API/src/main/java/net/juligames/core/api/external/AdventureWebuiEditorAPI.java b/API/src/main/java/net/juligames/core/api/external/AdventureWebuiEditorAPI.java new file mode 100644 index 00000000..084a32f6 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/external/AdventureWebuiEditorAPI.java @@ -0,0 +1,239 @@ +package net.juligames.core.api.external; +/* + * This file is part of adventure-webui, licensed under the MIT License. + * + * Copyright (c) 2021 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import net.juligames.core.api.API; +import net.juligames.core.api.message.Message; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.TestOnly; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * The adventure-webui editor API. + */ +public final class AdventureWebuiEditorAPI { + public static final URI JULIGAMES_API_PRODUCTION; + private static final Pattern TOKEN_PATTERN = Pattern.compile("\\{\"token\" *: *\"(.*?)\""); + @TestOnly + public static URI JULIGAMES_API_DEVELOPMENT_A; + @TestOnly + public static URI JULIGAMES_API_DEVELOPMENT_B; + + @TestOnly + public static URI JULIGAMES_API_DEVELOPMENT_LOCAL; + + static { + try { + JULIGAMES_API_PRODUCTION = new URI("https://editor.juligames.net"); + //JULIGAMES_API_DEVELOPMENT_A = new URI("censored"); + //JULIGAMES_API_DEVELOPMENT_B = new URI("censored"); + JULIGAMES_API_DEVELOPMENT_LOCAL = new URI("https://localhost"); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + private final URI root; + private final HttpClient client; + + /** + * Creates a new instance of the editor API with the given root URI. + * + * @param root the root URI + */ + public AdventureWebuiEditorAPI(final @NotNull URI root) { + this(root, HttpClient.newHttpClient()); + } + + /** + * Creates a new instance of the editor API with the default JuliGames api + */ + public AdventureWebuiEditorAPI() { + this(JULIGAMES_API_PRODUCTION, HttpClient.newHttpClient()); + } + + /** + * Creates a new instance of the editor API with the given root URI and a client. + * + * @param root the root URI + * @param client the client + */ + public AdventureWebuiEditorAPI(final @NotNull URI root, final @NotNull HttpClient client) { + this.root = Objects.requireNonNull(root, "root"); + this.client = Objects.requireNonNull(client, "client"); + } + + /** + * Creates a new instance of the editor API with the default JuliGames api and a default client. + * + * @param client the client + */ + public AdventureWebuiEditorAPI(final @NotNull HttpClient client) { + this.root = Objects.requireNonNull(JULIGAMES_API_PRODUCTION, "root"); + this.client = Objects.requireNonNull(client, "client"); + } + + /** + * Starts a session, returning the token. + * + * @param input the input + * @param command the command + * @param application the application name + * @return a completable future that will provide the token + */ + public @NotNull CompletableFuture startSession(final @NotNull String input, final @NotNull String command, final @NotNull String application) { + final HttpRequest request = HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(constructBody(input, command, application))).uri(root.resolve(URI.create("/api/editor/input"))).build(); + final CompletableFuture result = new CompletableFuture<>(); + + this.client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(stringHttpResponse -> { + if (stringHttpResponse.statusCode() != 200) { + result.completeExceptionally(new IOException("The server could not handle the request.")); + } else { + final String body = stringHttpResponse.body(); + final Matcher matcher = TOKEN_PATTERN.matcher(body); + + if (matcher.find()) { + final String group = matcher.group(1); + result.complete(group); + return result; + } + + result.completeExceptionally(new IOException("The result did not contain a token.")); + } + return null; + }); + + return result; + } + + /** + * Retrieves the result of a session, given a token. + * + * @param token the token + * @return the resulting MiniMessage string in a completable future + */ + public @NotNull CompletableFuture retrieveSession(final @NotNull String token) { + final HttpRequest request = HttpRequest.newBuilder().GET().uri(root.resolve(URI.create("/api/editor/output?token=" + token))).build(); + final CompletableFuture result = new CompletableFuture<>(); + + this.client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(stringHttpResponse -> { + final int statusCode = stringHttpResponse.statusCode(); + if (statusCode == 404) { + result.complete(null); + } else if (statusCode != 200) { + result.completeExceptionally(new IOException("The server could not handle the request.")); + } else { + result.complete(stringHttpResponse.body()); + } + return null; + }); + + return result; + } + + @Contract(pure = true) + public @NotNull String generateLink(String token) { + return root.toString() + "?token=" + token; + } + + /** + * @param input initial miniMessage + * @param command command with {token} + * @param app app name + * @return the link + */ + @Contract(pure = true) + public @NotNull String startSessionAndGenerateLink(String input, String command, String app) { + try { + return generateLink(startSession(input, command, app).get(10, TimeUnit.SECONDS)); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new RuntimeException(e); + } + } + + /** + * @param command command with {token} + * @return the link + */ + @Contract(pure = true) + public @NotNull String startSessionAndGenerateLink(String command) { + try { + return generateLink(startSession("", command, API.get().getVersion()).get(10, TimeUnit.SECONDS)); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new RuntimeException(e); + } + } + + /** + * @param command command with {token} + * @param app app + * @return the link + */ + @Contract(pure = true) + public @NotNull String startSessionAndGenerateLink(String command, String app) { + try { + return generateLink(startSession("", command, app).get(10, TimeUnit.SECONDS)); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new RuntimeException(e); + } + } + + /** + * @param message the initial message + * @param command command with {token} + * @param app app + * @return the link + */ + @Contract(pure = true) + public @NotNull String startSessionAndGenerateLink(@NotNull Message message, String command, String app) { + try { + return generateLink(startSession(message.getMiniMessage(), command, app).get(10, TimeUnit.SECONDS)); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + throw new RuntimeException(e); + } + } + + private @NotNull String constructBody(final @NotNull String input, final @NotNull String command, final @NotNull String application) { + return String.format("{\"input\":\"%s\",\"command\":\"%s\",\"application\":\"%s\"}", input, command, application); + } + + @Contract(pure = true) + public @NotNull String getClientInformation() { + return client + "@" + root; + } +} \ No newline at end of file diff --git a/API/src/main/java/net/juligames/core/api/external/tokenfix.diff b/API/src/main/java/net/juligames/core/api/external/tokenfix.diff new file mode 100644 index 00000000..f10ad452 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/external/tokenfix.diff @@ -0,0 +1,12 @@ +41c41 +< private static final Pattern TOKEN_PATTERN = Pattern.compile("\"(.*?)\""); +--- +> private static final Pattern TOKEN_PATTERN = Pattern.compile("\\{\"token\": \"(.*?)\"\\}"); +89,92c89,90 +< final String group = matcher.group(0); +< if (group.equalsIgnoreCase("token")) { +< result.complete(group); +< } +--- +> final String group = matcher.group(1); +> result.complete(group); \ No newline at end of file diff --git a/API/src/main/java/net/juligames/core/api/message/LegacyMessageType.java b/API/src/main/java/net/juligames/core/api/message/LegacyMessageType.java index 70136dd5..82530833 100644 --- a/API/src/main/java/net/juligames/core/api/message/LegacyMessageType.java +++ b/API/src/main/java/net/juligames/core/api/message/LegacyMessageType.java @@ -5,22 +5,60 @@ import org.jetbrains.annotations.NotNull; /** - * @author Ture Bentzin - * 25.02.2023 + * The {@code LegacyMessageType} interface represents the types of formatting codes used in legacy Minecraft chat messages. + * These codes are used to change the color, formatting, and other properties of text in the chat. + * + *

The {@code LegacyMessageType} interface provides two default implementations for commonly used formatting codes: + * {@link #AMPERSAND} and {@link #SECTION}. Additionally, custom formatting codes can be created using the static + * {@link #custom(char)} method. + * + *

Note that these formatting codes are only supported in legacy Minecraft versions. Modern Minecraft versions use + * JSON-formatted chat messages instead. You are still able to use them through Spigot but it is recommended to use + * Components instead + * + * @since 1.4 */ @ApiStatus.AvailableSince("1.4") public interface LegacyMessageType { + + /** + * A {@code LegacyMessageType} implementation for the '&' formatting code. + */ @NotNull LegacyMessageType AMPERSAND = () -> '&'; + + /** + * A {@code LegacyMessageType} implementation for the '§' formatting code. + */ @NotNull LegacyMessageType SECTION = () -> '§'; + /** + * Creates a custom {@code LegacyMessageType} implementation for the specified character. + * + * @param c the formatting code character + * @return a new {@code CustomLegacyMessageType} instance for the specified character + */ @Contract(pure = true) static @NotNull CustomLegacyMessageType custom(char c) { return new CustomLegacyMessageType(c); } + /** + * Gets the formatting code character for this {@code LegacyMessageType}. + * + * @return the formatting code character + */ char getChar(); + /** + * A {@code record} implementation of {@code LegacyMessageType} for custom formatting codes. + */ record CustomLegacyMessageType(char c) implements LegacyMessageType { + + /** + * Gets the formatting code character for this {@code CustomLegacyMessageType}. + * + * @return the formatting code character + */ @Override public char getChar() { return c; diff --git a/API/src/main/java/net/juligames/core/api/message/MessageApi.java b/API/src/main/java/net/juligames/core/api/message/MessageApi.java index 89ddb624..b0583364 100644 --- a/API/src/main/java/net/juligames/core/api/message/MessageApi.java +++ b/API/src/main/java/net/juligames/core/api/message/MessageApi.java @@ -10,27 +10,58 @@ import java.util.Locale; import java.util.stream.Stream; +/* +The MessageApi interface is a high-level API for sending messages to various recipients. +It defines methods for sending single or multiple messages, with or without message key replacement, to either a single recipient or a collection of recipients. + +The interface defines two types of message sending methods: sendMessage and broadcastMessage. +The sendMessage method is used to send a message to a single recipient, while the broadcastMessage method is used to send a message to multiple recipients. +Both methods have several overloaded versions to accommodate different scenarios, such as sending messages with or without message key replacements, specifying the default or override locales, and so on. + +The MessageApi interface also provides two methods to retrieve the default locale, defaultLocale and defaultUtilLocale. +These methods return the default locale as a string and a java.util.Locale object, respectively. + +Overall, the MessageApi interface is a powerful and flexible API for sending messages, suitable for a wide range of use cases. + */ + /** + * Provides an api for accessing and managing messages and message + * replacements. + * * @author Ture Bentzin * 18.11.2022 - * @apiNote Please consider that the core your using may not support all the operations below. If a core should not - * support one of the methods there are two options what could happen. First the core could automatically execute a similar - * Method and print a warning. If this should not be possible then the Core will throw an {@link UnsupportedOperationException} with further - * information on how to approach this issue. + * @apiNote Please consider that the core your using may not support all the + * operations below. + * If a core should not support one of the methods, there are two options for what could happen. + * First, the core could + * automatically execute a similar Method and print a warning. + * If this + * should not be possible, then the Core will throw an + * {@link UnsupportedOperationException} with further information on + * how to approach this issue. */ @SuppressWarnings("unused") public interface MessageApi { + /** + * Returns an array from a varargs input. + * + * @param ts the input arguments + * @param the type of the array elements + * @return the array + */ @SafeVarargs @ApiStatus.AvailableSince("1.5") static T[] arrFromVargs(T... ts) { return ts; } - /** - * This is an alias for {@link #arrFromVargs(Object[])} + * Alias for {@link #arrFromVargs(Object[])}. * + * @param ts the input arguments + * @param the type of the array elements + * @return the array * @apiNote You should import this method static! */ @SafeVarargs @@ -39,217 +70,902 @@ static T[] repl(T... ts) { return arrFromVargs(ts); } - //DAO + /** + * Calls a message DAO extension. + * + * @param extensionCallback the callback to call + * @param the return type of the callback + * @return the callback result + * @apiNote Internal use only. + */ @ApiStatus.Internal @ApiStatus.Experimental - - @Nullable R callMessageExtension(@NotNull ExtensionCallback extensionCallback); + @Nullable R callMessageExtension(@NotNull ExtensionCallback extensionCallback); + /** + * Calls a locale DAO extension. + * + * @param extensionCallback the callback to call + * @param the return type of the callback + * @return the callback result + * @apiNote Internal use only. + * @since experimental + */ @ApiStatus.Internal @ApiStatus.Experimental - - @Nullable R callLocaleExtension(@NotNull ExtensionCallback extensionCallback); + @Nullable R callLocaleExtension(@NotNull ExtensionCallback extensionCallback); + /** + * Calls an extension callback with a ReplacementDAO instance as the second argument. + * + * @param extensionCallback the extension callback to call + * @param the type of the return value + * @return the result of the extension callback, or null if the callback returns null + * @throws RuntimeException if the extension callback throws an exception + */ @ApiStatus.Internal @ApiStatus.Experimental - - @Nullable R callReplacementExtension(@NotNull ExtensionCallback extensionCallback); + @Nullable R callReplacementExtension(@NotNull ExtensionCallback extensionCallback); + /** + * Calls an extension callback with a ReplacementTypeDAO instance as the second argument. + * + * @param extensionCallback the extension callback to call + * @param the type of the return value + * @return the result of the extension callback, or null if the callback returns null + * @throws RuntimeException if the extension callback throws an exception + */ @ApiStatus.Internal @ApiStatus.Experimental - - @Nullable R callReplacementTypeExtension(@NotNull ExtensionCallback extensionCallback); + @Nullable R callReplacementTypeExtension(@NotNull ExtensionCallback extensionCallback); + //get + + /** + * Returns the {@link Message} with the specified message key and locale. If there is no {@link Message} with the + * specified key and locale, a FallbackMessage is returned instead. + * + * @param messageKey the key of the message to retrieve. + * @param locale the locale of the message to retrieve. + * @return the {@link Message} with the specified key and locale, or a FallbackMessage if no such message exists. + */ @NotNull Message getMessage(@NotNull String messageKey, @NotNull Locale locale); + /** + * Returns the {@link Message} with the specified message key and locale, with placeholders replaced by the specified + * replacement strings. If there is no {@link Message} with the specified key and locale, a FallbackMessage is returned + * instead. + * + * @param messageKey the key of the message to retrieve. + * @param locale the locale of the message to retrieve. + * @param replacements the replacement strings to substitute into the message. + * @return the {@link Message} with the specified key and locale, or a FallbackMessage if no such message exists. + */ @NotNull Message getMessage(@NotNull String messageKey, @NotNull Locale locale, String... replacements); + /** + * Returns the {@link Message} with the specified message key and locale string. If there is no {@link Message} with the + * specified key and locale, a FallbackMessage is returned instead. + * + * @param messageKey the key of the message to retrieve. + * @param locale the locale string of the message to retrieve. + * @return the {@link Message} with the specified key and locale, or a FallbackMessage if no such message exists. + */ @NotNull Message getMessage(@NotNull String messageKey, @NotNull String locale); + /** + * Returns the {@link Message} with the specified message key and locale string, with placeholders replaced by the specified + * replacement strings. If there is no {@link Message} with the specified key and locale, a FallbackMessage is returned + * instead. + * + * @param messageKey the key of the message to retrieve. + * @param locale the locale string of the message to retrieve. + * @param replacements the replacement strings to substitute into the message. + * @return the {@link Message} with the specified key and locale, or a FallbackMessage if no such message exists. + */ @NotNull Message getMessage(@NotNull String messageKey, @NotNull String locale, String... replacements); + + /** + * Returns the {@link Message} with the specified message key and locale string. If there is no {@link Message} with the + * specified key and locale, a FallbackMessage is returned instead. + * + * @param messageKey the key of the message to retrieve. + * @param dbLocale the locale string of the message to retrieve. + * @return the {@link Message} with the specified key and locale, or a FallbackMessage if no such message exists. + */ @NotNull Message getMessage(@NotNull String messageKey, @NotNull DBLocale dbLocale); + /** + * Returns the {@link Message} with the specified message key and locale string, with placeholders replaced by the specified + * replacement strings. If there is no {@link Message} with the specified key and locale, a FallbackMessage is returned + * instead. + * + * @param messageKey the key of the message to retrieve. + * @param dbLocale the locale string of the message to retrieve. + * @param replacements the replacement strings to substitute into the message. + * @return the {@link Message} with the specified key and locale, or a FallbackMessage if no such message exists. + */ @NotNull Message getMessage(@NotNull String messageKey, @NotNull DBLocale dbLocale, String... replacements); + /** + * Returns a collection of Messages from the MessageSystem that match the specified message key. + * + * @param messageKey the message key used to retrieve the messages + * @return a collection of messages that match the specified message key + */ @NotNull Collection getMessage(@NotNull String messageKey); + /** + * Returns a collection of Messages from the MessageSystem that match the specified message key with the + * specified replacements applied to each message. + * + * @param messageKey the message key used to retrieve the messages + * @param replacements the array of replacements to apply to the messages + * @return a collection of messages that match the specified message key with the replacements applied + */ @NotNull Collection getMessage(@NotNull String messageKey, String... replacements); + + /** + * Retrieves the message from the MessageSystem using the specified key and locale. The locale is automatically selected + * based on the provided locale parameter. + * + * @param messageKey The key to retrieve the message. + * @param locale The locale to use for the message. Can be null. + * @return The message. + */ @NotNull Message getMessageSmart(@NotNull String messageKey, @Nullable Locale locale); + /** + * Retrieves the message from the MessageSystem using the specified key, locale, and replacements. The locale is + * automatically selected based on the provided locale parameter. + * + * @param messageKey The key to retrieve the message. + * @param locale The locale to use for the message. Can be null. + * @param replacements The values to replace the placeholders in the message. + * @return The message. + */ @NotNull Message getMessageSmart(@NotNull String messageKey, @Nullable Locale locale, String... replacements); + /** + * Retrieves the message from the MessageSystem using the specified key and locale. The locale is automatically selected + * based on the provided locale parameter. + * + * @param messageKey The key to retrieve the message. + * @param locale The locale to use for the message. + * @return The message. + */ @NotNull Message getMessageSmart(@NotNull String messageKey, @NotNull String locale); + /** + * Retrieves the message from the MessageSystem using the specified key, locale, and replacements. The locale is + * automatically selected based on the provided locale parameter. + * + * @param messageKey The key to retrieve the message. + * @param locale The locale to use for the message. + * @param replacements The values to replace the placeholders in the message. + * @return The message. + */ @NotNull Message getMessageSmart(@NotNull String messageKey, @NotNull String locale, String... replacements); + /** + * Retrieves the message from the MessageSystem using the specified key and locale. The locale is automatically selected + * based on the provided locale parameter. + * + * @param messageKey The key to retrieve the message. + * @param dbLocale The DBLocale to use for the message. + * @return The message. + */ @NotNull Message getMessageSmart(@NotNull String messageKey, @NotNull DBLocale dbLocale); + /** + * Retrieves the message from the MessageSystem using the specified key, locale, and replacements. The locale is + * automatically selected based on the provided locale parameter. + * + * @param messageKey The key to retrieve the message. + * @param dbLocale The DBLocale to use for the message. + * @param replacements The values to replace the placeholders in the message. + * @return The message. + */ @NotNull Message getMessageSmart(@NotNull String messageKey, @NotNull DBLocale dbLocale, String... replacements); + + /** + * Returns all messages for the specified {@link Locale}. + * + * @param locale the {@code Locale} used to filter the messages + * @return a collection of all messages for the specified {@code Locale}, or an empty collection if none were found + */ @NotNull Collection getAllFromLocale(@NotNull Locale locale); + /** + * Returns all messages for the specified {@link Locale} with replacements applied. + * + * @param locale the {@code Locale} used to filter the messages + * @param replacements an optional list of replacements to apply to the messages + * @return a collection of all messages for the specified {@code Locale}, with replacements applied, or an empty collection if none were found + */ @NotNull Collection getAllFromLocale(@NotNull Locale locale, String... replacements); + /** + * Returns all messages for the specified locale code. + * + * @param locale the locale code used to filter the messages + * @return a collection of all messages for the specified locale code, or an empty collection if none were found + */ @NotNull Collection getAllFromLocale(@NotNull String locale); + /** + * Returns all messages for the specified locale code with replacements applied. + * + * @param locale the locale code used to filter the messages + * @param replacements an optional list of replacements to apply to the messages + * @return a collection of all messages for the specified locale code, with replacements applied, or an empty collection if none were found + */ @NotNull Collection getAllFromLocale(@NotNull String locale, String... replacements); + /** + * Returns all messages for the specified database locale. + * + * @param dbLocale the database locale used to filter the messages + * @return a collection of all messages for the specified database locale, or an empty collection if none were found + */ @NotNull Collection getAllFromLocale(@NotNull DBLocale dbLocale); + /** + * Returns all messages for the specified database locale with replacements applied. + * + * @param dbLocale the database locale used to filter the messages + * @param replacements an optional list of replacements to apply to the messages + * @return a collection of all messages for the specified database locale, with replacements applied, or an empty collection if none were found + */ @NotNull Collection getAllFromLocale(@NotNull DBLocale dbLocale, String... replacements); + + /** + * Returns all messages in the message system. + * This method is experimental and may be removed or changed in a future release. + * + * @return A collection of all messages in the message system. + */ @ApiStatus.Experimental - @NotNull - Collection getAll(); + @NotNull Collection getAll(); + /** + * Returns all messages in the message system with the given replacements. + * This method is experimental and may be removed or changed in a future release. + * + * @param replacements The replacements for the messages. + * @return A collection of all messages in the message system with the given replacements. + */ @ApiStatus.Experimental - @NotNull - Collection getAll(String... replacements); + @NotNull Collection getAll(String... replacements); + /** + * Returns a stream of all the database messages in the message system. + * This method is internal and should not be used outside of the message system implementation. + * + * @return A stream of all the database messages in the message system. + */ @ApiStatus.Internal - @NotNull - Stream streamData(); + @NotNull Stream streamData(); + + /** + * Returns a collection of all the replacers in the message system. + * + * @return a collection of all the replacers + */ @NotNull Collection getReplacers(); + //register + /** + * Registers a message with the specified message key. + * + * @param messageKey the message key to register + * @deprecated This method is deprecated and should be avoided. Instead, use the {@link #registerMessage(String, String)} + * method to register a message with a defaultMiniMessage. + */ @Deprecated void registerMessage(@NotNull String messageKey); + /** + * Registers a message with the specified message key and default mini message. + * + * @param messageKey the message key to register + * @param defaultMiniMessage the default mini message for the message (EN_US) + */ void registerMessage(@NotNull String messageKey, @NotNull String defaultMiniMessage); /** - * If you want to register a legacyMessage please use the provided method in the AdventureAPI + * Registers a third-party message with the specified message key, third-party message, and custom message dealer. + * If you want to register a legacy message, please use the provided method in the AdventureAPI. * - * @param messageKey the ley - * @param thirdPartyMessage the input - * @param dealer the dealer + * @param messageKey the message key to register + * @param thirdPartyMessage the third-party message to register (EN_US) + * @param dealer the custom message dealer to convert the third-party message to a message that can be handled + * by the message system + * @since 1.4 */ @ApiStatus.AvailableSince("1.4") void registerThirdPartyMessage(@NotNull String messageKey, @NotNull String thirdPartyMessage, @NotNull CustomMessageDealer dealer); + + /** + * Checks if a message exists in EN_US for the given message key in the message system. + * + * @param messageKey the key of the message to check + * @return {@code true} if a message exists for the given key, otherwise {@code false} + */ boolean hasMessage(@NotNull String messageKey); + /** + * Checks if a message exists for the given message key and locale in the message system. + * + * @param messageKey the key of the message to check + * @param locale the locale of the message to check + * @return {@code true} if a message exists for the given key and locale, otherwise {@code false} + */ boolean hasMessage(@NotNull String messageKey, @NotNull String locale); + /** + * Checks if a message exists for the given message key and locale in the message system. + * + * @param messageKey the key of the message to check + * @param locale the locale of the message to check + * @return {@code true} if a message exists for the given key and locale, otherwise {@code false} + */ boolean hasMessage(@NotNull String messageKey, @NotNull Locale locale); + /** + * Checks if a message exists for the given message key and locale in the message system. + * + * @param messageKey the key of the message to check + * @param locale the locale of the message to check + * @return {@code true} if a message exists for the given key and locale, otherwise {@code false} + */ boolean hasMessage(@NotNull String messageKey, @NotNull DBLocale locale); + //send + /** + * Sends a message to the specified recipient and returns a {@link MessagePostScript} that contains details about + * the message that was sent. The best locale for the message is determined via the {@link MessageRecipient}. + * + * @param messageKey the key of the message to send. + * @param messageRecipient the recipient of the message. + * @return a {@link MessagePostScript} that contains details about the sent message. + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient); + /** + * Sends the message with the specified message key to the given message recipient using the provided override locale. + * + * @param messageKey the message key + * @param messageRecipient the recipient of the message + * @param overrideLocale the locale to use instead of the best available locale for the recipient + * @return a MessagePostScript providing details about the sent message + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, @NotNull Locale overrideLocale); + /** + * Sends the message with the specified message key to the given message recipient using the provided override locale. + * + * @param messageKey the message key + * @param messageRecipient the recipient of the message + * @param overrideLocale the locale to use instead of the best available locale for the recipient as a string in the format of "language_country" + * @return a MessagePostScript providing details about the sent message + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, @NotNull String overrideLocale); + /** + * Sends the message with the specified message key to the given message recipient using the provided override locale. + * + * @param messageKey the message key + * @param messageRecipient the recipient of the message + * @param overrideLocale the locale to use instead of the best available locale for the recipient + * @return a MessagePostScript providing details about the sent message + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, @NotNull DBLocale overrideLocale); + /** + * Sends a message to all available recipients with the specified message key and default locale. + * + * @param messageKey the key of the message to be sent + * @param defaultLocale the default locale to be used for the message + * @return a {@link MultiMessagePostScript} describing the details of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull String messageKey, @NotNull Locale defaultLocale); + /** + * Sends a message to all available recipients with the specified message key and default locale. + * + * @param messageKey the key of the message to be sent + * @param defaultLocale the default locale to be used for the message + * @return a {@link MultiMessagePostScript} describing the details of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull String messageKey, @NotNull String defaultLocale); + /** + * Sends a message to all available recipients with the specified message key and default locale. + * + * @param messageKey the key of the message to be sent + * @param defaultLocale the default locale to be used for the message + * @return a {@link MultiMessagePostScript} describing the details of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull String messageKey, @NotNull DBLocale defaultLocale); + + /** + * Sends the message with the given {@code messageKey} to all available recipients and returns the + * {@link Collection} of {@link MessagePostScript}s containing details about each sent message. + * + * @param messageKey the key of the message to send + * @return a {@link Collection} of {@link MessagePostScript}s containing details about each sent message + */ @NotNull Collection broadcastMessage(@NotNull String messageKey); + + /** + * Sends multiple messages to the same recipient. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipient the recipient of the messages + * @return the post-script describing the details of the messages sent + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient); + /** + * Sends multiple messages to multiple recipients. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipients the collection of recipients of the messages + * @return the post-script describing the details of the messages sent + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients); + + /** + * Sends multiple messages to the same recipient with the given locale override. + * + * @param messageKeys the collection of message keys + * @param messageRecipient the message recipient + * @param overrideLocale the locale override + * @return the MultiMessagePostScript representing the sent messages + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, @NotNull String overrideLocale); + /** + * Sends multiple messages to the same recipient with the given locale override. + * + * @param messageKeys the collection of message keys + * @param messageRecipient the message recipient + * @param overrideLocale the locale override + * @return the MultiMessagePostScript representing the sent messages + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, @NotNull Locale overrideLocale); + /** + * Sends multiple messages to the same recipient with the given locale override. + * + * @param messageKeys the collection of message keys + * @param messageRecipient the message recipient + * @param overrideLocale the locale override + * @return the MultiMessagePostScript representing the sent messages + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, @NotNull DBLocale overrideLocale); - @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull String overrideLocale); + /** + * Sends a message composed of the given message keys to the specified message recipient, using the specified locale + * to override the default locale set in the recipient. + * + * @param messageKeys the collection of message keys to compose the message + * @param messageRecipients the collection of message recipients to send the message to + * @param overrideLocale the locale to use for message translation, overriding the recipient's default locale + * @return a MultiMessagePostScript containing details about the messages that were sent + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull Locale overrideLocale); + /** + * Sends a message composed of the given message keys to the specified message recipient, using the specified DBLocale + * to override the default locale set in the recipient. + * + * @param messageKeys the collection of message keys to compose the message + * @param messageRecipients the collection of message recipients to send the message to + * @param overrideLocale the DBLocale to use for message translation, overriding the recipient's default locale + * @return a MultiMessagePostScript containing details about the messages that were sent + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull DBLocale overrideLocale); + /** + * Sends a message composed of the given message keys to the specified message recipient, using the specified locale + * code (e.g. "EN_US") to override the default locale set in the recipient. + * + * @param messageKeys the collection of message keys to compose the message + * @param messageRecipients the collection of message recipients to send the message to + * @param overrideLocale the locale code to use for message translation, overriding the recipient's default locale + * @return a MultiMessagePostScript containing details about the messages that were sent + */ + @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull String overrideLocale); + + /** + * Sends the given messages to the given recipients, replacing any placeholders in the messages with the given replacements. + * The locale for each message is determined based on the locale of the recipient. + * + * @param messageKeys the keys of the messages to send + * @param messageRecipients the recipients to send the messages to + * @param replacement the replacements to use for any placeholders in the messages + * @return a collection of MultiMessagePostScript objects describing the messages that were sent + */ @NotNull Collection sendMessageSmart(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, String... replacement); + /** + * Sends the message with the given message key to the specified collection of recipients. + * + * @param messageKey the message key + * @param messageRecipients the collection of message recipients + * @return a collection of {@link MessagePostScript} objects describing the details of each message sent + */ @NotNull Collection sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients); + /** + * Sends a message with the given message key to a collection of message recipients with an overridden locale. + * + * @param messageKey The message key to use. + * @param messageRecipients The collection of message recipients to send the message to. + * @param overrideLocale The locale to use instead of the recipients' default locale. + * @return A MultiMessagePostScript containing details about the sent messages. + */ @NotNull MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, @NotNull String overrideLocale); + /** + * Sends a message with the given message key to a collection of message recipients with an overridden locale. + * + * @param messageKey The message key to use. + * @param messageRecipients The collection of message recipients to send the message to. + * @param overrideLocale The locale to use instead of the recipients' default locale. + * @return A MultiMessagePostScript containing details about the sent messages. + */ @NotNull MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, @NotNull Locale overrideLocale); + /** + * Sends a message with the given message key to a collection of message recipients with an overridden locale. + * + * @param messageKey The message key to use. + * @param messageRecipients The collection of message recipients to send the message to. + * @param overrideLocale The locale to use instead of the recipients' default locale. + * @return A MultiMessagePostScript containing details about the sent messages. + */ @NotNull MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, @NotNull DBLocale overrideLocale); + /** + * Sends a message with the given message key and replacements to the specified recipients. + * + * @param messageKey the message key for the message to be sent + * @param messageRecipients the recipients to send the message to + * @param replacements replacements to be made in the message + * @return a collection of {@code MessagePostScript} objects representing the details of the sent messages + */ @NotNull Collection sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, String... replacements); + /** + * Sends a message with the given message key, replacements, and locale override to the specified recipients. + * + * @param messageKey the message key for the message to be sent + * @param messageRecipients the recipients to send the message to + * @param overrideLocale the locale to use for the message, overriding any default locale + * @param replacements replacements to be made in the message + * @return a {@code MultiMessagePostScript} object representing the details of the sent messages + */ @NotNull MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, @NotNull String overrideLocale, String... replacements); + /** + * Sends a message with the given message key, replacements, and locale override to the specified recipients. + * + * @param messageKey the message key for the message to be sent + * @param messageRecipients the recipients to send the message to + * @param overrideLocale the locale to use for the message, overriding any default locale + * @param replacements replacements to be made in the message + * @return a {@code MultiMessagePostScript} object representing the details of the sent messages + */ @NotNull MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, @NotNull Locale overrideLocale, String... replacements); - @NotNull MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, @NotNull DBLocale overrideLocale, String... replacements); - + /** + * Sends a message with a specific message key to a collection of message recipients, + * with the option to override the default locale for message retrieval. + * + * @param messageKey the key for the message to be sent + * @param messageRecipients the collection of message recipients to receive the message + * @param overrideLocale the locale to use for message retrieval, overriding the default locale + * @param replacements the replacement values to be used in the message, if any + * @return the post-script for the message sending operation + */ + @NotNull + MultiMessagePostScript sendMessage(@NotNull String messageKey, @NotNull Collection messageRecipients, + @NotNull DBLocale overrideLocale, String... replacements); + /** + * Sends the specified message to multiple recipients with the specified default locale. + * + * @param messageKeys a collection of keys of the messages to be sent + * @param defaultLocale the default locale for the messages + * @return a {@link MultiMessagePostScript} object representing the result of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, @NotNull Locale defaultLocale); + /** + * Sends the specified message to multiple recipients with the specified default locale. + * + * @param messageKeys a collection of keys of the messages to be sent + * @param defaultLocale the default locale for the messages + * @return a {@link MultiMessagePostScript} object representing the result of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, @NotNull String defaultLocale); + /** + * Sends the specified message to multiple recipients with the specified default locale. + * + * @param messageKeys a collection of keys of the messages to be sent + * @param defaultLocale the default locale for the messages + * @return a {@link MultiMessagePostScript} object representing the result of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, @NotNull DBLocale defaultLocale); + /** + * Sends the specified message to multiple recipients with the default locale. + * + * @param messageKeys a collection of keys of the messages to be sent + * @return a {@link MultiMessagePostScript} object representing the result of the messages sent + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys); - //sending with replacements + /** + * Sends a message with replacement values to a single recipient. + * + * @param messageKey The key identifying the message to send. + * @param messageRecipient The recipient to send the message to. + * @param replacement The replacement values to use in the message. + * @return A {@code MessagePostScript} object representing the sent message. + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, String... replacement); + /** + * Sends a message with replacement values to a single recipient, overriding the locale of the message. + * + * @param messageKey The key identifying the message to send. + * @param messageRecipient The recipient to send the message to. + * @param overrideLocale The locale to use for the message, overriding the default locale. + * @param replacement The replacement values to use in the message. + * @return A {@code MessagePostScript} object representing the sent message. + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, @NotNull Locale overrideLocale, String... replacement); + /** + * Sends a message with replacement values to a single recipient, overriding the locale of the message. + * + * @param messageKey The key identifying the message to send. + * @param messageRecipient The recipient to send the message to. + * @param overrideLocale The locale to use for the message, overriding the default locale. + * @param replacement The replacement values to use in the message. + * @return A {@code MessagePostScript} object representing the sent message. + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, @NotNull String overrideLocale, String... replacement); + /** + * Sends a message with replacement values to a single recipient, overriding the locale of the message. + * + * @param messageKey The key identifying the message to send. + * @param messageRecipient The recipient to send the message to. + * @param overrideLocale The locale to use for the message, overriding the default locale. + * @param replacement The replacement values to use in the message. + * @return A {@code MessagePostScript} object representing the sent message. + */ @NotNull MessagePostScript sendMessage(@NotNull String messageKey, @NotNull MessageRecipient messageRecipient, @NotNull DBLocale overrideLocale, String... replacement); + + /** + * Broadcasts a message with the given key to all message recipients, using the given default locale and replacements. + * + * @param messageKey the key of the message to be broadcasted + * @param defaultLocale the default locale to use for the broadcast + * @param replacement optional replacements for placeholders in the message + * @return a multi-message post script containing the status of each sent message + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull String messageKey, @NotNull Locale defaultLocale, String... replacement); + /** + * Broadcasts a message with the given key to all message recipients, using the given default locale and replacements. + * + * @param messageKey the key of the message to be broadcasted + * @param defaultLocale the default locale to use for the broadcast + * @param replacement optional replacements for placeholders in the message + * @return a multi-message post script containing the status of each sent message + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull String messageKey, @NotNull String defaultLocale, String... replacement); + /** + * Broadcasts a message with the given key to all message recipients, using the given default locale and replacements. + * + * @param messageKey the key of the message to be broadcasted + * @param defaultLocale the default locale to use for the broadcast + * @param replacement optional replacements for placeholders in the message + * @return a multi-message post script containing the status of each sent message + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull String messageKey, @NotNull DBLocale defaultLocale, String... replacement); + + /** + * Sends a message with replacements to all recipients, using the default locale. + * + * @param messageKey the key for the message to send + * @param replacement the replacements for placeholders in the message + * @return a collection of message post-scripts for each recipient + */ @NotNull Collection broadcastMessage(@NotNull String messageKey, String... replacement); + + /** + * Sends a collection of messages to a single message recipient with optional replacements. + * + * @param messageKeys the collection of message keys to be sent + * @param messageRecipient the message recipient to send the messages to + * @param replacement optional replacement strings to be used in the message templates + * @return the postscript representing the sending of the messages + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, String... replacement); /** - * @deprecated use {@link MessageApi#sendMessageSmart(Collection, Collection, String...)} instead + * @param messageKeys The keys of the messages to send. + * @param messageRecipients The recipients of the messages. + * @param replacement The replacements for message placeholders. + * @return A {@link MultiMessagePostScript} object containing the result of the message sending operation. + * @deprecated Use {@link MessageApi#sendMessageSmart(Collection, Collection, String...)} instead. */ @Deprecated - @NotNull - MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, String... replacement); + @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, String... replacement); + /** + * Sends a message to a single recipient with the specified locale override and replacement values. + * + * @param messageKeys The keys of the messages to send. + * @param messageRecipient The recipient of the message. + * @param overrideLocale The locale to use instead of the recipient's locale. + * @param replacement The replacements for message placeholders. + * @return A {@link MultiMessagePostScript} object containing the result of the message sending operation. + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, @NotNull String overrideLocale, String... replacement); + /** + * Sends a message to the given message recipient(s) with the specified message keys and replacements. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipient the message recipient to send the messages to + * @param overrideLocale the locale to use when sending the message(s) + * @param replacement the array of message replacement values to use when sending the message(s) + * @return the message post-script + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, @NotNull Locale overrideLocale, String... replacement); + /** + * Sends a message to the given message recipient(s) with the specified message keys and replacements. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipient the message recipient to send the messages to + * @param overrideLocale the DB locale to use when sending the message(s) + * @param replacement the array of message replacement values to use when sending the message(s) + * @return the message post-script + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull MessageRecipient messageRecipient, @NotNull DBLocale overrideLocale, String... replacement); + /** + * Sends a message to the given message recipient(s) with the specified message keys and replacements. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipients the collection of message recipients to send the messages to + * @param overrideLocale the locale to use when sending the message(s) + * @param replacement the array of message replacement values to use when sending the message(s) + * @return the collection of message post-scripts + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull String overrideLocale, String... replacement); + /** + * Sends a collection of messages with replacements to a collection of message recipients, with the specified locale override. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipients the collection of message recipients to send the messages to + * @param overrideLocale the locale override to use for the messages + * @param replacement the array of replacements to use for each message + * @return a {@link MultiMessagePostScript} representing the post-script of the messages sent + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull Locale overrideLocale, String... replacement); + /** + * Sends a collection of messages with replacements to a collection of message recipients, with the specified DB locale override. + * + * @param messageKeys the collection of message keys to send + * @param messageRecipients the collection of message recipients to send the messages to + * @param overrideLocale the DB locale override to use for the messages + * @param replacement the array of replacements to use for each message + * @return a {@link MultiMessagePostScript} representing the post-script of the messages sent + */ @NotNull MultiMessagePostScript sendMessage(@NotNull Collection messageKeys, @NotNull Collection messageRecipients, @NotNull DBLocale overrideLocale, String... replacement); + /** + * Sends a message broadcast to all recipients with the given message keys and replacement values. + * + * @param messageKeys the message keys to send. + * @param defaultLocale the default locale to use if a specific locale is not specified. + * @param replacement the replacement values to use in the message. + * @return a {@code MultiMessagePostScript} object representing the result of the operation. + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, @NotNull Locale defaultLocale, String... replacement); + /** + * Sends a message broadcast to all recipients with the given message keys and replacement values. + * + * @param messageKeys the message keys to send. + * @param defaultLocale the default locale to use if a specific locale is not specified. + * @param replacement the replacement values to use in the message. + * @return a {@code MultiMessagePostScript} object representing the result of the operation. + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, @NotNull String defaultLocale, String... replacement); + /** + * Sends a message broadcast to all recipients with the given message keys and replacement values. + * + * @param messageKeys the message keys to send. + * @param defaultLocale the default locale to use if a specific locale is not specified. + * @param replacement the replacement values to use in the message. + * @return a {@code MultiMessagePostScript} object representing the result of the operation. + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, @NotNull DBLocale defaultLocale, String... replacement); + /** + * Sends a message broadcast to all recipients with the given message keys and replacement values. + * + * @param messageKeys the message keys to send. + * @param replacement the replacement values to use in the message. + * @return a {@code MultiMessagePostScript} object representing the result of the operation. + */ @NotNull MultiMessagePostScript broadcastMessage(@NotNull Collection messageKeys, String... replacement); + + /** + * @param messageKey the messageKey + * @param messageRecipient the recipient + * @return the best locale + * @deprecated use {@link MessageApi#findBestMessageForRecipient(String, MessageRecipient)} instead + */ + @Deprecated + @ApiStatus.AvailableSince("1.6") + String findBestForRecipient(String messageKey, @NotNull MessageRecipient messageRecipient); + + @ApiStatus.AvailableSince("1.6") + Message findBestMessageForRecipient(String messageKey, @NotNull MessageRecipient messageRecipient); + + @ApiStatus.AvailableSince("1.6") + Message findBestMessageForRecipient(String messageKey, @NotNull MessageRecipient messageRecipient, String... replacements); + + /** + * Returns the default locale (EN_US) as a String. + * + * @return the default locale as a String + */ @NotNull String defaultLocale(); + /** + * Returns the default locale (EN_US) as a Locale object. + * + * @return the default locale as a Locale object + */ @NotNull Locale defaultUtilLocale(); + //TagManager getTagManager(); removed in favor of AdventureCore / AdventureAPI } diff --git a/API/src/main/java/net/juligames/core/api/message/MessagePostScript.java b/API/src/main/java/net/juligames/core/api/message/MessagePostScript.java index 6334e297..a83421ba 100644 --- a/API/src/main/java/net/juligames/core/api/message/MessagePostScript.java +++ b/API/src/main/java/net/juligames/core/api/message/MessagePostScript.java @@ -11,22 +11,32 @@ public interface MessagePostScript { /** + * Gets the message that was sent. + * * @return the message that was sent (this may be not the exact message) */ @NotNull Message message(); /** + * Gets the time the message was sent. + * * @return the time (not exact - do not use for encryption) the message sending was complete */ @NotNull Date timeSent(); /** + * Gets the recipient of the message. + * * @return the recipient of the message */ @NotNull MessageRecipient recipient(); /** - * @return transfer this {@link MessagePostScript} to a {@link MultiMessagePostScript} + * Converts this {@link MessagePostScript} to a {@link MultiMessagePostScript}. + * + * @return the {@link MultiMessagePostScript} representation of this object */ @NotNull MultiMessagePostScript toMulti(); } + + diff --git a/API/src/main/java/net/juligames/core/api/message/MessageRecipient.java b/API/src/main/java/net/juligames/core/api/message/MessageRecipient.java index 996f6066..3fafc43f 100644 --- a/API/src/main/java/net/juligames/core/api/message/MessageRecipient.java +++ b/API/src/main/java/net/juligames/core/api/message/MessageRecipient.java @@ -6,40 +6,56 @@ import org.jetbrains.annotations.Nullable; /** + * The MessageRecipient interface defines the methods necessary for an object to receive a message. + * * @author Ture Bentzin * 18.11.2022 */ public interface MessageRecipient { /** - * @return A human-readable name that defines this recipient + * Returns a human-readable name that defines this recipient. + * + * @return a human-readable name that defines this recipient */ @NotNull String getName(); /** - * Delivers the specified Message to this MessageRecipient. The message should always be human-readable! + * Delivers the specified message to this MessageRecipient. The message should always be human-readable. * * @param message the message to deliver */ void deliver(@NotNull Message message); - /** - * delivers a miniMessage string to the recipient + * Returns the locale of this MessageRecipient, or null if the locale is not specified. + * + * @return the locale of this MessageRecipient, or null if the locale is not specified */ - @ApiStatus.Internal - @Deprecated - void deliver(@NotNull String miniMessage); - - @Nullable - String supplyLocale(); + @Nullable String supplyLocale(); /** - * This will return the default locale that is distributed by the master + * Returns the default locale that is distributed by the master, or the locale of this MessageRecipient if + * specified. + * + * @return the default locale that is distributed by the master, or the locale of this MessageRecipient if + * specified */ default @Nullable String supplyLocaleOrDefault() { - if (supplyLocale() != null) return supplyLocale(); + if (supplyLocale() != null) { + return supplyLocale(); + } return API.get().getHazelDataApi().getMasterInformation().get("default_locale"); } + /** + * @param miniMessage the miniMessage string to deliver + * @deprecated Use {@link #deliver(Message)} instead. + * + *

Delivers a miniMessage string to the recipient.

+ */ + @Deprecated + @ApiStatus.Internal + void deliver(@NotNull String miniMessage); + } diff --git a/API/src/main/java/net/juligames/core/api/message/MiniMessageSerializer.java b/API/src/main/java/net/juligames/core/api/message/MiniMessageSerializer.java index 6387762c..1fa43613 100644 --- a/API/src/main/java/net/juligames/core/api/message/MiniMessageSerializer.java +++ b/API/src/main/java/net/juligames/core/api/message/MiniMessageSerializer.java @@ -3,51 +3,66 @@ import org.jetbrains.annotations.NotNull; /** + * This interface provides methods for serializing a MiniMessage to plain text or legacy format. + * It also provides methods for translating legacy format messages to MiniMessage format. + * * @author Ture Bentzin * 25.12.2022 */ public interface MiniMessageSerializer { /** - * This will resolve the given message to a plain String. This will strip all colors and decorations! + * This method resolves the given message to a plain text string, stripping all colors and decorations. * - * @param message the message - * @return plain text + * @param message the MiniMessage to resolve + * @return a plain text string representation of the MiniMessage */ @NotNull String resolvePlain(@NotNull Message message); /** - * This will resolve the given message to a "legacy format String". This will remove all advanced decorations! + * This method resolves the given message to a legacy format string, removing all advanced decorations. * - * @param message the message - * @return legacy message + * @param message the MiniMessage to resolve + * @return a legacy format string representation of the MiniMessage */ @Deprecated @NotNull String resolveLegacy(@NotNull Message message); /** - * This will resolve the given message to a plain String. This will strip all colors and decorations! + * This method resolves the given miniMessage to a plain text string, stripping all colors and decorations. * - * @param miniMessage the message - * @return plain text + * @param miniMessage the miniMessage to resolve + * @return a plain text string representation of the miniMessage */ @Deprecated @NotNull String resolvePlain(@NotNull String miniMessage); /** - * This will resolve the given message to a "legacy format String". This will remove all advanced decorations! + * This method resolves the given miniMessage to a legacy format string, removing all advanced decorations. * - * @param miniMessage the message - * @return legacy message + * @param miniMessage the miniMessage to resolve + * @return a legacy format string representation of the miniMessage */ @Deprecated @NotNull String resolveLegacy(@NotNull String miniMessage); + /** + * This method translates a legacy format string to MiniMessage format. + * + * @param ampersand the legacy format string to translate + * @return a MiniMessage format string representation of the legacy format string + */ @NotNull String translateLegacyToMiniMessage(@NotNull String ampersand); + /** + * This method translates a legacy format section string to MiniMessage format. + * + * @param section the legacy format section string to translate + * @return a MiniMessage format string representation of the legacy format section string + */ @NotNull String translateLegacySectionToMiniMessage(@NotNull String section); } diff --git a/API/src/main/java/net/juligames/core/api/message/PatternType.java b/API/src/main/java/net/juligames/core/api/message/PatternType.java index fc61d9ce..6c2f2af2 100644 --- a/API/src/main/java/net/juligames/core/api/message/PatternType.java +++ b/API/src/main/java/net/juligames/core/api/message/PatternType.java @@ -1,53 +1,97 @@ +/** + * The PatternType enum represents the different types of patterns that can be used + * to identify placeholders in messages. The enum includes information about the + * start and end characters of the pattern, a tag identifier for the pattern, + * and whether the pattern should be resolved. + * + * @author Ture Bentzin + * @since 11.02.2023 + */ package net.juligames.core.api.message; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; -/** - * @author Ture Bentzin - * 11.02.2023 - */ public enum PatternType { + // Enum constants UNTRUSTED('{', '}', "safe", false), TRUSTED('[', ']', "unsafe", true); - + // Fields private final char start; private final char end; private final String tagIdentifier; private final boolean resolve; - PatternType(char start, char end, String tagIdentifier, boolean parse) { + // Constructor + PatternType(char start, char end, String tagIdentifier, boolean resolve) { this.start = start; this.end = end; this.tagIdentifier = tagIdentifier; - this.resolve = parse; + this.resolve = resolve; } + // Methods - why is this so EXTREME javadoced? + + /** + * Builds a pattern string using the start and end characters and an integer index. + * + * @param i the integer index to use in the pattern + * @return the pattern string + */ @Contract(pure = true) public @NotNull String buildPattern(int i) { return String.valueOf(start) + i + end; } + /** + * Builds a tag string using the tag identifier and an integer index. + * + * @param i the integer index to use in the tag + * @return the tag string + */ @Contract(pure = true) public @NotNull String buildTag(int i) { return "<" + buildTagID(i) + ">"; } + /** + * Builds a tag identifier string using the tag identifier and an integer index. + * + * @param i the integer index to use in the tag identifier + * @return the tag identifier string + */ @Contract(pure = true) public @NotNull String buildTagID(int i) { return "param_" + tagIdentifier + "_" + i; } + /** + * Converts a pattern string to a tag string. + * + * @param target the target string to convert + * @param index the integer index to use in the tag + * @return the converted tag string + */ public @NotNull String convertPatternToTag(@NotNull String target, int index) { return target.replace(buildPattern(index), buildTag(index)); } + /** + * Returns whether or not the pattern should be resolved. + * + * @return true if the pattern should be resolved, false otherwise + */ public boolean shouldParse() { return resolve; } + /** + * Returns a string representation of the PatternType enum constant. + * + * @return a string representation of the PatternType enum constant + */ @Contract(pure = true) public @NotNull String toString() { return "PatternType: " + name() + ": \"" + start + "i" + end + "\""; diff --git a/API/src/main/java/net/juligames/core/api/minigame/StartType.java b/API/src/main/java/net/juligames/core/api/minigame/StartType.java index 60c621cd..e61c93f7 100644 --- a/API/src/main/java/net/juligames/core/api/minigame/StartType.java +++ b/API/src/main/java/net/juligames/core/api/minigame/StartType.java @@ -11,19 +11,63 @@ @ApiStatus.AvailableSince("1.5") public interface StartType { + /** + * The direct starting option. + */ StartType DIRECT = new SimpleStartType("DIRECT"); + + /** + * The delayed starting option. + */ StartType DELAYED = new SimpleStartType("DELAYED"); + /** + * Compares two StartType objects for similarity. + * + * @param s1 the first StartType object + * @param s2 the second StartType object + * @return true if the objects are similar, false otherwise + */ static boolean compare(@NotNull StartType s1, StartType s2) { return s1.isSimilar(s2); } + /** + * Returns the name of this StartType object. + * + * @return the name of this StartType object + */ String getName(); + /** + * Checks whether this StartType object is similar to another StartType object. + * + *

+ * Two StartType objects are considered similar if they have the same name. + *

+ * + * @param startType the StartType object to compare to + * @return true if the objects are similar, false otherwise + * + *

Example usage:

+ *
{@code
+     * StartType s1 = StartType.DIRECT;
+     * StartType s2 = new SimpleStartType("DIRECT");
+     * boolean areSimilar = s1.isSimilar(s2); // true
+     *
+     * StartType s3 = new ImaginaryStartType("IMAGINARY");
+     * StartType s4 = new SimpleStartType("IMAGINARY");
+     * boolean areSimilar2 = s3.isSimilar(s4); // true
+     * }
+ */ default boolean isSimilar(@NotNull StartType startType) { return this.getName().equals(startType.getName()); } + + /** + * A simple implementation of the StartType interface. + */ record SimpleStartType(String name) implements StartType { @Override public String getName() { diff --git a/API/src/main/java/net/juligames/core/api/misc/APIUtils.java b/API/src/main/java/net/juligames/core/api/misc/APIUtils.java new file mode 100644 index 00000000..9af23098 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/misc/APIUtils.java @@ -0,0 +1,176 @@ +package net.juligames.core.api.misc; + +import de.bentzin.tools.Independent; +import de.bentzin.tools.logging.JavaLogger; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.logging.Logger; +import java.util.stream.Stream; + +/** + * @author Ture Bentzin + * 10.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +public class APIUtils { + + private APIUtils() { + + } + + /** + * Formats a StackFrame object to obtain caller information. + * + * @param frame The StackFrame object representing the caller. + * @return A formatted string containing caller information. + */ + @Independent + public static @NotNull String formatCaller(StackWalker.@NotNull StackFrame frame) { + return frame.getMethodName() + + "@" + + frame.getClassName() + + ":" + frame.getLineNumber() + + (frame.getClassName().equals(frame.getFileName()) + ? " " + : " located in " + frame.getFileName() + "[" + frame.getByteCodeIndex() + "]"); + } + + /** + * Executes the given `ThrowingRunnable` and returns `true` if it completes without throwing an exception, or if the thrown exception is not contained in the specified collection of exceptions. + * + * @param runnable the `ThrowingRunnable` to execute + * @param exceptions a collection of exceptions that should not be counted as a failure + * @return `true` if the `ThrowingRunnable` completes without throwing an exception or if the thrown exception is not contained in the specified collection of exceptions + */ + @Independent + public static boolean executedWithoutException(@NotNull ThrowingRunnable runnable, @NotNull Collection exceptions) { + try { + runnable.run(); + return true; + } catch (Exception e) { + return !exceptions.contains(e); + } + } + + /** + * Executes the given `ThrowingRunnable` and returns `true` if it completes without throwing an exception. + * + * @param runnable the `ThrowingRunnable` to execute + * @return `true` if the `ThrowingRunnable` completes without throwing an exception + */ + @Independent + public static boolean executedWithoutException(@NotNull ThrowingRunnable runnable) { + return executedWithoutException(runnable, Collections.emptyList()); + } + + /** + * Executes the given `ThrowingRunnable` and returns `true` if it completes without throwing an exception that is contained in the specified array of exceptions. + * + * @param runnable the `ThrowingRunnable` to execute + * @param exceptions an array of exceptions that should not be counted as a failure + * @return `true` if the `ThrowingRunnable` completes without throwing an exception or if the thrown exception is not contained in the specified array of exceptions + */ + @Independent + public static boolean executedWithoutException(@NotNull ThrowingRunnable runnable, Exception... exceptions) { + return executedWithoutException(runnable, List.of(exceptions)); + } + + /** + * Executes the given `Runnable` and returns `true` if it completes without throwing an exception, or if the thrown exception is not contained in the specified collection of exceptions. + * + * @param runnable the `Runnable` to execute + * @param exceptions a collection of exceptions that should not be counted as a failure + * @return `true` if the `Runnable` completes without throwing an exception or if the thrown exception is not contained in the specified collection of exceptions + */ + @Independent + public static boolean executedWithoutExceptionL(@NotNull Runnable runnable, @NotNull Collection exceptions) { + try { + runnable.run(); + return true; + } catch (Exception e) { + return !exceptions.contains(e); + } + } + + /** + * Executes the given `Runnable` and returns `true` if it completes without throwing an exception. + * + * @param runnable the `Runnable` to execute + * @return `true` if the `Runnable` completes without throwing an exception + */ + @Independent + public static boolean executedWithoutExceptionL(@NotNull Runnable runnable) { + return executedWithoutExceptionL(runnable, Collections.emptyList()); + } + + /** + * Maps the elements of the input stream using the provided mapper function, while dropping any elements that cause an exception during mapping. + * + * @param the type of the input stream elements + * @param the type of the resulting stream elements + * @param stream the input stream to be mapped + * @param mapper the mapping function to apply to each element of the input stream + * @return a new stream containing the mapped elements, with any elements causing an exception during mapping dropped + */ + @Independent + public static @NotNull Stream mapOrDrop(@NotNull Stream stream, @NotNull Function mapper) { + List list = new ArrayList<>(); + stream.forEachOrdered(t -> { + try { + list.add(mapper.apply(t)); + } catch (Exception ignored) { + // drop + } + }); + return list.stream(); + } + + public static void executeAndSwallow(ThrowingRunnable throwingRunnable) { + try { + throwingRunnable.run(); + } catch (Exception ignored) { + } + } + + @SafeVarargs + public static Optional executeAndReturnFirstSuccess(Supplier @NotNull ... suppliers) { + for (Supplier supplier : suppliers) { + try { + return Optional.of(supplier.get()); + } catch (Exception ignore) { + } + } + return Optional.empty(); + } + + /** + * Creates a JavaLogger instance from a {@link Logger}. + * + * @param logger The {@link Logger} instance. + * @return A {@link JavaLogger} instance. + */ + @Contract("_ -> new") + @Independent + public static @NotNull JavaLogger fromUtil(Logger logger) { + return new JavaLogger(logger.getName(), logger); + } + + /** + * Creates a JavaLogger instance from a logger name. + * + * @param name The name of the logger. + * @return A {@link JavaLogger} instance. + */ + @Contract("_ -> new") + @Independent + public static @NotNull JavaLogger fromName(String name) { + return fromUtil(Logger.getLogger(name)); + } + + +} diff --git a/API/src/main/java/net/juligames/core/api/misc/DurationFormatUtils.java b/API/src/main/java/net/juligames/core/api/misc/DurationFormatUtils.java index 6285d582..df83d4f9 100644 --- a/API/src/main/java/net/juligames/core/api/misc/DurationFormatUtils.java +++ b/API/src/main/java/net/juligames/core/api/misc/DurationFormatUtils.java @@ -22,7 +22,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrBuilder; import org.apache.commons.lang.time.DateUtils; -import org.checkerframework.checker.units.qual.A; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -78,6 +77,26 @@ public class DurationFormatUtils { static final @NotNull Object S = "S"; public static @NotNull String INTERNAL_MESSAGE_PREFIX = "internal.api.misc.format."; + static { + API.get().getAPILogger().info(DurationFormatUtils.class.getName() + " was loaded! Trying to register default messages:"); + long s1 = System.currentTimeMillis(); + try { + registerMessages(); + //BIT + final long between = s1 - System.currentTimeMillis(); + Duration duration = Duration.ofMillis(between); + String formatDurationWords = formatDurationWords(duration, false, false, null, + API.get().getMessageApi().defaultUtilLocale()); + API.get().getAPILogger().info("finished registration of default messages! (took: " + formatDurationWords + ")"); + } catch (Exception e) { + API.get().getAPILogger().warning("failed to register default messages: " + e); + ThrowableDebug.debug(e); + } + + } + + //----------------------------------------------------------------------- + /** *

DurationFormatUtils instances should NOT be constructed in standard programming.

* @@ -88,8 +107,6 @@ public DurationFormatUtils() { super(); } - //----------------------------------------------------------------------- - /** *

Formats the time gap as a string.

* @@ -245,24 +262,6 @@ public DurationFormatUtils() { suppressTrailingZeroElements, getLocalisationFromMessageSystem(locale, s)); } - static { - API.get().getAPILogger().info(DurationFormatUtils.class.getName() + " was loaded! Trying to register default messages:"); - long s1 = System.currentTimeMillis(); - try { - registerMessages(); - //BIT - final long between = s1 - System.currentTimeMillis(); - Duration duration = Duration.ofMillis(between); - String formatDurationWords = formatDurationWords(duration, false, false, null, - API.get().getMessageApi().defaultUtilLocale()); - API.get().getAPILogger().info("finished registration of default messages! (took: " + formatDurationWords + ")"); - }catch (Exception e){ - API.get().getAPILogger().error("failed to register default messages: " + e); - ThrowableDebug.debug(e); - } - - } - public static void registerMessages() { registerDefaultMessage("days"); registerDefaultMessage("hours"); @@ -669,7 +668,7 @@ static Token[] lexx(String format) { Object value = null; switch (ch) { // TODO: Need to handle escaping of ' - case '\'': + case '\'' -> { if (inLiteral) { buffer = null; inLiteral = false; @@ -678,34 +677,21 @@ static Token[] lexx(String format) { list.add(new Token(buffer)); inLiteral = true; } - break; - case 'y': - value = y; - break; - case 'M': - value = M; - break; - case 'd': - value = d; - break; - case 'H': - value = H; - break; - case 'm': - value = m; - break; - case 's': - value = s; - break; - case 'S': - value = S; - break; - default: + } + case 'y' -> value = y; + case 'M' -> value = M; + case 'd' -> value = d; + case 'H' -> value = H; + case 'm' -> value = m; + case 's' -> value = s; + case 'S' -> value = S; + default -> { if (buffer == null) { buffer = new StringBuffer(); list.add(new Token(buffer)); } buffer.append(ch); + } } if (value != null) { @@ -719,7 +705,7 @@ static Token[] lexx(String format) { buffer = null; } } - return (Token[]) list.toArray(new Token[list.size()]); + return (Token[]) list.toArray(new Token[0]); } diff --git a/API/src/main/java/net/juligames/core/api/misc/EntryInterpretationUtil.java b/API/src/main/java/net/juligames/core/api/misc/EntryInterpretationUtil.java index d16c6d75..f5bf9c77 100644 --- a/API/src/main/java/net/juligames/core/api/misc/EntryInterpretationUtil.java +++ b/API/src/main/java/net/juligames/core/api/misc/EntryInterpretationUtil.java @@ -1,36 +1,80 @@ package net.juligames.core.api.misc; import net.juligames.core.api.config.Interpreter; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; import java.util.Collection; import java.util.Map; +import java.util.function.IntFunction; import java.util.stream.Collectors; +@ApiStatus.AvailableSince("1.5") public class EntryInterpretationUtil { private EntryInterpretationUtil() { } + /** + * Interpret an entry in a {@link Map} using the given key and value interpreters and return an unmodifiable map entry. + * + * @param stringStringEntry the map entry to interpret + * @param kInterpreter the key interpreter + * @param vInterpreter the value interpreter + * @param the type of the key + * @param the type of the value + * @return an unmodifiable map entry with an interpreted key and value + * @throws Exception if there is an error during interpretation + */ @Contract("_, _, _ -> new") public static Map.@NotNull @Unmodifiable Entry interpretEntry(Map.@NotNull Entry stringStringEntry, @NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) throws Exception { return Map.entry(kInterpreter.interpret(stringStringEntry.getKey()), vInterpreter.interpret(stringStringEntry.getValue())); } + /** + * Reverse an entry in a {@link Map} using the given key and value interpreters and return an unmodifiable map entry. + * + * @param stringStringEntry the map entry to reverse + * @param kInterpreter the key interpreter + * @param vInterpreter the value interpreter + * @param the type of the key + * @param the type of the value + * @return an unmodifiable map entry with reversed key and value + */ @Contract("_, _, _ -> new") public static Map.@NotNull @Unmodifiable Entry reverseEntry(Map.@NotNull Entry stringStringEntry, @NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) { return Map.entry(kInterpreter.reverse(stringStringEntry.getKey()), vInterpreter.reverse(stringStringEntry.getValue())); } - public static @Unmodifiable Collection> reverseEntries(@NotNull Collection> collection, Interpreter kInterpreter, Interpreter vInterpreter) { + /** + * Reverse a collection of entries in a {@link Map} using the given key and value interpreters and return an unmodifiable collection of map entries. + * + * @param collection the collection of map entries to reverse + * @param kInterpreter the key interpreter + * @param vInterpreter the value interpreter + * @param the type of the key + * @param the type of the value + * @return an unmodifiable collection of map entries with reversed key and value + */ + public static @NotNull @Unmodifiable Collection> reverseEntries(@NotNull Collection> collection, @NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) { return collection.stream().map(kvEntry -> reverseEntry(kvEntry, kInterpreter, vInterpreter)).collect(Collectors.toUnmodifiableSet()); } - - public static @Unmodifiable Collection> interpretEntries(@NotNull Collection> collection, Interpreter kInterpreter, Interpreter vInterpreter) { + /** + * Interpret a collection of entries in a {@link Map} using the given key and value interpreters and return an unmodifiable collection of map entries. + * + * @param collection the collection of map entries to interpret + * @param kInterpreter the key interpreter + * @param vInterpreter the value interpreter + * @param the type of the key + * @param the type of the value + * @return an unmodifiable collection of map entries with interpreted key and value + * @throws RuntimeException if there is an error during interpretation + */ + public static @NotNull @Unmodifiable Collection> interpretEntries(@NotNull Collection> collection, @NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) { return collection.stream().map(kvEntry -> { try { return interpretEntry(kvEntry, kInterpreter, vInterpreter); @@ -39,4 +83,13 @@ private EntryInterpretationUtil() { } }).collect(Collectors.toUnmodifiableSet()); } + + public static @Unmodifiable @NotNull Map interpretMap(@NotNull Map map, @NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) { + return Map.ofEntries(interpretEntries(map.entrySet(), kInterpreter, vInterpreter).toArray((IntFunction[]>) value -> new Map.Entry[0])); + } + + public static @Unmodifiable @NotNull Map reverseMap(@NotNull Map map, @NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) { + return Map.ofEntries(reverseEntries(map.entrySet(), kInterpreter, vInterpreter).toArray((IntFunction[]>) value -> new Map.Entry[0])); + } + } diff --git a/API/src/main/java/net/juligames/core/api/misc/GenericHashtableToStringHashtableMapper.java b/API/src/main/java/net/juligames/core/api/misc/GenericHashtableToStringHashtableMapper.java index 614977bf..d29f992e 100644 --- a/API/src/main/java/net/juligames/core/api/misc/GenericHashtableToStringHashtableMapper.java +++ b/API/src/main/java/net/juligames/core/api/misc/GenericHashtableToStringHashtableMapper.java @@ -9,6 +9,10 @@ import java.util.function.Function; /** + * The GenericHashtableToStringHashtableMapper class is a function that maps a Hashtable with generic key-value types + * to a Hashtable with String keys and String values. + * It uses provided interpreters to convert the keys and values of the input Hashtable to their String representations. + * * @author Ture Bentzin * 03.03.2023 */ @@ -18,16 +22,39 @@ public class GenericHashtableToStringHashtableMapper implements Function kInterpreter; private final @NotNull Interpreter vInterpreter; + /** + * Constructs a GenericHashtableToStringHashtableMapper with the given key and value interpreters. + * + * @param kInterpreter the interpreter for converting keys to String + * @param vInterpreter the interpreter for converting values to String + * @throws NullPointerException if the kInterpreter or vInterpreter parameter is null + */ public GenericHashtableToStringHashtableMapper(@NotNull Interpreter kInterpreter, @NotNull Interpreter vInterpreter) { this.kInterpreter = kInterpreter; this.vInterpreter = vInterpreter; } + /** + * Creates a new GenericHashtableToStringHashtableMapper with a parallel interpreter for keys and values of the same type. + * + * @param the type of the keys and values + * @param tInterpreter the interpreter for converting keys and values to String + * @return a GenericHashtableToStringHashtableMapper with parallel key and value interpreters + * @throws NullPointerException if the tInterpreter parameter is null + */ @Contract(value = "_ -> new", pure = true) public static @NotNull GenericHashtableToStringHashtableMapper createParallel(final Interpreter tInterpreter) { return new GenericHashtableToStringHashtableMapper<>(tInterpreter, tInterpreter); } + /** + * Applies the mapping function to the given input Hashtable, converting its keys and values to String + * and creating a new Hashtable with String keys and String values. + * + * @param objectObjectHashtable the input Hashtable to be mapped + * @return a new Hashtable with String keys and String values + * @throws NullPointerException if the objectObjectHashtable parameter is null + */ @Override public @NotNull Hashtable apply(@NotNull Hashtable objectObjectHashtable) { final Hashtable stringStringHashtable = new Hashtable<>(); @@ -35,4 +62,4 @@ public GenericHashtableToStringHashtableMapper(@NotNull Interpreter kInterpre stringStringHashtable.put(kInterpreter.reverse(key), vInterpreter.reverse(value))); return stringStringHashtable; } -} +} \ No newline at end of file diff --git a/API/src/main/java/net/juligames/core/api/misc/Predicates.java b/API/src/main/java/net/juligames/core/api/misc/Predicates.java new file mode 100644 index 00000000..0f5fa0f0 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/misc/Predicates.java @@ -0,0 +1,53 @@ +package net.juligames.core.api.misc; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +/** + * This class provides predicates + * + * @author Ture Bentzin + * 19.05.2023 + * @implNote might use {@link APIUtils#executedWithoutExceptionL(Runnable)} later + */ +public class Predicates { + + private Predicates() { + + } + + @Contract(pure = true) + @ApiStatus.AvailableSince("1.6") + public static @NotNull Predicate instanceOf(Class clazz) { + return t -> t.getClass().isInstance(clazz); + } + + @Contract(pure = true) + @ApiStatus.AvailableSince("1.6") + public static @NotNull Predicate nestedIn(Class clazz) { + return t -> t.getClass().getNestHost().equals(clazz); + } + + @Contract(pure = true) + @ApiStatus.AvailableSince("1.6") + public static @NotNull Predicate executes(Consumer operation) { + return t -> { + try { + operation.accept(t); + } catch (Exception e) { + return false; + } + return true; + }; + } + + @Contract(pure = true) + @ApiStatus.AvailableSince("1.6") + public static @NotNull Predicate fails(Consumer operation) { + return Predicate.not(executes(operation)); + } +} diff --git a/API/src/main/java/net/juligames/core/api/misc/ThrowableDebug.java b/API/src/main/java/net/juligames/core/api/misc/ThrowableDebug.java index e00d4c16..834d5e5c 100644 --- a/API/src/main/java/net/juligames/core/api/misc/ThrowableDebug.java +++ b/API/src/main/java/net/juligames/core/api/misc/ThrowableDebug.java @@ -1,7 +1,6 @@ package net.juligames.core.api.misc; import net.juligames.core.api.API; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.io.PrintWriter; diff --git a/API/src/main/java/net/juligames/core/api/misc/ThrowingRunnable.java b/API/src/main/java/net/juligames/core/api/misc/ThrowingRunnable.java new file mode 100644 index 00000000..20966f71 --- /dev/null +++ b/API/src/main/java/net/juligames/core/api/misc/ThrowingRunnable.java @@ -0,0 +1,20 @@ +package net.juligames.core.api.misc; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +/** + * Like a normal {@link Runnable} but it accepts Exceptions + * + * @author Ture Bentzin + * 10.04.2023 + */ +public interface ThrowingRunnable { + + @Contract(pure = true) + static @NotNull ThrowingRunnable fromRunnable(@NotNull Runnable runnable) { + return runnable::run; + } + + void run() throws Exception; +} diff --git a/AdventureAPI/pom.xml b/AdventureAPI/pom.xml index a183bb67..1331fef7 100644 --- a/AdventureAPI/pom.xml +++ b/AdventureAPI/pom.xml @@ -1,12 +1,18 @@ + JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -24,7 +30,7 @@ net.juligames.core API - 1.5 + 1.6 @@ -45,6 +51,11 @@ 4.11.0 compile + + de.bentzin + ConversationLib + 1.3.1 + \ No newline at end of file diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/AdventureTagManager.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/AdventureTagManager.java index 703b2d8d..4b14ba28 100644 --- a/AdventureAPI/src/main/java/net/juligames/core/adventure/AdventureTagManager.java +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/AdventureTagManager.java @@ -49,6 +49,7 @@ public interface AdventureTagManager extends MiniMessageSerializer { /** * This method creates a new {@link TagResolver} that combines {@link #getResolver()} and append + * * @param append additional resolvers * @return a new {@link TagResolver} */ diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/AdventureAPI.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/AdventureAPI.java index 0ddd9055..16f188dd 100644 --- a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/AdventureAPI.java +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/AdventureAPI.java @@ -18,7 +18,7 @@ * Please make sure the modules (AdventureAPI & AdventureCore) are the same version to avoid issues while execution! */ public interface AdventureAPI { - @NotNull String API_VERSION = "1.5"; + @NotNull String API_VERSION = "1.6"; static @NotNull AdventureAPI get() { AdventureAPI api = AdventureAPICore.getAPI(); diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/MessageRepresentation.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/MessageRepresentation.java new file mode 100644 index 00000000..f449c5f4 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/MessageRepresentation.java @@ -0,0 +1,99 @@ +package net.juligames.core.adventure.api; + +import net.juligames.core.adventure.AdventureTagManager; +import net.juligames.core.api.API; +import net.juligames.core.api.config.representations.Representation; +import net.juligames.core.api.jdbi.DBLocale; +import net.juligames.core.api.message.Message; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.pointer.Pointered; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public class MessageRepresentation implements ComponentLike, Representation { + + private final @NotNull AdventureTagManager adventureTagManager; + private final @NotNull Message message; + + public MessageRepresentation(@NotNull AdventureTagManager adventureTagManager, @NotNull Message message) { + this.adventureTagManager = adventureTagManager; + this.message = message; + } + + public MessageRepresentation(@NotNull Message message) { + this.adventureTagManager = AdventureAPI.get().getAdventureTagManager(); + this.message = message; + } + + @Contract("_, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, String locale) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, locale)); + } + + @Contract("_, _, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, String locale, String... replacements) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, locale, replacements)); + } + + @Contract("_, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, Locale locale) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, locale)); + } + + @Contract("_, _, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, Locale locale, String... replacements) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, locale, replacements)); + } + + @Contract("_, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, DBLocale locale) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, locale)); + } + + @Contract("_, _, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, DBLocale locale, String... replacements) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, locale, replacements)); + } + + //Personal + + @Contract("_, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, @NotNull Pointered pointered) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, + pointered.get(Identity.LOCALE).orElse(defaultLocale()))); + } + + @Contract("_, _, _ -> new") + public static @NotNull MessageRepresentation represent(String messageKey, @NotNull Pointered pointered, String... replacements) { + return new MessageRepresentation(API.get().getMessageApi().getMessageSmart(messageKey, pointered.get(Identity.LOCALE).orElse(defaultLocale()), replacements)); + } + + private static @NotNull Locale defaultLocale() { + return API.get().getMessageApi().defaultUtilLocale(); + } + + @Override + public @NotNull Component asComponent() { + return adventureTagManager.resolve(message); + } + + public Message getMessage() { + return message; + } + + @Override + @ApiStatus.AvailableSince("1.6") + public Component represent() { + return asComponent(); + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/interpreter/IndexBackedInterpreterProvider.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/interpreter/IndexBackedInterpreterProvider.java new file mode 100644 index 00000000..16ba8dbb --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/interpreter/IndexBackedInterpreterProvider.java @@ -0,0 +1,164 @@ +package net.juligames.core.adventure.api.interpreter; + +import net.juligames.core.api.config.Interpreter; +import net.kyori.adventure.util.Index; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.NoSuchElementException; +import java.util.function.Supplier; + +/** + * The `IndexBackedInterpreterProvider` class is an implementation of the `Supplier` interface that provides an interpreter for a given index-based data source. + * It allows interpreting values based on keys and vice versa, using two different interpreters for key and value types. + * The interpretation can be done in the normal direction or in an inverted direction, depending on the `inverted` flag. + * + * @param The type of the keys in the index. + * @param The type of the values in the index. + * @param The type of the index that implements the `Index` interface. + * @author Ture Bentzin + * 18.05.2023 + * @apiNote You can use {@link IndexBackedInterpreterProviderBuilder} to build your instance + */ +@ApiStatus.AvailableSince("1.6") +public final class IndexBackedInterpreterProvider> implements Supplier> { + + private final Interpreter normalInterpreter; + private final Interpreter invertedInterpreter; + private final Index source; + private final boolean inverted; + + /** + * Creates a new `IndexBackedInterpreterProvider` with the provided key and value interpreters and data source. + * The interpretation is set to be in the normal direction (non-inverted). + * + * @param kInterpreter The interpreter for the key type `K`. + * @param vInterpreter The interpreter for the value type `V`. + * @param source The data source to pull from, implementing the `Index` interface. + */ + public IndexBackedInterpreterProvider(Interpreter kInterpreter, Interpreter vInterpreter, Index source) { + this(kInterpreter, vInterpreter, source, false); + } + + /** + * Creates a new `IndexBackedInterpreterProvider` with the provided key and value interpreters, data source, and inversion flag. + * + * @param kInterpreter The interpreter for the key type `K`. + * @param vInterpreter The interpreter for the value type `V`. + * @param source The data source to pull from, implementing the `Index` interface. + * @param inverted Flag indicating whether the interpretation is inverted or not. + */ + public IndexBackedInterpreterProvider(Interpreter kInterpreter, Interpreter vInterpreter, Index source, boolean inverted) { + this.source = source; + this.inverted = inverted; + + // Create the normal interpreter + this.normalInterpreter = new Interpreter<>() { + /** + * Interprets the given input as a value and retrieves it from the source. + * + * @param input The input to interpret. + * @return The interpreted value. + * @throws Exception if an error occurs during interpretation or value retrieval. + */ + @Override + public @NotNull V interpret(String input) throws Exception { + return getSource().valueOrThrow(kInterpreter.interpret(input)); + } + + /** + * Reverses the interpretation by converting the value back to its corresponding key. + * + * @param v The value to reverse. + * @return The reversed key as a string. + * @throws NoSuchElementException If the key corresponding to the value is not found in the index. + */ + @Override + public @NotNull String reverse(V v) throws NoSuchElementException { + try { + return kInterpreter.reverse(getSource().keyOrThrow(v)); + } catch (NoSuchElementException e) { + throw new NoSuchElementException("Key not found for the provided value."); + } + } + }; + + // Create the inverted interpreter + this.invertedInterpreter = new Interpreter<>() { + /** + * Interprets the given input as a key and retrieves the corresponding value from the source. + * + * @param input The input to interpret. + * @return The interpreted key. + * @throws Exception if an error occurs during interpretation or key retrieval. + */ + @Override + public @NotNull K interpret(String input) throws Exception { + return getSource().keyOrThrow(vInterpreter.interpret(input)); + } + + /** + * Reverses the interpretation by converting the key back to its corresponding value. + * + * @param k The key to reverse. + * @return The reversed value as a string. + * @throws NoSuchElementException If the value corresponding to the key is not found in the index. + */ + @Override + public @NotNull String reverse(K k) throws NoSuchElementException { + try { + return vInterpreter.reverse(getSource().valueOrThrow(k)); + } catch (NoSuchElementException e) { + throw new NoSuchElementException("Value not found for the provided key."); + } + } + + }; + } + + /** + * Retrieves the data source associated with this interpreter provider. + * + * @return The index-based data source. + */ + public Index getSource() { + return source; + } + + /** + * Returns the interpreter to use based on the inversion flag. + * + * @return The interpreter instance. + */ + @Override + public Interpreter get() { + return !inverted ? normalInterpreter : invertedInterpreter; + } + + /** + * Retrieves the inverted interpreter. + * + * @return The interpreter for inverted interpretation. + */ + public Interpreter getInvertedInterpreter() { + return invertedInterpreter; + } + + /** + * Retrieves the normal (non-inverted) interpreter. + * + * @return The interpreter for normal interpretation. + */ + public Interpreter getNormalInterpreter() { + return normalInterpreter; + } + + /** + * Indicates whether the interpretation of the `get()` method is inverted or not. + * + * @return `true` if the interpretation is inverted, `false` otherwise. + */ + public boolean isInverted() { + return inverted; + } +} \ No newline at end of file diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/interpreter/IndexBackedInterpreterProviderBuilder.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/interpreter/IndexBackedInterpreterProviderBuilder.java new file mode 100644 index 00000000..7becca85 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/interpreter/IndexBackedInterpreterProviderBuilder.java @@ -0,0 +1,91 @@ +package net.juligames.core.adventure.api.interpreter; + +import net.juligames.core.api.config.Interpreter; +import net.kyori.adventure.util.Index; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * The `IndexBackedInterpreterProviderBuilder` class provides a builder pattern for creating instances of `IndexBackedInterpreterProvider`. + * + * @param The type of the keys in the index. + * @param The type of the values in the index. + * @param The type of the index that implements the `Index` interface. + * @author Ture Bentzin + * 18.05.2023 + */ +@ApiStatus.AvailableSince("1.6") +@ApiStatus.Experimental +public class IndexBackedInterpreterProviderBuilder> { + private Interpreter kInterpreter; + private Interpreter vInterpreter; + private Index source; + private boolean inverted = false; + + /** + * Sets the interpreter for the key type `K`. + * + * @param kInterpreter The interpreter for the key type. + * @return The builder instance. + */ + public @NotNull IndexBackedInterpreterProviderBuilder setKInterpreter(Interpreter kInterpreter) { + this.kInterpreter = kInterpreter; + return this; + } + + /** + * Sets the interpreter for the value type `V`. + * + * @param vInterpreter The interpreter for the value type. + * @return The builder instance. + */ + public @NotNull IndexBackedInterpreterProviderBuilder setVInterpreter(Interpreter vInterpreter) { + this.vInterpreter = vInterpreter; + return this; + } + + /** + * Sets the data source to pull from, implementing the `Index` interface. + * + * @param source The data source. + * @return The builder instance. + */ + public @NotNull IndexBackedInterpreterProviderBuilder setSource(Index source) { + this.source = source; + return this; + } + + /** + * Sets the inversion flag, indicating whether the interpretation is inverted or not. + * + * @param inverted The inversion flag. + * @return The builder instance. + */ + public @NotNull IndexBackedInterpreterProviderBuilder setInverted(boolean inverted) { + this.inverted = inverted; + return this; + } + + /** + * Creates a new instance of `IndexBackedInterpreterProvider` based on the provided configuration. + * + * @return The created `IndexBackedInterpreterProvider`. + * @throws IllegalStateException If the builder does not meet the minimum criteria to create an `IndexBackedInterpreterProvider`. + */ + public @NotNull IndexBackedInterpreterProvider createIndexBackedInterpreterProvider() throws IllegalStateException { + if (kInterpreter == null || vInterpreter == null || source == null) { + throw new IllegalStateException("Builder does not meet the minimum criteria to create an IndexBackedInterpreterProvider!"); + } + return new IndexBackedInterpreterProvider<>(kInterpreter, vInterpreter, source, inverted); + } + + /** + * Builds and supplies the appropriate `Interpreter` based on the builder configuration. + * + * @return The built `Interpreter`. + * @throws IllegalStateException If the builder does not meet the minimum criteria to create an `IndexBackedInterpreterProvider` or the `get()` method of the provider throws an exception. + */ + public @NotNull Interpreter buildAndSupply() throws IllegalStateException { + return createIndexBackedInterpreterProvider().get(); + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/BackedPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/BackedPrompt.java new file mode 100644 index 00000000..487b5ed9 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/BackedPrompt.java @@ -0,0 +1,167 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import de.bentzin.conversationlib.prompt.ValidatingPrompt; +import de.bentzin.tools.misc.SubscribableType; +import net.juligames.core.api.config.PrimitiveInterpreter; +import net.juligames.core.api.misc.APIUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.function.Function; + +/** + * @author Ture Bentzin + * 10.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class BackedPrompt extends ValidatingPrompt { + + private final @NotNull PromptBacker promptBacker; + + public BackedPrompt(@NotNull PromptBacker promptBacker) { + this.promptBacker = promptBacker; + } + + protected @NotNull PromptBacker getPromptBacker() { + return promptBacker; + } + + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) { + return promptBacker.canProvide(input); + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + return acceptIndexBackedInput(context, promptBacker.provide(input)); + } + + public abstract @Nullable Prompt acceptIndexBackedInput(@NotNull ConversationContext context, @NotNull V v); + + public interface PromptBacker { + + static @NotNull PromptBacker fromFunction(@NotNull Function function) { + return new SimplePromptBacker() { + @Override + public boolean canProvide(@NotNull String string) { + return provideOrNull(string) != null; + } + + @Override + public @Nullable V provideOrNull(@NotNull String string) { + return function.apply(string); + } + }; + } + + static @NotNull PromptBacker fromInterpreter(@NotNull PrimitiveInterpreter vInterpreter) { + return new SimplePromptBacker() { + @Override + public boolean canProvide(@NotNull String string) { + return APIUtils.executedWithoutException(() -> vInterpreter.interpret(string)); + } + + @Override + public @NotNull V provideOrNull(@NotNull String string) { + try { + return vInterpreter.interpret(string); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + + boolean canProvide(@NotNull String string); + + @NotNull + V provide(@NotNull String string) throws NoSuchElementException; + + @Nullable + V provideOrNull(@NotNull String string); + + @NotNull + V provideOr(@NotNull String string, @NotNull V v); + + @NotNull + Optional asOptional(@NotNull String string); + + @NotNull + SubscribableType asType(@NotNull String string); + } + + /** + * A PromptBacker that works backed from the #provideOrNull(String) + * + * @param v + */ + public abstract static class SimplePromptBacker implements PromptBacker { + + @Override + public abstract boolean canProvide(@NotNull String string); + + @Override + public abstract @Nullable V provideOrNull(@NotNull String string); + + @Override + public @NotNull V provide(@NotNull String string) throws NoSuchElementException { + boolean b; + try { + b = canProvide(string); + } catch (Exception e) { + throw new NoSuchElementException("wasn't able to provide because of a failed #canProvide(String) check", e); + } + if (!b) { + throw new NoSuchElementException("wasn't able to provide for string: " + string); + } else { + try { + final V provideOrNull = provideOrNull(string); + + if (provideOrNull == null) { + throw new NoSuchElementException("Missmatch!! #canProvide indicated providing for " + string + + " was possible, but #provideOrNull returned null! NAG AUTHOR!!"); + } else { + //oh, finally + return provideOrNull; + } + + } catch (Exception e) { + throw new NoSuchElementException("wasn't able to provide because of failed #provideOrNull(String)", e); + } + + } + } + + @Override + public @NotNull V provideOr(@NotNull String string, @NotNull V v) { + return canProvide(string) ? or(this::provideOrNull, string, v) : v; + } + + @Override + public @NotNull Optional asOptional(@NotNull String string) { + return Optional.ofNullable(provideOrNull(string)); + } + + @Override + public @NotNull SubscribableType asType(@NotNull String string) { + SubscribableType vSubscribableType = new SubscribableType<>(); + if (canProvide(string)) { + vSubscribableType.set(provideOrNull(string)); + } + return vSubscribableType; + } + + private @NotNull V or(@NotNull Function function, @NotNull String string, @NotNull V backup) { + @NotNull V apply = function.apply(string); + if (apply == null) { + return backup; + } + return apply; + } + + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/ConfigurationAPIPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/ConfigurationAPIPrompt.java new file mode 100644 index 00000000..22373388 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/ConfigurationAPIPrompt.java @@ -0,0 +1,16 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.prompt.Prompt; +import net.juligames.core.api.config.ConfigurationAPI; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public interface ConfigurationAPIPrompt extends Prompt { + + @NotNull ConfigurationAPI getConfigurationApi(); +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/ConfigurationPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/ConfigurationPrompt.java new file mode 100644 index 00000000..bae79d26 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/ConfigurationPrompt.java @@ -0,0 +1,41 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import de.bentzin.conversationlib.prompt.Prompt; +import net.juligames.core.api.config.Configuration; +import net.juligames.core.api.config.ConfigurationAPI; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class ConfigurationPrompt extends FixedSetPrompt implements ConfigurationAPIPrompt { + + private final ConfigurationAPI configurationAPI; + + public ConfigurationPrompt(@NotNull ConfigurationAPI configurationAPI) { + super(configurationAPI::getOrCreate, configurationAPI.getAll()); + this.configurationAPI = configurationAPI; + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + return acceptConfigurationInput(context, configurationAPI.getOrCreate(input)); + } + + public abstract Prompt acceptConfigurationInput(@NotNull ConversationContext context, @NotNull Configuration configuration); + + public ConfigurationAPI getConfigurationAPI() { + return configurationAPI; + } + + @Override + public @NotNull ConfigurationAPI getConfigurationApi() { + return configurationAPI; + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/DictionaryBackedPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/DictionaryBackedPrompt.java new file mode 100644 index 00000000..68d25531 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/DictionaryBackedPrompt.java @@ -0,0 +1,46 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import de.bentzin.conversationlib.prompt.ValidatingPrompt; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.Dictionary; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * @author Ture Bentzin + * 10.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class DictionaryBackedPrompt extends ValidatingPrompt { + + private final Dictionary dictionary; + + public DictionaryBackedPrompt(Dictionary index) { + this.dictionary = index; + } + + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) { + return Collections.list(dictionary.keys()).contains(input); + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + return acceptIndexBackedInput(context, Objects.requireNonNull(dictionary.get(input))); + } + + public abstract @Nullable Prompt acceptIndexBackedInput(@NotNull ConversationContext context, @NotNull V v); + + @Override + protected @Nullable Component getFailedValidationText(@NotNull ConversationContext context, @NotNull String invalidInput) { + Stream stringStream = Collections.list(dictionary.keys()).stream().filter(s -> s.contains(invalidInput)); + return Component.text(" -> " + stringStream.toList() + "?"); + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/IndexBackedPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/IndexBackedPrompt.java new file mode 100644 index 00000000..b7d22cde --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/IndexBackedPrompt.java @@ -0,0 +1,45 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import de.bentzin.conversationlib.prompt.ValidatingPrompt; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.util.Index; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.stream.Stream; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class IndexBackedPrompt extends ValidatingPrompt { + + private final Index index; + + public IndexBackedPrompt(@NotNull Index index) { + this.index = index; + } + + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) { + return index.keys().contains(input); + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + return acceptIndexBackedInput(context, Objects.requireNonNull(index.value(input))); + } + + public abstract @Nullable Prompt acceptIndexBackedInput(@NotNull ConversationContext context, @NotNull V v); + + @Override + protected @Nullable Component getFailedValidationText(@NotNull ConversationContext context, @NotNull String invalidInput) { + Stream stringStream = index.keys().stream().filter(s -> s.contains(invalidInput)); + return Component.text(" -> " + stringStream.toList() + "?"); + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/MapBackedPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/MapBackedPrompt.java new file mode 100644 index 00000000..aab9bc95 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/MapBackedPrompt.java @@ -0,0 +1,45 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import de.bentzin.conversationlib.prompt.ValidatingPrompt; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class MapBackedPrompt extends ValidatingPrompt { + + private final Map map; + + public MapBackedPrompt(@NotNull Map map) { + this.map = map; + } + + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) { + return map.containsKey(input); + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + return acceptIndexBackedInput(context, Objects.requireNonNull(map.get(input))); + } + + public abstract @Nullable Prompt acceptIndexBackedInput(@NotNull ConversationContext context, @NotNull V v); + + @Override + protected @Nullable Component getFailedValidationText(@NotNull ConversationContext context, @NotNull String invalidInput) { + Stream stringStream = map.keySet().stream().filter(s -> s.contains(invalidInput)); + return Component.text(" -> " + stringStream.toList() + "?"); + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/MiniMessagePrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/MiniMessagePrompt.java new file mode 100644 index 00000000..999a7ad9 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/MiniMessagePrompt.java @@ -0,0 +1,40 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import net.juligames.core.adventure.AdventureTagManager; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class MiniMessagePrompt implements Prompt { + + private final @NotNull AdventureTagManager adventureTagManager; + + protected MiniMessagePrompt(@NotNull AdventureTagManager adventureTagManager) { + this.adventureTagManager = adventureTagManager; + } + + + @Override + public boolean blocksForInput(@NotNull ConversationContext context) { + return true; + } + + @Override + public @Nullable Prompt getNextPrompt(@NotNull ConversationContext conversationContext, @Nullable String input) { + if (input != null) { + return acceptComponentInput(conversationContext, adventureTagManager.resolve(input)); + } + return acceptComponentInput(conversationContext, null); + } + + public abstract @Nullable Prompt acceptComponentInput(@NotNull ConversationContext conversationContext, @Nullable Component component); +} + diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/NamedTextColorPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/NamedTextColorPrompt.java new file mode 100644 index 00000000..12798eb1 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/NamedTextColorPrompt.java @@ -0,0 +1,16 @@ +package net.juligames.core.adventure.api.prompt; + +import net.kyori.adventure.text.format.NamedTextColor; +import org.jetbrains.annotations.ApiStatus; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class NamedTextColorPrompt extends IndexBackedPrompt { + public NamedTextColorPrompt() { + super(NamedTextColor.NAMES); + } + +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/SpacePrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/SpacePrompt.java new file mode 100644 index 00000000..daccbf37 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/SpacePrompt.java @@ -0,0 +1,26 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.MessagePrompt; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * This prompt only displays a single line that defaults to " ". + * You can set "spaceprompt.text" (in {@link ConversationContext#sessionData()}) to a {@link String} or a {@link ComponentLike} + * - if you do this the default message will be replaced to the content of "spaceprompt.text" + * + * @author Ture Bentzin + * 14.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class SpacePrompt extends MessagePrompt { + + @Override + public final @NotNull ComponentLike getPromptMessage(@NotNull ConversationContext conversationContext) { + final Object object = conversationContext.getData("spaceprompt.text"); + return object instanceof ComponentLike componentLike ? componentLike : Component.text(object instanceof String plaintext ? plaintext : " "); + } +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/VoidPrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/VoidPrompt.java new file mode 100644 index 00000000..b59034d2 --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/VoidPrompt.java @@ -0,0 +1,33 @@ +package net.juligames.core.adventure.api.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This prompt sends an empty message + * + * @author Ture Bentzin + * 14.04.2023 + */ +public final class VoidPrompt implements Prompt { + + @Override + public @NotNull ComponentLike getPromptMessage(@NotNull ConversationContext conversationContext) { + return Component.empty(); + } + + @Override + public boolean blocksForInput(@NotNull ConversationContext context) { + return false; + } + + @Override + public @Nullable Prompt getNextPrompt(@NotNull ConversationContext conversationContext, @Nullable String input) { + return END_OF_CONVERSATION; + } + +} diff --git a/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/msc/TransitivePrompt.java b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/msc/TransitivePrompt.java new file mode 100644 index 00000000..6ecf599c --- /dev/null +++ b/AdventureAPI/src/main/java/net/juligames/core/adventure/api/prompt/msc/TransitivePrompt.java @@ -0,0 +1,76 @@ +package net.juligames.core.adventure.api.prompt.msc; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import de.bentzin.conversationlib.prompt.ValidatingPrompt; +import net.kyori.adventure.text.ComponentLike; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * @author Ture Bentzin + * 19.05.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class TransitivePrompt extends ValidatingPrompt { + + private final @NotNull Function typeSupplier; + private final @NotNull BiFunction transitiveFunction; + + public TransitivePrompt(@NotNull Function typeSupplier, @NotNull BiFunction transitiveFunction) { + this.typeSupplier = typeSupplier; + this.transitiveFunction = transitiveFunction; + } + + public static TransitivePrompt functional( + @NotNull Function messageFunction, + @NotNull Function typeSupplier, + @NotNull BiFunction transitiveFunction, + @Nullable BiFunction acceptTransitiveInputFunction + ) { + return new TransitivePrompt(typeSupplier, transitiveFunction) { + @Override + protected @Nullable Prompt acceptTransitiveInput(@NotNull ConversationContext context, @NotNull R relation) { + if (acceptTransitiveInputFunction == null) return null; + return acceptTransitiveInputFunction.apply(context, relation); + } + + @Override + public @NotNull ComponentLike getPromptMessage(@NotNull ConversationContext conversationContext) { + return messageFunction.apply(conversationContext); + } + }; + } + + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) { + try { + final T t = supplyType(context); + transitiveFunction.apply(t, input); + } catch (Exception e) { + return false; + } + return true; + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + final T t = supplyType(context); + R applied = transitiveFunction.apply(t, input); + return acceptTransitiveInput(context, applied); + } + + protected abstract @Nullable Prompt acceptTransitiveInput(@NotNull ConversationContext context, @NotNull R relation); + + public @NotNull BiFunction getTransitiveFunction() { + return transitiveFunction; + } + + public @NotNull T supplyType(ConversationContext context) { + return typeSupplier.apply(context); + } +} diff --git a/AdventureCore/pom.xml b/AdventureCore/pom.xml index a75dd7db..44e5204f 100644 --- a/AdventureCore/pom.xml +++ b/AdventureCore/pom.xml @@ -6,7 +6,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -22,7 +22,7 @@ net.juligames.core AdventureAPI - 1.5 + 1.6 diff --git a/AdventureCore/src/main/java/net/juligames/core/adventure/AdventureCore.java b/AdventureCore/src/main/java/net/juligames/core/adventure/AdventureCore.java index 0a6bd57d..01d2b02b 100644 --- a/AdventureCore/src/main/java/net/juligames/core/adventure/AdventureCore.java +++ b/AdventureCore/src/main/java/net/juligames/core/adventure/AdventureCore.java @@ -21,7 +21,7 @@ * support it. Because of this the AdventureAPI will only function as intended when bundled with the API you are using! */ public class AdventureCore implements AdventureAPI { - final String CORE_VERSION = "1.5"; + final String CORE_VERSION = "1.6"; private CoreAdventureTagManager adventureTagManager; private Logger logger; diff --git a/AdventureCore/src/main/java/net/juligames/core/adventure/CoreAdventureTagManager.java b/AdventureCore/src/main/java/net/juligames/core/adventure/CoreAdventureTagManager.java index cbcab713..8dbdc869 100644 --- a/AdventureCore/src/main/java/net/juligames/core/adventure/CoreAdventureTagManager.java +++ b/AdventureCore/src/main/java/net/juligames/core/adventure/CoreAdventureTagManager.java @@ -5,7 +5,6 @@ import net.juligames.core.api.jdbi.DBReplacement; import net.juligames.core.api.jdbi.ReplacementDAO; import net.juligames.core.api.message.Message; -import net.juligames.core.api.message.MiniMessageSerializer; import net.juligames.core.api.message.PatternType; import net.juligames.core.api.message.TagManager; import net.kyori.adventure.text.Component; diff --git a/AdventureCore/src/main/java/net/juligames/core/adventure/JDBITagAdapter.java b/AdventureCore/src/main/java/net/juligames/core/adventure/JDBITagAdapter.java index ccbb0e16..2bb22714 100644 --- a/AdventureCore/src/main/java/net/juligames/core/adventure/JDBITagAdapter.java +++ b/AdventureCore/src/main/java/net/juligames/core/adventure/JDBITagAdapter.java @@ -22,9 +22,17 @@ */ public class JDBITagAdapter { - + /** + * The logger for this class. + */ public static final Logger logger = API.get().getAPILogger().adopt("adapter"); + /** + * Converts a {@link DBReplacement} to a {@link Tag}. + * + * @param replacement the DBReplacement to convert + * @return the resulting Tag + */ @SuppressWarnings("PatternValidation") public static Tag fromJDBI(@NotNull DBReplacement replacement) { String s = replacement.getReplacementType(); @@ -63,21 +71,22 @@ public static Tag fromJDBI(@NotNull DBReplacement replacement) { /** * WARNING: This uses the fallback resolver + * + * @param replacement the DBReplacement to resolve + * @return the resulting Component */ private static @NotNull Component resolveValue(@NotNull DBReplacement replacement) { return AdventureAPI.get().getAdventureTagManager().fallbackResolve(replacement.getValue()); //TODO currently this is hardcode and i intend to leave it that way to insure that fallback resolving never fails } + /** + * Represents the type of a replacement. + * + * @see CoreSQLManager + */ @SuppressWarnings("JavadocReference") public enum ReplacementType { - TEXT_CLOSING, - TEXT, - COLOR_HEX, - COLOR_HEX_CSS, - NAMED_COLOR, - FONT, - INSERT, - PROCESS; + TEXT_CLOSING, TEXT, COLOR_HEX, COLOR_HEX_CSS, NAMED_COLOR, FONT, INSERT, PROCESS; /** * @see net.juligames.core.jdbi.CoreSQLManager diff --git a/Core/pom.xml b/Core/pom.xml index d9e9e55d..25057e66 100644 --- a/Core/pom.xml +++ b/Core/pom.xml @@ -1,12 +1,18 @@ + JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -24,7 +30,7 @@ net.juligames.core HazelAPI - 1.5 + 1.6 org.jdbi diff --git a/Core/src/main/java/net/juligames/core/Core.java b/Core/src/main/java/net/juligames/core/Core.java index 0c5e00a1..aef0bde2 100644 --- a/Core/src/main/java/net/juligames/core/Core.java +++ b/Core/src/main/java/net/juligames/core/Core.java @@ -14,6 +14,7 @@ import net.juligames.core.api.err.dev.TODOException; import net.juligames.core.api.message.MessageRecipient; import net.juligames.core.api.minigame.BasicMiniGame; +import net.juligames.core.api.misc.APIUtils; import net.juligames.core.caching.CoreCacheApi; import net.juligames.core.caching.MessageCaching; import net.juligames.core.cluster.CoreClusterApi; @@ -37,6 +38,8 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.logging.LogManager; +import java.util.stream.Stream; /** * @author Ture Bentzin @@ -48,9 +51,9 @@ public final class Core implements API { * This can be set depending on the build of the Core */ public static final String CORE_BRAND = "Core"; - public static final String CORE_VERSION_NUMBER = "1.5"; + public static final String CORE_VERSION_NUMBER = "1.6"; public static final String CORE_SPECIFICATION = "Gustav"; - private static final String BUILD_VERSION = "1.5"; //POM VERSION + private static final String BUILD_VERSION = "1.6"; //POM VERSION private static Core core; private final Registerator> hazelcastPostPreparationWorkers = new Registerator<>("hazelcastPostPreparationWorkers"); @@ -79,6 +82,14 @@ public Core() { * @param core_name the core name */ public Core(String core_name) { + APIUtils.fromName("JG-Foreman") + .debug(getShortCoreName() + " started by " + APIUtils.formatCaller( + StackWalker + .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) + .walk(Stream::findFirst) + .orElseThrow(() -> new IllegalCallerException("Core cant identify caller of constructor?!"))) + ); + start(core_name); } @@ -162,10 +173,16 @@ public void start(String core_name, @NotNull Logger logger, boolean member) { .addMessageListener(coreNotificationApi); - { + if (getHazelDataApi().isMasterInformationAvailable()) { if (!Boolean.getBoolean("acknowledgeUnsafeMasterCheck")) { coreLogger.info("checking compatibility with master.."); final String masterVersion = getHazelDataApi().getMasterInformation().get("master_version"); + if (masterVersion == null) { + coreLogger.error("Flawed data! Please check if hazelcast is working correctly and if your master" + + " is operating normal! If you see this message on your Master, you should get in touch with me at " + + "mailto://bentzin@tdrstudios.de! Thank you for using JuliGamesCore. It is likely that your JuliGamesCore Cluster " + + "starts but you should expect severe issues after reading this message!"); + } if (!getVersion().equals(masterVersion)) { coreLogger.warning("********************************************************"); @@ -178,7 +195,8 @@ public void start(String core_name, @NotNull Logger logger, boolean member) { coreLogger.warning("********************************************************"); } } - } + } else + logger.debug("master information is not available! If you see this on the Master it is expected behavior"); logger.info("hooking to shutdown..."); getJavaRuntime().addShutdownHook(new Thread(() -> { @@ -410,6 +428,11 @@ public void setOnlineRecipientProvider(@NotNull Supplier> getHazelcastPostPreparationWorkers() { return hazelcastPostPreparationWorkers; } @@ -421,4 +444,10 @@ protected void finalize() { //Currently only for testing around with GarbageColl getCoreLogger().debug("This API implementation is no longer available!"); } } + + @Contract(pure = true) + @Override + public @NotNull Logger getLogger() { + return getAPILogger(); + } } diff --git a/Core/src/main/java/net/juligames/core/config/CoreConfiguration.java b/Core/src/main/java/net/juligames/core/config/CoreConfiguration.java index 7c3a032c..69f3649b 100644 --- a/Core/src/main/java/net/juligames/core/config/CoreConfiguration.java +++ b/Core/src/main/java/net/juligames/core/config/CoreConfiguration.java @@ -60,7 +60,7 @@ public CoreConfiguration(String name) { for (Map.Entry entry : entries) { if (override) { String old = map.put(entry.getKey().toString(), entry.getValue().toString()); //oh man... oh man - Core.getInstance().getCoreLogger().debug("OVERRIDE: set " + entry.getKey() + "from " + old + " to " + entry.getValue()); + Core.getInstance().getCoreLogger().debug("OVERRIDE: set " + entry.getKey() + " from " + old + " to " + entry.getValue()); } else if (!map.containsKey(entry.getKey().toString())) { String old = map.put(entry.getKey().toString(), entry.getValue().toString()); //oh man... oh man Core.getInstance().getCoreLogger().debug("set " + entry.getKey() + "from " + old + " to " + entry.getValue()); diff --git a/Core/src/main/java/net/juligames/core/config/CoreConfigurationApi.java b/Core/src/main/java/net/juligames/core/config/CoreConfigurationApi.java index a6c41cd0..107dc41b 100644 --- a/Core/src/main/java/net/juligames/core/config/CoreConfigurationApi.java +++ b/Core/src/main/java/net/juligames/core/config/CoreConfigurationApi.java @@ -1,5 +1,6 @@ package net.juligames.core.config; +import com.hazelcast.core.DistributedObject; import net.juligames.core.Core; import net.juligames.core.api.config.ConfigWriter; import net.juligames.core.api.config.Configuration; @@ -8,6 +9,7 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; import java.util.*; import java.util.stream.Collectors; @@ -167,4 +169,23 @@ public Configuration database() { return Map.entry(section + "_" + after.substring(0, after.indexOf("_")), s.getValue()); }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))); } + + @Override + public @NotNull @Unmodifiable Collection getAll() { + return getAllHazels() + .stream() + .map(this::getOrCreate) + .collect(Collectors.toUnmodifiableSet()); + } + + @Override + public @NotNull @Unmodifiable Collection getAllHazels() { + return Core.getInstance() + .getHazelDataApi() + .getAll() + .stream() + .map(DistributedObject::getName) + .filter(s -> s.startsWith("config:")) + .collect(Collectors.toUnmodifiableSet()); + } } diff --git a/Core/src/main/java/net/juligames/core/config/IterableSplitter.java b/Core/src/main/java/net/juligames/core/config/IterableSplitter.java index 6eff05ac..796699fe 100644 --- a/Core/src/main/java/net/juligames/core/config/IterableSplitter.java +++ b/Core/src/main/java/net/juligames/core/config/IterableSplitter.java @@ -10,15 +10,31 @@ import java.util.stream.Collectors; /** + * The IterableSplitter class provides utility methods for splitting and processing iterables. + * It allows splitting an iterable into strings, writing the split strings to a configuration, + * and reading split strings from a configuration and interpreting them as objects of a given type. + * This class is final and cannot be subclassed. + * * @author Ture Bentzin * 10.01.2023 */ public final class IterableSplitter { + /** + * Private constructor to prevent instantiation of the IterableSplitter class. + */ private IterableSplitter() { } - + /** + * Splits the elements of the given iterable into strings using the provided interpreter and returns a list of the split strings. + * + * @param the type of the elements in the iterable + * @param iterable the iterable to be split + * @param interpreter the interpreter to convert elements to strings + * @return a list of strings representing the split elements + * @throws NullPointerException if the iterable or interpreter parameter is null + */ @Contract(pure = true) public static @NotNull List simpleSplit(@NotNull Iterable iterable, Interpreter interpreter) { final ArrayList data = new ArrayList<>(); @@ -29,46 +45,111 @@ private IterableSplitter() { return data; } + /** + * Splits the elements of the given iterable into strings using the provided interpreter + * and returns a SplitCollectionConfigWriter that can be used to write the split strings to a configuration. + * + * @param the type of the elements in the iterable + * @param iterable the iterable to be split + * @param interpreter the interpreter to convert elements to strings + * @return a SplitCollectionConfigWriter for writing the split strings to a configuration + * @throws NullPointerException if the iterable or interpreter parameter is null + */ @Contract(pure = true) public static @NotNull SplitCollectionConfigWriter splitToWriter(Iterable iterable, Interpreter interpreter) { return new SplitCollectionConfigWriter(simpleSplit(iterable, interpreter)); } + /** + * Splits the elements of the given iterable into strings using the provided interpreter + * and writes the split strings to the given configuration under the specified key space. + * + * @param the type of the elements in the iterable + * @param iterable the iterable to be split + * @param interpreter the interpreter to convert elements to strings + * @param configuration the configuration to write the split strings to + * @param keySpace the key space under which the split strings will be written + * @throws NullPointerException if any of the iterable, interpreter, configuration, or keySpace parameters is null + */ public static void splitAndWrite(Iterable iterable, Interpreter interpreter, Configuration configuration, String keySpace) { splitToWriter(iterable, interpreter).write(configuration, keySpace); } + /** + * Reads a collection of objects of the specified type from the given strings using the provided interpreter. + * Any strings that cannot be interpreted as objects will be ignored. + * + * @param the type of the objects to read + * @param strings the strings to read the objects from + * @param interpreter the interpreter to convert strings to objects + * @return a collection of objects interpreted from the strings + * @throws NullPointerException if the strings or interpreter parameter is null + */ public static @NotNull Collection tryReadStrings(@NotNull Iterable strings, Interpreter interpreter) { final Collection ts = new ArrayList<>(); - for (String value : strings) + for (String value : strings) { try { ts.add(interpreter.interpret(value)); } catch (Exception ignored) { + // Ignore strings that cannot be interpreted as objects } + } return ts; } + /** + * Reads a collection of objects of the specified type from the values in the given configuration + * that are associated with keys starting with the specified key space. + * + * @param the type of the objects to read + * @param configuration the configuration to read the values from + * @param keySpace the key space indicating the keys to read the values from + * @param interpreter the interpreter to convert strings to objects + * @return a collection of objects interpreted from the configuration values + * @throws NullPointerException if any of the configuration, keySpace, or interpreter parameters is null + */ public static @NotNull Collection tryReadSplitCollection(@NotNull Configuration configuration, String keySpace, Interpreter interpreter) { - return tryReadStrings(configuration.entrySet().stream().filter(entry -> - entry.getKey().startsWith(keySpace)).map(Map.Entry::getValue) - .collect(Collectors.toUnmodifiableSet()), interpreter); + return tryReadStrings( + configuration.entrySet() + .stream() + .filter(entry -> entry.getKey().startsWith(keySpace)) + .map(Map.Entry::getValue) + .collect(Collectors.toUnmodifiableSet()), + interpreter); } + /** + * The SplitCollectionConfigWriter class provides a way to write split strings to a configuration. + * It implements the ConfigWriter interface and is used by the splitToWriter method in the IterableSplitter class. + */ public static final class SplitCollectionConfigWriter implements ConfigWriter { - final List data; + private final List data; + /** + * Constructs a SplitCollectionConfigWriter with the given list of split strings. + * + * @param simpleSplit the list of split strings to be written to a configuration + * @throws NullPointerException if the simpleSplit parameter is null + */ public SplitCollectionConfigWriter(List simpleSplit) { data = Collections.unmodifiableList(simpleSplit); } + /** + * Writes the split strings to the specified configuration under the given key space. + * + * @param configuration the configuration to write the split strings to + * @param keySpace the key space under which the split strings will be written + * @throws NullPointerException if the configuration or keySpace parameter is null + */ @Override - public void write(@NotNull Configuration configuration, @NotNull String keyspace) { + public void write(@NotNull Configuration configuration, @NotNull String keySpace) { for (int i = 0; i < data.size(); i++) { final String simple = data.get(i); - final String key = keyspace + "_" + i; + final String key = keySpace + "_" + i; configuration.setString(key, simple); } } } -} +} \ No newline at end of file diff --git a/Core/src/main/java/net/juligames/core/data/HazelDataCore.java b/Core/src/main/java/net/juligames/core/data/HazelDataCore.java index d5039621..577a6071 100644 --- a/Core/src/main/java/net/juligames/core/data/HazelDataCore.java +++ b/Core/src/main/java/net/juligames/core/data/HazelDataCore.java @@ -10,9 +10,11 @@ import net.juligames.core.Core; import net.juligames.core.api.data.HazelDataApi; import net.juligames.core.api.hazel.NativeHazelDataAPI; +import net.juligames.core.api.misc.APIUtils; import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.Optional; /** * @author Ture Bentzin @@ -47,6 +49,11 @@ public final class HazelDataCore implements HazelDataApi, NativeHazelDataAPI { return getHazelcastInstance().getList(hazel); } + @Override + public boolean isMasterInformationAvailable() { + return getMasterInformation().containsKey("available"); + } + @Override public @NotNull ITopic getTopic(@NotNull String hazel) { return getHazelcastInstance().getTopic(hazel); @@ -57,5 +64,23 @@ public final class HazelDataCore implements HazelDataApi, NativeHazelDataAPI { return getHazelcastInstance().getDistributedObjects(); } + /** + * Retrieves an instance of the specified type that extends DistributedObject, with the given name. + * + * @param hazel the name of the distributed object to retrieve + * @param the type of the distributed object to retrieve + * @return an Optional containing the instance of the distributed object, if found; otherwise, an empty Optional + * @throws NullPointerException if the hazel parameter is null + */ + @Override + public @NotNull Optional get(@NotNull String hazel) { + //noinspection unchecked + return APIUtils.mapOrDrop(getAll() + .stream() + .filter(distributedObject -> + distributedObject.getName().equals(hazel)), + distributedObject -> (T) distributedObject).findAny(); + } + } diff --git a/Core/src/main/java/net/juligames/core/hcast/HazelConnector.java b/Core/src/main/java/net/juligames/core/hcast/HazelConnector.java index 293d3916..dbf1a38d 100644 --- a/Core/src/main/java/net/juligames/core/hcast/HazelConnector.java +++ b/Core/src/main/java/net/juligames/core/hcast/HazelConnector.java @@ -5,11 +5,14 @@ import com.hazelcast.config.Config; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; +import org.checkerframework.checker.optional.qual.MaybePresent; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -18,29 +21,29 @@ * 16.11.2022 */ public class HazelConnector { - private final String clientName; + private final @NotNull String clientName; - private final CompletableFuture instance = new CompletableFuture<>(); + private final @NotNull CompletableFuture instance = new CompletableFuture<>(); - private HazelConnector(String memberName) { - this.clientName = memberName; + private HazelConnector(@NotNull String clientName) { + this.clientName = clientName; } @Contract("_ -> new") - public static @NotNull HazelConnector getInstance(String name) { + public static @NotNull HazelConnector getInstance(@NotNull String name) { return new HazelConnector(name); } - public static HazelConnector getInstanceAndConnect(String name) { + public static @NotNull HazelConnector getInstanceAndConnect(@NotNull String name) { return new HazelConnector(name).connect(); } @ApiStatus.Internal - public static HazelConnector getInstanceAndConnectAsMember(String name) { + public static @NotNull HazelConnector getInstanceAndConnectAsMember(@NotNull String name) { return new HazelConnector(name).connectMember(); } - public HazelConnector connect() { + public @NotNull HazelConnector connect() { ClientConfig clientConfig = HCastConfigProvider.provide(clientName); HazelcastInstance hazelcastInstance = HazelcastClient.newHazelcastClient(clientConfig); instance.complete(hazelcastInstance); @@ -48,7 +51,7 @@ public HazelConnector connect() { } @ApiStatus.Internal - public HazelConnector connectMember() { + public @NotNull HazelConnector connectMember() { Config config = HCastConfigProvider.provideMember(clientName); config.getJetConfig().setEnabled(true); HazelcastInstance hazelcastInstance = Hazelcast.getOrCreateHazelcastInstance(config); @@ -64,13 +67,43 @@ public void disconnect() { } } + /** + * This method acts like {@link #disconnect()} but cant throw an {@link Exception} and does not wait for population of + * the {@link #instance} field! + * + * @return an Optional that might contain an {@link Exception} that was thrown while trying to kill hazelcast + */ + @ApiStatus.Internal + @MaybePresent + public @NotNull Optional kill() { + try { + Objects.requireNonNull(instance.getNow(null), "cant kill instance! Your cluster might be bricked now...") + .shutdown(); + return Optional.empty(); + } catch (Exception e) { + e.printStackTrace(); //print but do not propagate! + return Optional.of(e); + } + } + @ApiStatus.Internal @Nullable public HazelcastInstance getForce() { return getInstance().getNow(null); } - public CompletableFuture getInstance() { + public @NotNull CompletableFuture getInstance() { return instance; } + + /** + * Get the configured {@link #clientName}. + * This represents a memberName if this {@link HazelConnector} is used for members (masters) or a real clientName if it is + * used for creating members + * + * @return the clientName + */ + protected @NotNull String getClientName() { + return clientName; + } } diff --git a/Core/src/main/java/net/juligames/core/message/CoreMessageApi.java b/Core/src/main/java/net/juligames/core/message/CoreMessageApi.java index cfb949b9..a104d7bf 100644 --- a/Core/src/main/java/net/juligames/core/message/CoreMessageApi.java +++ b/Core/src/main/java/net/juligames/core/message/CoreMessageApi.java @@ -18,6 +18,8 @@ import java.security.InvalidParameterException; import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAmount; import java.util.*; import java.util.function.Function; import java.util.stream.Stream; @@ -106,7 +108,7 @@ public CoreMessageApi() { } @Override - public @NotNull CoreMessage getMessage(@NotNull String messageKey, @NotNull String locale, String... replacements) { + public @NotNull CoreMessage getMessage(@NotNull String messageKey, @NotNull String locale, @Nullable String... replacements) { CoreMessage message = getMessage(messageKey, locale); Core.getInstance().getCoreLogger().debug("inserting replacements: " + Arrays.toString(replacements) + " to " + message.getMiniMessage() + "@" + message.getMessageData().getMessageKey()); insertReplacements(message, replacements); @@ -514,7 +516,7 @@ public boolean hasMessage(@NotNull String messageKey, @NotNull DBLocale locale) Collection messages = new ArrayList<>(); for (String messageKey : messageKeys) { for (MessageRecipient messageRecipient : messageRecipients) { - CoreMessage message = getMessage(messageKey, messageRecipient.supplyLocaleOrDefault()); //no fallback?! oh, fuck this could get interesting | This may cause NullPointerException - let us hope it does not... + CoreMessage message = getMessage(messageKey, Objects.requireNonNull(messageRecipient.supplyLocaleOrDefault())); //no fallback?! oh, fuck this could get interesting | This may cause NullPointerException - let us hope it does not... insertReplacements(message, replacements); if (messages.stream().noneMatch(message1 -> message1.getMessageData().getMessageKey().equals(message.getMessageData().getMessageKey()))) { messages.add(message); @@ -621,6 +623,16 @@ public boolean hasMessage(@NotNull String messageKey, @NotNull DBLocale locale) return Date.from(Instant.now()); } + @Contract("_ -> new") + private @NotNull Date nowPlusThen(long fluxCompensator) { + return Date.from(Instant.now().plus(fluxCompensator, ChronoUnit.MILLIS)); + } + + @Contract("_ -> new") + private @NotNull Date nowPlusThen(TemporalAmount fluxCompensator) { + return Date.from(Instant.now().plus(fluxCompensator)); + } + /** * Support for the old replacement handling * @@ -639,7 +651,6 @@ public boolean hasMessage(@NotNull String messageKey, @NotNull DBLocale locale) Core.getInstance().getCoreLogger().debug("insert start: " + miniMessage); for (int i = 0; i < replacements.length; i++) { if (replacements[i] == null) replacements[i] = "null"; - //TODO replacements[i] = replacements[i].replace("","blank"); miniMessage = miniMessage.replace(buildPattern(i), replacements[i]); Core.getInstance().getCoreLogger().debug("insert step: " + i + " :" + miniMessage); } @@ -648,7 +659,7 @@ public boolean hasMessage(@NotNull String messageKey, @NotNull DBLocale locale) }; } - private void insertReplacements(CoreMessage coreMessage, String @NotNull ... replacements) { + private void insertReplacements(@NotNull CoreMessage coreMessage, @Nullable String... replacements) { Map map = new HashMap<>(); if (replacements == null) return; diff --git a/HazelAPI/pom.xml b/HazelAPI/pom.xml index d08ccce7..6124cdb7 100644 --- a/HazelAPI/pom.xml +++ b/HazelAPI/pom.xml @@ -6,7 +6,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -28,7 +28,7 @@ net.juligames.core API - 1.5 + 1.6 compile diff --git a/HazelAPI/src/main/java/net/juligames/core/api/hazel/NativeHazelDataAPI.java b/HazelAPI/src/main/java/net/juligames/core/api/hazel/NativeHazelDataAPI.java index 2854a9ad..46adf8f4 100644 --- a/HazelAPI/src/main/java/net/juligames/core/api/hazel/NativeHazelDataAPI.java +++ b/HazelAPI/src/main/java/net/juligames/core/api/hazel/NativeHazelDataAPI.java @@ -7,8 +7,10 @@ import com.hazelcast.map.IMap; import com.hazelcast.topic.ITopic; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import java.util.Collection; +import java.util.Optional; /** * @author Ture Bentzin @@ -66,4 +68,19 @@ public interface NativeHazelDataAPI { @ApiStatus.Experimental @ApiStatus.AvailableSince("1.5") Collection getAll(); + + /** + * Retrieves an instance of the specified type that extends DistributedObject, with the given name. + * This method can be used to automatically determine the type of hazel! + * The implementation might be changed over a version. + * This one should not be used outside user interface applications + * + * @param hazel hazel + * @param the implementation of {@link DistributedObject} + * @return the optional that may contain a {@link DistributedObject} + * @apiNote The implementation currently uses {@link #getAll()} and {@link java.util.function.Predicate}s to + * determine what will be returned. + */ + @ApiStatus.AvailableSince("1.6") + Optional get(@NotNull String hazel); } diff --git a/Master/dependency-reduced-pom.xml b/Master/dependency-reduced-pom.xml index b5fcafe4..e15127da 100644 --- a/Master/dependency-reduced-pom.xml +++ b/Master/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 Master diff --git a/Master/pom.xml b/Master/pom.xml index 54452e2e..7b01e112 100644 --- a/Master/pom.xml +++ b/Master/pom.xml @@ -6,7 +6,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -22,7 +22,7 @@ net.juligames.core Core - 1.5 + 1.6 org.jetbrains diff --git a/Master/src/main/java/net/juligames/core/master/CoreMaster.java b/Master/src/main/java/net/juligames/core/master/CoreMaster.java index 12967be6..cc925fde 100644 --- a/Master/src/main/java/net/juligames/core/master/CoreMaster.java +++ b/Master/src/main/java/net/juligames/core/master/CoreMaster.java @@ -35,23 +35,26 @@ private CoreMaster() { public static void main(String[] args) { + //Setup + MasterLogger.setupJavaLogging(); + logger = new MasterLogger("Master", java.util.logging.Logger.getLogger(Core.getShortRelease())); //entry point for Master masterCommandRunner = new MasterCommandRunner(logger); masterConfigManager = new MasterConfigManager(); - logger.info("welcome to " + Core.getFullCoreName() + " Master by Ture Bentzin "); + logger.info("Welcome to " + Core.getFullCoreName() + " Master by Ture Bentzin "); - logger.info("preparing directory..."); + logger.info("Preparing directory..."); masterConfigManager.createDatabaseConfiguration(); - logger.info("booting hazelcast (MEMBER CORE):"); + logger.info("Booting hazelcast (MEMBER CORE):"); Core core = new Core(); try { core.getHazelcastPostPreparationWorkers().register(hazelcastInstance -> { - logger.info("loading config"); + logger.info("Loading config"); masterConfigManager.load(); }); } catch (Registerator.DuplicateEntryException ignored) { @@ -67,11 +70,11 @@ public static void main(String[] args) { logger.error("FAILED TO SETUP HAZELCAST - Master will probably start anyway but the master should be restarted as soon as possible"); } - logger.info("hazelcast boot was completed - advice: hazelcast could potentially fail to boot for a variety of reasons, so if you should see" + + logger.info("Hazelcast boot was completed - advice: hazelcast could potentially fail to boot for a variety of reasons, so if you should see" + "an error above then you might want to restart the master. In the case that the Clients are put on hold by the core you should also" + "consider restarting."); - logger.debug("sql: start"); + logger.debug("SQL: start"); SQLManager = Core.getInstance().getSQLManager(); SQLManager.createTables(); //MASTER CREATES TABLES (NOT THE CORE!!!) @@ -83,7 +86,7 @@ public static void main(String[] args) { //Data masterHazelInformationProvider = new MasterHazelInformationProvider(hazelcast); - logger.warning("not all code execution on master is stable because the master DOES NOT PROVIDE a fully usable core!!!"); + logger.warning("Not all code execution on master is stable because the master DOES NOT PROVIDE a fully usable core!!!"); try { registerCommands(); } catch (Registerator.DuplicateEntryException e) { @@ -92,15 +95,15 @@ public static void main(String[] args) { } //CommandSystem - logger.info("registering CommandHandler..."); + logger.info("Registering CommandHandler..."); core.getCommandApi().setCommandHandler(new CommandHandler()); - logger.info("master is now ready to receive commands from hazelcast"); + logger.info("Master is now ready to receive commands from hazelcast"); //HOOK Core.getInstance().getJavaRuntime().addShutdownHook(new Thread(() -> { try { - logger.info("master is going down!!"); + logger.info("Master is going down!!"); } catch (Exception ignored) { System.out.println("Seems like everything is cursed right now. Please report this!"); } diff --git a/Master/src/main/java/net/juligames/core/master/cmd/PrintMapCommand.java b/Master/src/main/java/net/juligames/core/master/cmd/PrintMapCommand.java index 56fd2dcd..849ac93c 100644 --- a/Master/src/main/java/net/juligames/core/master/cmd/PrintMapCommand.java +++ b/Master/src/main/java/net/juligames/core/master/cmd/PrintMapCommand.java @@ -2,11 +2,16 @@ import com.hazelcast.core.DistributedObject; import com.hazelcast.map.IMap; +import de.bentzin.tools.logging.Logger; import net.juligames.core.Core; import net.juligames.core.api.TODO; import org.jetbrains.annotations.NotNull; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +import static net.juligames.core.api.misc.APIUtils.executeAndReturnFirstSuccess; /** * @author Ture Bentzin @@ -23,27 +28,59 @@ public PrintMapCommand() { public void executeCommand(@NotNull String commandString) { String[] s = commandString.replaceFirst(" ", "").split(" "); String ident = s[0]; - Optional optionalDistributedObject = - Core.getInstance().getOrThrow().getDistributedObjects().stream() - .filter(distributedObject -> distributedObject.getName().equals(ident)) - .findFirst(); - if (optionalDistributedObject.isEmpty()) { - throw new IllegalArgumentException("cant find: " + ident); - } - IMap map = Core.getInstance().getOrThrow().getMap(ident); - Core.getInstance().getCoreLogger().info("printing result of printMap: " + ident); - final boolean[] first = {true}; - Core.getInstance().getCoreLogger().warning("The here referenced classes may be inaccurate"); + final IMap map = findMap(ident); + Logger coreLogger = Core.getInstance().getCoreLogger(); + coreLogger.info("printing result of printMap: " + map.getName()); + + AtomicReference> keyClass = new AtomicReference<>(); + AtomicReference> valueClass = new AtomicReference<>(); map.forEach((o, o2) -> { - if (first[0]) { - first[0] = false; - Core.getInstance().getCoreLogger().info("keyClass: " + o.getClass().getSimpleName()); - Core.getInstance().getCoreLogger().info("valueClass: " + o2.getClass().getSimpleName()); + if (o == null || o2 == null) { + coreLogger.warning("FLAWED: " + o + " :! " + o2); + return; } - Core.getInstance().getCoreLogger().info(o.toString() + " : " + o2.toString()); + if (keyClass.get() != o.getClass() || valueClass.get() != o2.getClass()) { + coreLogger.info(o.getClass().getSimpleName() + " | " + o2.getClass().getSimpleName()); + keyClass.set(o.getClass()); + valueClass.set(o2.getClass()); + } + + coreLogger.info(o + " : " + o2); }); } + + + @SuppressWarnings("unchecked") + protected IMap findMap(String query) { + return (IMap) executeAndReturnFirstSuccess( + //Equal names + () -> findMapHard(query, distributedObject -> distributedObject.getName().equals(query)), + //Semi Equal names + () -> findMapHard(query, distributedObject -> distributedObject.getName().equalsIgnoreCase(query)), + //Starts with + () -> findMapHard(query, distributedObject -> distributedObject.getName().startsWith(query)), + //Starts with (semi) + () -> findMapHard(query, distributedObject -> distributedObject.getName().toLowerCase().startsWith(query.toLowerCase())), + //Contains + () -> findMapHard(query, distributedObject -> distributedObject.getName().contains(query)), + //Contains (semi) + () -> findMapHard(query, distributedObject -> distributedObject.getName().toLowerCase().contains(query.toLowerCase())) + + ).orElseThrow(() -> new IllegalArgumentException("cant find a map for query: \"" + query + "\"")); + } + + @SuppressWarnings("unchecked") + private @NotNull IMap findMapHard(String name, Predicate predicate) { + Optional optionalDistributedObject = + Core.getInstance().getOrThrow().getDistributedObjects().stream() + .filter(predicate) + .findFirst(); + if (optionalDistributedObject.isEmpty()) { + throw new IllegalArgumentException("cant find: " + name); + } + return (IMap) optionalDistributedObject.get(); + } } diff --git a/Master/src/main/java/net/juligames/core/master/data/MasterHazelInformationProvider.java b/Master/src/main/java/net/juligames/core/master/data/MasterHazelInformationProvider.java index c4bcfb89..c6933126 100644 --- a/Master/src/main/java/net/juligames/core/master/data/MasterHazelInformationProvider.java +++ b/Master/src/main/java/net/juligames/core/master/data/MasterHazelInformationProvider.java @@ -38,15 +38,16 @@ protected void genMap() { public void update() { //Locale DBLocale defaultEnglish = CoreSQLManager.defaultEnglish(); + informationMap().put("available", ""); informationMap().put("default_locale", defaultEnglish.getLocale()); informationMap().put("default_locale_description", defaultEnglish.getDescription()); informationMap().put("master_uuid", hazelcast.getLocalEndpoint().getUuid().toString()); informationMap().put("master_name", hazelcast.getName()); informationMap().put("master_boot", String.valueOf(CoreMaster.getBootMillis().orElseThrow())); informationMap().put("master_version", API.get().getVersion()); - informationMap().put("last_update", System.currentTimeMillis() + ""); + informationMap().put("last_update", String.valueOf(System.currentTimeMillis())); //Experimental - informationMap().put("master_commands", CoreMaster.getMasterCommandRunner().getIndex() + ""); + informationMap().put("master_commands", String.valueOf(CoreMaster.getMasterCommandRunner().getIndex())); informationMap().put("hazelcast_version", VersionFinder.getVersion()); informationMap().put("master_os", VersionFinder.OS.name()); informationMap().put("java_version", VersionFinder.JAVA_VERSION); diff --git a/Master/src/main/java/net/juligames/core/master/logging/MasterLogger.java b/Master/src/main/java/net/juligames/core/master/logging/MasterLogger.java index 53ee208f..70661d35 100644 --- a/Master/src/main/java/net/juligames/core/master/logging/MasterLogger.java +++ b/Master/src/main/java/net/juligames/core/master/logging/MasterLogger.java @@ -2,6 +2,7 @@ import de.bentzin.tools.logging.JavaLogger; import de.bentzin.tools.logging.Logger; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import java.time.LocalDateTime; @@ -25,6 +26,11 @@ public MasterLogger(String name, java.util.logging.@NotNull Logger logger) { logger.addHandler(LogFileHandlerManager.generateFileHandler()); } + @ApiStatus.AvailableSince("1.6") + public static void setupJavaLogging() { + System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT] [%4$-7s] %5$s %n"); + } + @Override public void log(String message, @NotNull LogLevel logLevel) { super.log(timeAndDate(message), logLevel); diff --git a/MiniGameAPI/pom.xml b/MiniGameAPI/pom.xml index 8fce11d3..83a2169d 100644 --- a/MiniGameAPI/pom.xml +++ b/MiniGameAPI/pom.xml @@ -6,7 +6,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -22,7 +22,7 @@ net.juligames.core PaperAPI - 1.5 + 1.6 provided diff --git a/MiniGameAPI/src/main/java/net/juligames/core/minigame/api/team/procedures/InsertionProcedure.java b/MiniGameAPI/src/main/java/net/juligames/core/minigame/api/team/procedures/InsertionProcedure.java index 58b9dd1d..eecfb4cb 100644 --- a/MiniGameAPI/src/main/java/net/juligames/core/minigame/api/team/procedures/InsertionProcedure.java +++ b/MiniGameAPI/src/main/java/net/juligames/core/minigame/api/team/procedures/InsertionProcedure.java @@ -7,6 +7,13 @@ import java.util.function.BiFunction; /** + * A sealed interface that defines the insertion procedure for adding players to teams. + * The method {@code apply()} should return {@code true} if the player was successfully inserted into a team, + * and {@code false} if the insertion failed for any reason. + *

+ * You can use {@link ConsumerInsertionProcedure} to make a custom implementation (at the time) - this system is subjet + * to change but i will try to not break compatibility + * * @author Ture Bentzin * 18.12.2022 */ diff --git a/MiniGameAPI/src/main/java/net/juligames/core/util/ShuffleUtil.java b/MiniGameAPI/src/main/java/net/juligames/core/util/ShuffleUtil.java index d87b2194..24b7ecd4 100644 --- a/MiniGameAPI/src/main/java/net/juligames/core/util/ShuffleUtil.java +++ b/MiniGameAPI/src/main/java/net/juligames/core/util/ShuffleUtil.java @@ -25,7 +25,6 @@ public final class ShuffleUtil { ); private ShuffleUtil() { - } @SuppressWarnings("unchecked") diff --git a/PaperAPI/pom.xml b/PaperAPI/pom.xml index b529d78b..e3ce7f2c 100644 --- a/PaperAPI/pom.xml +++ b/PaperAPI/pom.xml @@ -1,12 +1,18 @@ + JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -28,7 +34,7 @@ net.juligames.core AdventureAPI - 1.5 + 1.6 compile diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/PaperConversationManager.java b/PaperAPI/src/main/java/net/juligames/core/paper/PaperConversationManager.java new file mode 100644 index 00000000..3028199b --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/PaperConversationManager.java @@ -0,0 +1,36 @@ +package net.juligames.core.paper; + +import de.bentzin.conversationlib.Converser; +import de.bentzin.conversationlib.manager.ConversationManager; +import net.kyori.adventure.audience.Audience; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +public class PaperConversationManager extends ConversationManager { + + private static PaperConversationManager instance; + + public PaperConversationManager(@NotNull Map audienceConverserMap) { + super(audienceConverserMap); + if (instance != null) { + throw new IllegalStateException("instance is already present!"); + } + instance = this; + } + + public PaperConversationManager() { + if (instance != null) { + throw new IllegalStateException("instance is already present!"); + } + instance = this; + } + + public static PaperConversationManager getInstance() { + return instance; + } +} \ No newline at end of file diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/PaperMessageRecipient.java b/PaperAPI/src/main/java/net/juligames/core/paper/PaperMessageRecipient.java index 352d472c..011bf8e1 100644 --- a/PaperAPI/src/main/java/net/juligames/core/paper/PaperMessageRecipient.java +++ b/PaperAPI/src/main/java/net/juligames/core/paper/PaperMessageRecipient.java @@ -10,6 +10,9 @@ import org.jetbrains.annotations.Nullable; /** + * Represents a message recipient on a Paper server, which is capable of receiving messages + * through the Bukkit {@link CommandSender} interface. + * * @author Ture Bentzin * 19.11.2022 */ @@ -17,40 +20,57 @@ public class PaperMessageRecipient implements MessageRecipient { private final CommandSender commandSender; + /** + * Constructs a new {@code PaperMessageRecipient} with the specified {@link CommandSender}. + * + * @param commandSender the Bukkit {@link CommandSender} for this recipient + */ public PaperMessageRecipient(CommandSender commandSender) { this.commandSender = commandSender; } /** - * @return A human-readable name that defines this recipient + * Returns a human-readable name that defines this recipient. + * + * @return the name of this recipient, which is equivalent to the name of the Bukkit {@link CommandSender} */ @Override public @NotNull String getName() { return commandSender.getName(); } + /** + * Delivers a message to this recipient using the Adventure API. + * + * @param message the {@link Message} to be delivered + */ @Override public void deliver(@NotNull Message message) { commandSender.sendMessage(AdventureAPI.get().getAdventureTagManager().resolve(message)); } /** - * delivers a miniMessage string to the recipient + * Delivers a miniMessage string to this recipient using the Adventure API. * - * @param miniMessage the miniMessage to deliver to the commandSender + * @param miniMessage the miniMessage to be delivered to the Bukkit {@link CommandSender} */ @Override public void deliver(@NotNull String miniMessage) { - //Component resolve = Core.getInstance().getMessageApi().getTagManager().resolve(miniMessage); Component resolve = AdventureAPI.get().getAdventureTagManager().resolve(miniMessage); commandSender.sendMessage(resolve); } + /** + * Returns the locale of the player associated with this recipient, if applicable. + * + * @return the locale string of the player, or {@code null} if the recipient is not a player + */ @Override public @Nullable String supplyLocale() { if (commandSender instanceof Player player) { return player.locale().toString(); - } else + } else { return null; + } } -} +} \ No newline at end of file diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/inventory/InventoryConfigWriter.java b/PaperAPI/src/main/java/net/juligames/core/paper/inventory/InventoryConfigWriter.java index b3028ffb..a35ba2ca 100644 --- a/PaperAPI/src/main/java/net/juligames/core/paper/inventory/InventoryConfigWriter.java +++ b/PaperAPI/src/main/java/net/juligames/core/paper/inventory/InventoryConfigWriter.java @@ -11,19 +11,43 @@ import java.util.Collection; /** - * @author Ture Bentzin - * 23.01.2023 + * This class represents a writer for Bukkit inventories that can be serialized into configurations. + * It implements the ConfigWriter interface and provides methods for populating and writing an inventory to a configuration. + *

+ * The class uses an Interpreter to convert ItemStacks to their serialized form, and vice versa. + *

+ * + * @author Ture Bentzin 23.01.2023 */ public record InventoryConfigWriter(Inventory inventory) implements ConfigWriter { + /** + * The interpreter used to convert ItemStacks to their serialized form. + */ @SuppressWarnings("ProtectedMemberInFinalClass") protected static final @NotNull Interpreter itemStackInterpreter = new ItemStackInterpreter(); + /** + * Populates an inventory with items from the given configuration. + * + * @param configuration the configuration to read items from + * @param keyspace the keyspace in the configuration to read items from + * @param inventory the inventory to populate + */ public static void populateInventory(@NotNull Configuration configuration, @NotNull String keyspace, @NotNull Inventory inventory) { Collection collection = configuration.getCollection(keyspace, itemStackInterpreter); inventory.setContents(collection.toArray(new ItemStack[0])); } + /** + * Populates an inventory with items from the given configuration and returns the inventory. + *

+ * + * @param configuration the configuration to read items from + * @param keyspace the keyspace in the configuration to read items from + * @param inventory the inventory to populate + * @return the populated inventory + */ @ApiStatus.Experimental public static synchronized @NotNull Inventory populateAndReturnInventory(@NotNull Configuration configuration, @NotNull String keyspace, @NotNull Inventory inventory) { @@ -31,6 +55,12 @@ public static void populateInventory(@NotNull Configuration configuration, @NotN return inventory; } + /** + * Writes the inventory to the given configuration. + * + * @param configuration the configuration to write the inventory to + * @param keyspace the keyspace in the configuration to write the inventory to + */ @Override public void write(@NotNull Configuration configuration, @NotNull String keyspace) { configuration.setIterable(keyspace, inventory, itemStackInterpreter); diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/misc/configmapping/Autofill.java b/PaperAPI/src/main/java/net/juligames/core/paper/misc/configmapping/Autofill.java new file mode 100644 index 00000000..bfd2ebf0 --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/misc/configmapping/Autofill.java @@ -0,0 +1,14 @@ +package net.juligames.core.paper.misc.configmapping; + +import org.jetbrains.annotations.ApiStatus; + +/** + * @author Ture Bentzin + * 14.04.2023 + */ +@ApiStatus.AvailableSince("1.6") +public @interface Autofill { + String defaultName = ""; + + String customName() default defaultName; +} diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/misc/configmapping/ObjectifiedConfiguration.java b/PaperAPI/src/main/java/net/juligames/core/paper/misc/configmapping/ObjectifiedConfiguration.java new file mode 100644 index 00000000..db805879 --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/misc/configmapping/ObjectifiedConfiguration.java @@ -0,0 +1,246 @@ +package net.juligames.core.paper.misc.configmapping; + +import net.juligames.core.api.API; +import net.juligames.core.api.config.representations.Representation; +import org.bukkit.configuration.Configuration; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Field; +import java.net.URI; +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Consumer; + +/** + * This class provides a convenient way to map configuration files to Java classes in your plugins. + * When you create a subclass of {@link ObjectifiedConfiguration}, you can add your configuration variables as fields to the class, + * and they will be automatically populated with the execution of the constructor {@link #ObjectifiedConfiguration(ConfigurationSection)}. + * You need to annotate the fields you would like to be automatically filled with @{@link Autofill}. + *

+ * The @{@link Autofill} annotation is used to mark fields that should be automatically populated with values from the configuration file. + * The {@link ObjectifiedConfiguration} class uses reflection to look up the fields in the subclass and then populates + * them with the appropriate values from the associated {@link ConfigurationSection}. + *

+ * The ObjectifiedConfiguration class provides methods to save and reload the associated configuration file. + * The {@link #save()} method saves the current values of the fields to the associated {@link ConfigurationSection}. + * The {@link #reload()} method reads the current values of the fields from the associated {@link ConfigurationSection}. + *

+ * Example implementation: + * + *

{@code
+ * package com.example.plugin;
+ *
+ * import org.bukkit.configuration.ConfigurationSection;
+ * import net.juligames.core.paper.misc.configmapping.Autofill;
+ * import net.juligames.core.paper.misc.configmapping.ObjectifiedConfiguration;
+ *
+ * public class MyConfig extends ObjectifiedConfiguration {
+ *
+ *     @Autofill
+ *     public String databaseUsername = "defaultUsername";
+ *
+ *     @Autofill
+ *     public String databasePassword = "defaultPassword";
+ *
+ *     public MyConfig(ConfigurationSection section) {
+ *         super(section);
+ *     }
+ *
+ * }
+ * }
+ *

+ * In this example, the {@code MyConfig} class extends {@link ObjectifiedConfiguration} and has two fields marked with the @{@link Autofill} + * annotation. These fields will be automatically populated with values from the associated configuration file. + * You can also manually {@link #save()} or {@link #reload()} the config. + * + * @author Ture Bentzin + * 14.04.2023 + */ +@ApiStatus.Experimental +@ApiStatus.AvailableSince("1.6") +public class ObjectifiedConfiguration { + + private final @NotNull ConfigurationSection associatedSection; + + /** + * Constructs an ObjectifiedConfiguration object with an associated ConfigurationSection. + * Calls the {@link #reload()} method to populate fields with values from the ConfigurationSection. + * + * @param associatedSection the ConfigurationSection to associate with this ObjectifiedConfiguration + */ + public ObjectifiedConfiguration(@NotNull ConfigurationSection associatedSection) { + this.associatedSection = associatedSection; + reload(); + } + + /** + * Constructs an ObjectifiedConfiguration object with an associated File. + * Calls the {@link #reload()} method to populate fields with values from the FileConfiguration. + * + * @param ymlFile the File to associate with this ObjectifiedConfiguration + */ + public ObjectifiedConfiguration(@NotNull File ymlFile) { + this(YamlConfiguration.loadConfiguration(ymlFile)); + } + + /** + * Constructs an ObjectifiedConfiguration object with an associated Reader. + * Calls the {@link #reload()} method to populate fields with values from the FileConfiguration. + * + * @param reader the Reader to associate with this ObjectifiedConfiguration + */ + public ObjectifiedConfiguration(@NotNull Reader reader) { + this(YamlConfiguration.loadConfiguration(reader)); + } + + /** + * Constructs an ObjectifiedConfiguration object with an associated String data and section name. + * If the section name is null, creates a ConfigurationSection using the loaded data. + * Calls the {@link #reload()} method to populate fields with values from the ConfigurationSection. + * + * @param data the data to associate with this ObjectifiedConfiguration + * @param section the section name to associate with this ObjectifiedConfiguration, can be null + * @throws InvalidConfigurationException if the loaded data is invalid + */ + public ObjectifiedConfiguration(@NotNull String data, @Nullable String section) throws InvalidConfigurationException { + YamlConfiguration configuration = new YamlConfiguration(); + configuration.loadFromString(data); + this.associatedSection = section == null ? configuration : configuration.createSection(section); + } + + /** + * Constructs an ObjectifiedConfiguration object with an associated path String. + * Calls the {@link #ObjectifiedConfiguration(File)} constructor to associate the File with this ObjectifiedConfiguration. + * + * @param path the path of the File to associate with this ObjectifiedConfiguration + */ + public ObjectifiedConfiguration(@NotNull String path) { + this(new File(path)); + } + + /** + * Constructs an ObjectifiedConfiguration object with an associated URI. + * Calls the {@link #ObjectifiedConfiguration(File)} constructor to associate the File with this ObjectifiedConfiguration. + * + * @param path the URI of the File to associate with this ObjectifiedConfiguration + */ + public ObjectifiedConfiguration(@NotNull URI path) { + this(new File(path)); + } + + /** + * Constructs an ObjectifiedConfiguration object with an associated ConfigurationSection obtained from a representation. + * Calls the {@link #reload()} method to populate fields with values from the ConfigurationSection. + * + * @param sectionRepresentation the representation of the ConfigurationSection to associate with this ObjectifiedConfiguration + */ + public ObjectifiedConfiguration(@NotNull Representation sectionRepresentation) { + this(sectionRepresentation.represent()); + } + + + public final @NotNull ConfigurationSection getAssociatedSection() { + return associatedSection; + } + + public void reload() { + Arrays.stream(this.getClass().getFields()).forEachOrdered(this::reload); + } + + public void save() { + Arrays.stream(this.getClass().getFields()).forEachOrdered(this::save); + } + + /** + * @param file file to save to + * @throws IOException if saving fails fatally + * @apiNote This method is not available with standalone! + */ + @ApiStatus.Experimental + public void save(File file) throws IOException { + save(); + Configuration root = associatedSection.getRoot(); + if (root == null) { + API.get().getAPILogger().warning("cant save " + this.getClass().getSimpleName() + " because associatedSection does not" + + " belong to any Configuration"); + return; + } + if (root instanceof FileConfiguration fileConfiguration) { + fileConfiguration.save(file); + } else { + API.get().getAPILogger().warning("cant save " + this.getClass().getSimpleName() + " because associatedSection does not" + + " belong to a FileConfiguration! :: " + root.getClass().getSimpleName()); + } + } + + @ApiStatus.Experimental + public void save(Consumer saver) { + save(); + Configuration root = associatedSection.getRoot(); + if (root == null) { + API.get().getAPILogger().warning("cant save " + this.getClass().getSimpleName() + " because associatedSection does not" + + " belong to any Configuration"); + return; + } + saver.accept((C) root); + } + + public void save(@NotNull Field field) { + if (field.getDeclaringClass().equals(this.getClass())) { + String key = getKeyForField(field); + //time to set the value + try { + getAssociatedSection().set(key, field.get(this)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + + } + } + + private String getKeyForField(@NotNull Field field) { + return + Arrays.stream( + field.getAnnotations()) + .filter(annotation -> annotation.annotationType().equals(Autofill.class)) + .map(annotation -> (Autofill) annotation) + .findFirst() + .flatMap(annotation -> { + if (!annotation.customName().equals(Autofill.defaultName)) + return annotation.customName().describeConstable(); + return Optional.empty(); + }).orElse(field.getName()); + } + + protected final void reload(@NotNull Field field) { + if (field.getDeclaringClass().equals(this.getClass())) { + String key = getKeyForField(field); + //check for default + try { + //time to get the value from section + field.set(this, extractDefault(field) + .map(o1 -> getAssociatedSection() + .get(key, o1)) + .orElseGet(() -> getAssociatedSection() + .get(key))); + + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + protected final Optional extractDefault(@NotNull Field field) throws IllegalAccessException { + final Object o = field.get(this); + return Optional.ofNullable(o); + } +} diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/plugin/CorePlugin.java b/PaperAPI/src/main/java/net/juligames/core/paper/plugin/CorePlugin.java index 90befe9e..bdb677bd 100644 --- a/PaperAPI/src/main/java/net/juligames/core/paper/plugin/CorePlugin.java +++ b/PaperAPI/src/main/java/net/juligames/core/paper/plugin/CorePlugin.java @@ -56,6 +56,7 @@ protected CorePlugin(@NotNull final CorePluginLoader loader, @NotNull final Plug throw new IllegalStateException("Cannot use initialization constructor at runtime"); } init(loader, loader.server, description, dataFolder, file, classLoader); + } /** diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/prompt/MaterialPrompt.java b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/MaterialPrompt.java new file mode 100644 index 00000000..ccec998b --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/MaterialPrompt.java @@ -0,0 +1,31 @@ +package net.juligames.core.paper.prompt; + +import de.bentzin.conversationlib.ConversationContext; +import de.bentzin.conversationlib.prompt.Prompt; +import de.bentzin.conversationlib.prompt.ValidatingPrompt; +import org.bukkit.Material; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class MaterialPrompt extends ValidatingPrompt { + @Override + protected boolean isInputValid(@NotNull ConversationContext context, @NotNull String input) { + return Material.getMaterial(input) != null; + } + + @Override + protected @Nullable Prompt acceptValidatedInput(@NotNull ConversationContext context, @NotNull String input) { + return acceptMaterialInput(context, Objects.requireNonNull(Material.getMaterial(input))); + } + + public abstract Prompt acceptMaterialInput(@NotNull ConversationContext context, @NotNull Material material); + +} diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PlayerPrompt.java b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PlayerPrompt.java new file mode 100644 index 00000000..e9499ea9 --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PlayerPrompt.java @@ -0,0 +1,28 @@ +package net.juligames.core.paper.prompt; + +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class PlayerPrompt extends FixedSetPrompt implements ServerPrompt { + private final Server server; + + public PlayerPrompt(@NotNull Server server) { + super(server::getPlayer, Collections.unmodifiableCollection(server.getOnlinePlayers())); + this.server = server; + } + + @Override + public Server getServer() { + return server; + } +} diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PluginManagerPrompt.java b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PluginManagerPrompt.java new file mode 100644 index 00000000..fac8bbbf --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PluginManagerPrompt.java @@ -0,0 +1,16 @@ +package net.juligames.core.paper.prompt; + +import de.bentzin.conversationlib.prompt.Prompt; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public interface PluginManagerPrompt extends Prompt { + + @NotNull PluginManager getPluginManager(); +} diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PluginPrompt.java b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PluginPrompt.java new file mode 100644 index 00000000..fbb3291d --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/PluginPrompt.java @@ -0,0 +1,26 @@ +package net.juligames.core.paper.prompt; + +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class PluginPrompt extends FixedSetPrompt implements PluginManagerPrompt { + private final PluginManager pluginManager; + + public PluginPrompt(@NotNull PluginManager pluginManager) { + super(pluginManager::getPlugin, pluginManager.getPlugins()); + this.pluginManager = pluginManager; + } + + @Override + public @NotNull PluginManager getPluginManager() { + return pluginManager; + } +} \ No newline at end of file diff --git a/PaperAPI/src/main/java/net/juligames/core/paper/prompt/ServerPrompt.java b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/ServerPrompt.java new file mode 100644 index 00000000..25bd81a9 --- /dev/null +++ b/PaperAPI/src/main/java/net/juligames/core/paper/prompt/ServerPrompt.java @@ -0,0 +1,15 @@ +package net.juligames.core.paper.prompt; + +import de.bentzin.conversationlib.prompt.Prompt; +import org.bukkit.Server; +import org.jetbrains.annotations.ApiStatus; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public interface ServerPrompt extends Prompt { + + Server getServer(); +} diff --git a/PaperCore/dependency-reduced-pom.xml b/PaperCore/dependency-reduced-pom.xml index ca50dabb..7b5ba6d8 100644 --- a/PaperCore/dependency-reduced-pom.xml +++ b/PaperCore/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 PaperCore diff --git a/PaperCore/pom.xml b/PaperCore/pom.xml index 7fb7f88f..c55b8158 100644 --- a/PaperCore/pom.xml +++ b/PaperCore/pom.xml @@ -6,7 +6,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -16,19 +16,19 @@ net.juligames.core PaperAPI - 1.5 + 1.6 compile net.juligames.core Core - 1.5 + 1.6 compile net.juligames.core AdventureCore - 1.5 + 1.6 compile diff --git a/PaperCore/src/main/java/net/juligames/core/paper/PaperCorePlugin.java b/PaperCore/src/main/java/net/juligames/core/paper/PaperCorePlugin.java index 2d3e7da7..a17952dc 100644 --- a/PaperCore/src/main/java/net/juligames/core/paper/PaperCorePlugin.java +++ b/PaperCore/src/main/java/net/juligames/core/paper/PaperCorePlugin.java @@ -9,6 +9,7 @@ import net.juligames.core.api.minigame.BasicMiniGame; import net.juligames.core.caching.MessageCaching; import net.juligames.core.paper.bstats.Metrics; +import net.juligames.core.paper.conversation.ConversationListener; import net.juligames.core.paper.events.ServerBootFinishedEvent; import net.juligames.core.paper.minigame.StartCommand; import net.juligames.core.paper.notification.EventNotificationListener; @@ -58,6 +59,7 @@ public void onEnable() { getLogger().info("starting adventureCore v." + AdventureCore.API_VERSION); adventureCore = new AdventureCore(); adventureCore.start(); + new PaperConversationManager(); core.setOnlineRecipientProvider(() -> { List paperMessageRecipients = new java.util.ArrayList<>(Bukkit.getOnlinePlayers().stream().map(PaperMessageRecipient::new).toList()); paperMessageRecipients.add(new PaperMessageRecipient(Bukkit.getConsoleSender())); @@ -76,6 +78,7 @@ public void onEnable() { Objects.requireNonNull(getCommand("printMessageCache")).setExecutor(new PrintMessageCacheCommand()); Bukkit.getPluginManager().registerEvents(new PaperCoreEventListener(), this); + Bukkit.getPluginManager().registerEvents(new ConversationListener(), this); //Register NotificationEvent core.getNotificationApi().registerListener(new EventNotificationListener()); diff --git a/PaperCore/src/main/java/net/juligames/core/paper/conversation/ConversationListener.java b/PaperCore/src/main/java/net/juligames/core/paper/conversation/ConversationListener.java new file mode 100644 index 00000000..e3a44c90 --- /dev/null +++ b/PaperCore/src/main/java/net/juligames/core/paper/conversation/ConversationListener.java @@ -0,0 +1,35 @@ +package net.juligames.core.paper.conversation; + +import net.juligames.core.Core; +import net.juligames.core.paper.PaperConversationManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.server.ServerCommandEvent; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +public class ConversationListener implements Listener { + + @SuppressWarnings("deprecation") + @EventHandler + public void onChat(@NotNull AsyncPlayerChatEvent event) { + //Player + if (PaperConversationManager.getInstance().handleInput(event.getPlayer(), event.getMessage())) { + Core.getInstance().getCoreLogger().debug("handled input for conversing: " + event.getPlayer().getName() + ": " + event.getMessage()); + event.setCancelled(true); + } + } + + @EventHandler + public void onCommand(@NotNull ServerCommandEvent serverCommandEvent) { + if (PaperConversationManager.getInstance().handleInput(serverCommandEvent.getSender(), + serverCommandEvent.getCommand())) { + serverCommandEvent.setCancelled(true); + } + } + +} diff --git a/SuperCore.iml b/SuperCore.iml new file mode 100644 index 00000000..c4622759 --- /dev/null +++ b/SuperCore.iml @@ -0,0 +1,12 @@ + + + + + + + ADVENTURE + + + + + \ No newline at end of file diff --git a/SuperCore/SUPERCORE b/SuperCore/SUPERCORE new file mode 100644 index 00000000..e69de29b diff --git a/SuperCore/SuperCore.iml b/SuperCore/SuperCore.iml new file mode 100644 index 00000000..c4622759 --- /dev/null +++ b/SuperCore/SuperCore.iml @@ -0,0 +1,12 @@ + + + + + + + ADVENTURE + + + + + \ No newline at end of file diff --git a/SuperCore/pom.xml b/SuperCore/pom.xml new file mode 100644 index 00000000..853cac7a --- /dev/null +++ b/SuperCore/pom.xml @@ -0,0 +1,90 @@ + + + 4.0.0 + + net.juligames.core + JuliGamesCore + 1.6 + + + SuperCore + + + 17 + 17 + UTF-8 + + + + + + net.juligames.core + Master + 1.6 + + + net.juligames.core + PaperCore + 1.6 + + + net.juligames.core + VelocityCore + 1.6 + + + net.juligames.core + MiniGameAPI + 1.6 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + SuperCore-${project.version}-EXPERIMENTAL + false + + + + package + + shade + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + true + net.juligames.core.master.CoreMaster + + + + + + + + + + + + juligames + Juligames Maven + https://maven.juligames.net/juligames + + + + \ No newline at end of file diff --git a/VelocityAPI/pom.xml b/VelocityAPI/pom.xml index afdfeaf7..e459a66b 100644 --- a/VelocityAPI/pom.xml +++ b/VelocityAPI/pom.xml @@ -1,13 +1,18 @@ - + JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -30,7 +35,7 @@ net.juligames.core AdventureAPI - 1.5 + 1.6 compile diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/VelocityConversationManager.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/VelocityConversationManager.java new file mode 100644 index 00000000..c83f08ea --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/VelocityConversationManager.java @@ -0,0 +1,39 @@ +package net.juligames.core.velocity; + +import de.bentzin.conversationlib.Converser; +import de.bentzin.conversationlib.manager.ConversationManager; +import net.juligames.core.api.TODO; +import net.kyori.adventure.audience.Audience; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * @author Ture Bentzin + * 25.03.2023 + * @apiNote This currently does not support automatic input insertion! + */ +@TODO +public class VelocityConversationManager extends ConversationManager { + + private static VelocityConversationManager instance; + + public VelocityConversationManager(@NotNull Map audienceConverserMap) { + super(audienceConverserMap); + if (instance != null) { + throw new IllegalStateException("instance is already present!"); + } + instance = this; + } + + public VelocityConversationManager() { + if (instance != null) { + throw new IllegalStateException("instance is already present!"); + } + instance = this; + } + + public static VelocityConversationManager getInstance() { + return instance; + } +} diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PartialPlayerPrompt.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PartialPlayerPrompt.java new file mode 100644 index 00000000..a3798224 --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PartialPlayerPrompt.java @@ -0,0 +1,37 @@ +package net.juligames.core.velocity.prompt; + +import com.velocitypowered.api.plugin.PluginManager; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Collections; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class PartialPlayerPrompt extends FixedSetPrompt> implements ProxyServerPrompt, PluginManagerPrompt { + + private final ProxyServer proxyServer; + + public PartialPlayerPrompt(@NotNull ProxyServer proxyServer) { + super(s -> Collections.singleton(proxyServer.matchPlayer(s).stream().findFirst().orElse(null)), + proxyServer.getAllPlayers()); + this.proxyServer = proxyServer; + } + + @Override + public ProxyServer getProxy() { + return proxyServer; + } + + @Override + public @NotNull PluginManager getPluginManager() { + return getProxy().getPluginManager(); + } +} diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PlayerPrompt.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PlayerPrompt.java new file mode 100644 index 00000000..0bf379d8 --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PlayerPrompt.java @@ -0,0 +1,33 @@ +package net.juligames.core.velocity.prompt; + +import com.velocitypowered.api.plugin.PluginManager; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class PlayerPrompt extends FixedSetPrompt implements ProxyServerPrompt, PluginManagerPrompt { + + private final ProxyServer proxyServer; + + public PlayerPrompt(@NotNull ProxyServer proxyServer) { + super(s -> proxyServer.getPlayer(s).orElse(null), proxyServer.getAllPlayers()); + this.proxyServer = proxyServer; + } + + @Override + public ProxyServer getProxy() { + return proxyServer; + } + + @Override + public @NotNull PluginManager getPluginManager() { + return getProxy().getPluginManager(); + } +} diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PluginManagerPrompt.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PluginManagerPrompt.java new file mode 100644 index 00000000..67f942d3 --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PluginManagerPrompt.java @@ -0,0 +1,16 @@ +package net.juligames.core.velocity.prompt; + +import com.velocitypowered.api.plugin.PluginManager; +import de.bentzin.conversationlib.prompt.Prompt; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public interface PluginManagerPrompt extends Prompt { + + @NotNull PluginManager getPluginManager(); +} diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PluginPrompt.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PluginPrompt.java new file mode 100644 index 00000000..6aefe390 --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/PluginPrompt.java @@ -0,0 +1,28 @@ +package net.juligames.core.velocity.prompt; + +import com.velocitypowered.api.plugin.PluginContainer; +import com.velocitypowered.api.proxy.ProxyServer; +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class PluginPrompt extends FixedSetPrompt implements ProxyServerPrompt { + + private final ProxyServer proxyServer; + + public PluginPrompt(@NotNull ProxyServer proxyServer) { + super(s -> proxyServer.getPluginManager().getPlugin(s).orElse(null), + proxyServer.getPluginManager().getPlugins()); + this.proxyServer = proxyServer; + } + + @Override + public ProxyServer getProxy() { + return proxyServer; + } +} \ No newline at end of file diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/ProxyServerPrompt.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/ProxyServerPrompt.java new file mode 100644 index 00000000..fbfea51c --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/ProxyServerPrompt.java @@ -0,0 +1,14 @@ +package net.juligames.core.velocity.prompt; + +import com.velocitypowered.api.proxy.ProxyServer; +import de.bentzin.conversationlib.prompt.Prompt; +import org.jetbrains.annotations.ApiStatus; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public interface ProxyServerPrompt extends Prompt { + ProxyServer getProxy(); +} diff --git a/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/ServerPrompt.java b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/ServerPrompt.java new file mode 100644 index 00000000..6a24dc69 --- /dev/null +++ b/VelocityAPI/src/main/java/net/juligames/core/velocity/prompt/ServerPrompt.java @@ -0,0 +1,27 @@ +package net.juligames.core.velocity.prompt; + +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import de.bentzin.conversationlib.prompt.FixedSetPrompt; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * @author Ture Bentzin + * 25.03.2023 + */ +@ApiStatus.AvailableSince("1.6") +public abstract class ServerPrompt extends FixedSetPrompt implements ProxyServerPrompt { + + private final ProxyServer proxyServer; + + public ServerPrompt(@NotNull ProxyServer proxyServer) { + super(s -> proxyServer.getServer(s).orElse(null), proxyServer.getAllServers()); + this.proxyServer = proxyServer; + } + + @Override + public ProxyServer getProxy() { + return proxyServer; + } +} diff --git a/VelocityCore/dependency-reduced-pom.xml b/VelocityCore/dependency-reduced-pom.xml index bdd4a20c..44cd80c5 100644 --- a/VelocityCore/dependency-reduced-pom.xml +++ b/VelocityCore/dependency-reduced-pom.xml @@ -4,7 +4,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 VelocityCore diff --git a/VelocityCore/pom.xml b/VelocityCore/pom.xml index 0cbb5179..27d70dfa 100644 --- a/VelocityCore/pom.xml +++ b/VelocityCore/pom.xml @@ -6,7 +6,7 @@ JuliGamesCore net.juligames.core - 1.5 + 1.6 4.0.0 @@ -22,7 +22,7 @@ net.juligames.core VelocityAPI - 1.5 + 1.6 compile @@ -34,13 +34,13 @@ net.juligames.core Core - 1.5 + 1.6 compile net.juligames.core AdventureCore - 1.5 + 1.6 compile diff --git a/VelocityCore/src/main/java/net/juligames/core/velocity/VelocityCore.java b/VelocityCore/src/main/java/net/juligames/core/velocity/VelocityCore.java index 577c53da..d6e4f0c0 100644 --- a/VelocityCore/src/main/java/net/juligames/core/velocity/VelocityCore.java +++ b/VelocityCore/src/main/java/net/juligames/core/velocity/VelocityCore.java @@ -23,7 +23,7 @@ * 03.12.2022 */ -@Plugin(id = "velocitycore", name = "JuliGames Velocity Core", version = "1.5", +@Plugin(id = "velocitycore", name = "JuliGames Velocity Core", version = "1.6", url = "https://github.com/JuliGames/JuliGamesCore", description = "Velocity Client for the core - necessary to provide the API here", authors = {"Ture Bentzin"}) public final class VelocityCore { @@ -65,6 +65,8 @@ public void onProxyInitialization(ProxyInitializeEvent event) { adventureCore = new AdventureCore(); adventureCore.start(); + new VelocityConversationManager(); + core.setOnlineRecipientProvider(() -> { Collection recipients = new ArrayList<>(); //1. players diff --git a/pom.xml b/pom.xml index 3f1f5127..ae2278c8 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ net.juligames.core JuliGamesCore pom - 1.5 + 1.6 PaperCore Core @@ -20,6 +20,7 @@ VelocityAPI VelocityCore MiniGameAPI + SuperCore