From 35f94eac3b3cc381b5132a09841c1d6280a7a322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jozef=20Steinh=C3=BCbl?= Date: Sun, 24 Dec 2023 15:46:13 +0100 Subject: [PATCH] feat: messaging (#7) * feat: messaging * fix: some things * feat: make it actually working * feat: disable messaging by default * fix: avoid infinity loop * feat: multiple channels * remove todo message * fix: global channel should be lighteco:messages * fix: check if CHANNELS not null * refactor: set channels in constructor * fix: push user updates on quit when user is dirty --- .../dev/xhyrom/lighteco/api/LightEco.java | 10 ++ .../api/messaging/MessagingService.java | 8 + .../messenger/IncomingMessageConsumer.java | 10 ++ .../lighteco/api/messenger/Messenger.java | 12 ++ .../api/messenger/MessengerProvider.java | 7 + .../api/messenger/message/Message.java | 12 ++ .../messenger/message/OutgoingMessage.java | 15 ++ .../message/type/UserUpdateMessage.java | 23 +++ .../lighteco/bukkit/BukkitLightEcoPlugin.java | 6 + .../listeners/BukkitConnectionListener.java | 13 +- common/build.gradle.kts | 1 + .../lighteco/common/api/LightEcoApi.java | 8 + .../common/api/impl/ApiAbstractManager.java | 6 +- .../common/api/impl/ApiCommandManager.java | 8 +- .../common/api/impl/ApiCurrencyManager.java | 14 +- .../common/api/impl/ApiMessagingService.java | 23 +++ .../lighteco/common/api/impl/ApiUser.java | 36 +++-- .../common/api/impl/ApiUserManager.java | 14 +- .../xhyrom/lighteco/common/config/Config.java | 4 + .../config/messaging/MessagingConfig.java | 15 ++ .../config/messaging/MessagingDataConfig.java | 16 ++ .../common/dependencies/Dependency.java | 23 +++ .../dependencies/DependencyManager.java | 2 + .../dependencies/DependencyManagerImpl.java | 49 +++--- .../dependencies/DependencyRegistry.java | 15 ++ .../messaging/InternalMessagingService.java | 13 ++ .../messaging/LightEcoMessagingService.java | 145 ++++++++++++++++++ .../common/messaging/MessagingFactory.java | 38 +++++ .../common/messaging/MessagingType.java | 6 + .../messaging/message/AbstractMessage.java | 20 +++ .../common/messaging/message/MessageType.java | 5 + .../message/UserUpdateMessageImpl.java | 49 ++++++ .../messaging/type/redis/RedisMessenger.java | 94 ++++++++++++ .../type/redis/RedisMessengerProvider.java | 33 ++++ .../lighteco/common/model/user/User.java | 13 +- .../common/plugin/AbstractLightEcoPlugin.java | 28 +++- .../common/plugin/LightEcoPlugin.java | 5 + .../common/storage/StorageFactory.java | 3 +- .../common/util/gson/GsonProvider.java | 12 ++ .../lighteco/sponge/SpongeLightEcoPlugin.java | 6 + 40 files changed, 743 insertions(+), 77 deletions(-) create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messaging/MessagingService.java create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messenger/IncomingMessageConsumer.java create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messenger/Messenger.java create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messenger/MessengerProvider.java create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/Message.java create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/OutgoingMessage.java create mode 100644 api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/type/UserUpdateMessage.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiMessagingService.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/config/messaging/MessagingConfig.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/config/messaging/MessagingDataConfig.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/InternalMessagingService.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/LightEcoMessagingService.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingFactory.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingType.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/AbstractMessage.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/MessageType.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/UserUpdateMessageImpl.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessenger.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessengerProvider.java create mode 100644 common/src/main/java/dev/xhyrom/lighteco/common/util/gson/GsonProvider.java diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/LightEco.java b/api/src/main/java/dev/xhyrom/lighteco/api/LightEco.java index 451c6e7..7b880b7 100644 --- a/api/src/main/java/dev/xhyrom/lighteco/api/LightEco.java +++ b/api/src/main/java/dev/xhyrom/lighteco/api/LightEco.java @@ -3,10 +3,13 @@ package dev.xhyrom.lighteco.api; import dev.xhyrom.lighteco.api.manager.CommandManager; import dev.xhyrom.lighteco.api.manager.CurrencyManager; import dev.xhyrom.lighteco.api.manager.UserManager; +import dev.xhyrom.lighteco.api.messaging.MessagingService; import dev.xhyrom.lighteco.api.platform.Platform; import dev.xhyrom.lighteco.api.platform.PlayerAdapter; import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Optional; + public interface LightEco { /** * Gets the {@link Platform}, which represents the current platform the @@ -37,6 +40,13 @@ public interface LightEco { */ @NonNull CommandManager getCommandManager(); + /** + * Gets the {@link MessagingService}, which manages the messaging. + * + * @return the messaging service + */ + @NonNull Optional getMessagingService(); + /** * Gets the {@link PlayerAdapter} for a player class. * diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messaging/MessagingService.java b/api/src/main/java/dev/xhyrom/lighteco/api/messaging/MessagingService.java new file mode 100644 index 0000000..72cfdc1 --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messaging/MessagingService.java @@ -0,0 +1,8 @@ +package dev.xhyrom.lighteco.api.messaging; + +import dev.xhyrom.lighteco.api.model.currency.Currency; +import dev.xhyrom.lighteco.api.model.user.User; + +public interface MessagingService { + void pushUserUpdate(User user, Currency currency); +} diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messenger/IncomingMessageConsumer.java b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/IncomingMessageConsumer.java new file mode 100644 index 0000000..ff83582 --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/IncomingMessageConsumer.java @@ -0,0 +1,10 @@ +package dev.xhyrom.lighteco.api.messenger; + +import dev.xhyrom.lighteco.api.messenger.message.Message; +import org.checkerframework.checker.nullness.qual.NonNull; + +public interface IncomingMessageConsumer { + void consumeIncomingMessage(@NonNull Message message); + + void consumeRawIncomingMessage(@NonNull String message); +} diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messenger/Messenger.java b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/Messenger.java new file mode 100644 index 0000000..ea6d7e3 --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/Messenger.java @@ -0,0 +1,12 @@ +package dev.xhyrom.lighteco.api.messenger; + +import dev.xhyrom.lighteco.api.messenger.message.OutgoingMessage; +import org.checkerframework.checker.nullness.qual.NonNull; + +public interface Messenger extends AutoCloseable { + void sendOutgoingMessage(@NonNull OutgoingMessage message, boolean global); + + @Override + default void close() { + } +} diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messenger/MessengerProvider.java b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/MessengerProvider.java new file mode 100644 index 0000000..86cee89 --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/MessengerProvider.java @@ -0,0 +1,7 @@ +package dev.xhyrom.lighteco.api.messenger; + +import org.checkerframework.checker.nullness.qual.NonNull; + +public interface MessengerProvider { + @NonNull Messenger obtain(@NonNull IncomingMessageConsumer consumer); +} diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/Message.java b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/Message.java new file mode 100644 index 0000000..a1b9d67 --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/Message.java @@ -0,0 +1,12 @@ +package dev.xhyrom.lighteco.api.messenger.message; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.UUID; + +/** + * Represents a message that can be received by a {@link dev.xhyrom.lighteco.api.messenger.Messenger}. + */ +public interface Message { + @NonNull UUID getId(); +} diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/OutgoingMessage.java b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/OutgoingMessage.java new file mode 100644 index 0000000..bd7007e --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/OutgoingMessage.java @@ -0,0 +1,15 @@ +package dev.xhyrom.lighteco.api.messenger.message; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Represents a message that can be sent via a {@link dev.xhyrom.lighteco.api.messenger.Messenger}. + */ +public interface OutgoingMessage extends Message { + /** + * Serializes message into a string. + * + * @return serialized message + */ + @NonNull String serialize(); +} diff --git a/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/type/UserUpdateMessage.java b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/type/UserUpdateMessage.java new file mode 100644 index 0000000..7d6b2e8 --- /dev/null +++ b/api/src/main/java/dev/xhyrom/lighteco/api/messenger/message/type/UserUpdateMessage.java @@ -0,0 +1,23 @@ +package dev.xhyrom.lighteco.api.messenger.message.type; + +import dev.xhyrom.lighteco.api.messenger.message.Message; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.math.BigDecimal; +import java.util.UUID; + +/** + * Represents a message that is sent when a user updates their profile. + */ +public interface UserUpdateMessage extends Message { + /** + * Gets the unique id of the user that updated their profile. + * + * @return the user's unique id + */ + @NonNull UUID getUserUniqueId(); + + @NonNull String getCurrencyIdentifier(); + + @NonNull BigDecimal getNewBalance(); +} diff --git a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java index 63b5ce3..bca9b1f 100644 --- a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java +++ b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/BukkitLightEcoPlugin.java @@ -8,6 +8,7 @@ import dev.xhyrom.lighteco.bukkit.listeners.BukkitConnectionListener; import dev.xhyrom.lighteco.bukkit.manager.BukkitCommandManager; import dev.xhyrom.lighteco.bukkit.manager.BukkitContextManager; import dev.xhyrom.lighteco.common.manager.currency.StandardCurrencyManager; +import dev.xhyrom.lighteco.common.messaging.MessagingFactory; import dev.xhyrom.lighteco.common.plugin.AbstractLightEcoPlugin; import dev.xhyrom.lighteco.common.manager.user.StandardUserManager; import lombok.Getter; @@ -45,6 +46,11 @@ public class BukkitLightEcoPlugin extends AbstractLightEcoPlugin { this.contextManager = new BukkitContextManager(); } + @Override + protected MessagingFactory getMessagingFactory() { + return new MessagingFactory(this); + } + @Override protected void registerApiOnPlatform(LightEco api) { this.getBootstrap().getLoader().getServer().getServicesManager().register(LightEco.class, api, this.getBootstrap().getLoader(), ServicePriority.Normal); diff --git a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/listeners/BukkitConnectionListener.java b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/listeners/BukkitConnectionListener.java index c8176b2..2b79005 100644 --- a/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/listeners/BukkitConnectionListener.java +++ b/bukkit/src/main/java/dev/xhyrom/lighteco/bukkit/listeners/BukkitConnectionListener.java @@ -1,11 +1,11 @@ package dev.xhyrom.lighteco.bukkit.listeners; import dev.xhyrom.lighteco.bukkit.BukkitLightEcoPlugin; +import dev.xhyrom.lighteco.common.model.currency.Currency; import dev.xhyrom.lighteco.common.model.user.User; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -58,11 +58,10 @@ public class BukkitConnectionListener implements Listener { } this.plugin.getUserManager().saveUser(user) - .thenAccept(v -> { - // make sure the player is offline before unloading - if (Bukkit.getPlayer(uniqueId) != null) return; - - this.plugin.getUserManager().unload(uniqueId); - }); + .thenAccept(v -> this.plugin.getMessagingService().ifPresent(service -> { + for (Currency currency : this.plugin.getCurrencyManager().getRegisteredCurrencies()) { + service.pushUserUpdate(user, currency); + } + })); } } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 049f144..a2595f9 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { implementation("eu.okaeri:okaeri-configs-validator-okaeri:5.0.0-beta.5") compileOnly("com.zaxxer:HikariCP:5.0.1") + compileOnly("redis.clients:jedis:5.1.0") compileOnly("org.projectlombok:lombok:1.18.28") annotationProcessor("org.projectlombok:lombok:1.18.28") diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/api/LightEcoApi.java b/common/src/main/java/dev/xhyrom/lighteco/common/api/LightEcoApi.java index dbf396b..554241c 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/api/LightEcoApi.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/api/LightEcoApi.java @@ -4,12 +4,15 @@ import dev.xhyrom.lighteco.api.LightEco; import dev.xhyrom.lighteco.api.manager.CommandManager; import dev.xhyrom.lighteco.api.manager.CurrencyManager; import dev.xhyrom.lighteco.api.manager.UserManager; +import dev.xhyrom.lighteco.api.messaging.MessagingService; import dev.xhyrom.lighteco.api.platform.Platform; import dev.xhyrom.lighteco.api.platform.PlayerAdapter; import dev.xhyrom.lighteco.common.api.impl.*; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Optional; + public class LightEcoApi implements LightEco { private final LightEcoPlugin plugin; @@ -49,6 +52,11 @@ public class LightEcoApi implements LightEco { return this.commandManager; } + @Override + public @NonNull Optional getMessagingService() { + return this.plugin.getMessagingService().map(ApiMessagingService::new); + } + @Override public @NonNull PlayerAdapter getPlayerAdapter(@NonNull Class playerClass) { Class expected = this.plugin.getContextManager().getPlayerClass(); diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiAbstractManager.java b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiAbstractManager.java index 026b2a7..8c9e118 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiAbstractManager.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiAbstractManager.java @@ -4,10 +4,10 @@ import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; public abstract class ApiAbstractManager { protected final LightEcoPlugin plugin; - protected final H handler; + protected final H handle; - protected ApiAbstractManager(LightEcoPlugin plugin, H handler) { + protected ApiAbstractManager(LightEcoPlugin plugin, H handle) { this.plugin = plugin; - this.handler = handler; + this.handle = handle; } } diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiCommandManager.java b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiCommandManager.java index 0954065..bab2ecd 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiCommandManager.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiCommandManager.java @@ -6,8 +6,8 @@ import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import org.checkerframework.checker.nullness.qual.NonNull; public class ApiCommandManager extends ApiAbstractManager implements CommandManager { - public ApiCommandManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.command.CommandManager handler) { - super(plugin, handler); + public ApiCommandManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.command.CommandManager handle) { + super(plugin, handle); } @Override @@ -15,7 +15,7 @@ public class ApiCommandManager extends ApiAbstractManager implements CurrencyManager { - public ApiCurrencyManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.currency.CurrencyManager handler) { - super(plugin, handler); + public ApiCurrencyManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.currency.CurrencyManager handle) { + super(plugin, handle); } - private Currency wrap(dev.xhyrom.lighteco.common.model.currency.Currency handler) { - return handler.getProxy(); + private Currency wrap(dev.xhyrom.lighteco.common.model.currency.Currency handle) { + return handle.getProxy(); } @Override public @NonNull Collection getRegisteredCurrencies() { - return this.handler.values() + return this.handle.values() .stream().map(this::wrap) .toList(); } @Override public Currency getCurrency(@NonNull String identifier) { - return wrap(this.handler.getIfLoaded(identifier)); + return wrap(this.handle.getIfLoaded(identifier)); } @Override public void registerCurrency(@NonNull Currency currency) { dev.xhyrom.lighteco.common.model.currency.Currency internal = new dev.xhyrom.lighteco.common.model.currency.Currency(currency); - this.handler.registerCurrency(internal); + this.handle.registerCurrency(internal); } } diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiMessagingService.java b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiMessagingService.java new file mode 100644 index 0000000..2944469 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiMessagingService.java @@ -0,0 +1,23 @@ +package dev.xhyrom.lighteco.common.api.impl; + +import dev.xhyrom.lighteco.api.messaging.MessagingService; +import dev.xhyrom.lighteco.api.model.currency.Currency; +import dev.xhyrom.lighteco.api.model.user.User; +import dev.xhyrom.lighteco.common.messaging.InternalMessagingService; + +public class ApiMessagingService implements MessagingService { + private final InternalMessagingService handle; + + public ApiMessagingService(InternalMessagingService handle) { + this.handle = handle; + } + + @Override + public void pushUserUpdate(User user, Currency currency) { + dev.xhyrom.lighteco.common.model.currency.Currency internalCurrency = this.handle.getPlugin() + .getCurrencyManager() + .getIfLoaded(currency.getIdentifier()); + + this.handle.pushUserUpdate(ApiUser.cast(user), internalCurrency); + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUser.java b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUser.java index a3d01d4..454cd5c 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUser.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUser.java @@ -10,60 +10,68 @@ import java.math.BigDecimal; import java.util.UUID; public class ApiUser implements User { - private final dev.xhyrom.lighteco.common.model.user.User handler; + public static dev.xhyrom.lighteco.common.model.user.User cast(User u) { + if (u instanceof ApiUser) { + return ((ApiUser) u).handle; + } - public ApiUser(dev.xhyrom.lighteco.common.model.user.User handler) { - this.handler = handler; + throw new IllegalArgumentException("Cannot cast " + u.getClass().getName() + " to " + ApiUser.class.getName()); + } + + private final dev.xhyrom.lighteco.common.model.user.User handle; + + public ApiUser(dev.xhyrom.lighteco.common.model.user.User handle) { + this.handle = handle; } @Override public @NonNull UUID getUniqueId() { - return this.handler.getUniqueId(); + return this.handle.getUniqueId(); } @Override public @Nullable String getUsername() { - return this.handler.getUsername(); + return this.handle.getUsername(); } @Override public @NonNull BigDecimal getBalance(@NonNull Currency currency) { - dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handler.getPlugin() + dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handle.getPlugin() .getCurrencyManager() .getIfLoaded(currency.getIdentifier()); - return this.handler.getBalance(internal); + return this.handle.getBalance(internal); } @Override public void setBalance(@NonNull Currency currency, @NonNull BigDecimal balance) { - dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handler.getPlugin() + dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handle.getPlugin() .getCurrencyManager() .getIfLoaded(currency.getIdentifier()); - this.handler.setBalance(internal, balance); + this.handle.setBalance(internal, balance); } @Override public void deposit(@NonNull Currency currency, @NonNull BigDecimal amount) { - dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handler.getPlugin() + dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handle.getPlugin() .getCurrencyManager() .getIfLoaded(currency.getIdentifier()); - this.handler.deposit(internal, amount); + this.handle.deposit(internal, amount); } @Override public void withdraw(@NonNull Currency currency, @NonNull BigDecimal amount) { - dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handler.getPlugin() + dev.xhyrom.lighteco.common.model.currency.Currency internal = this.handle.getPlugin() .getCurrencyManager() .getIfLoaded(currency.getIdentifier()); - this.handler.withdraw(internal, amount); + this.handle.withdraw(internal, amount); } @Override public void sendMessage(Component message) { - this.handler.sendMessage(message); + this.handle.sendMessage(message); } } diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUserManager.java b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUserManager.java index c65d882..cb93789 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUserManager.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/api/impl/ApiUserManager.java @@ -10,13 +10,13 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; public class ApiUserManager extends ApiAbstractManager implements UserManager { - public ApiUserManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.user.UserManager handler) { - super(plugin, handler); + public ApiUserManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.user.UserManager handle) { + super(plugin, handle); } - private User wrap(dev.xhyrom.lighteco.common.model.user.User handler) { - this.plugin.getUserManager().getHousekeeper().registerUsage(handler.getUniqueId()); - return handler.getProxy(); + private User wrap(dev.xhyrom.lighteco.common.model.user.User handle) { + this.plugin.getUserManager().getHousekeeper().registerUsage(handle.getUniqueId()); + return handle.getProxy(); } @Override @@ -42,11 +42,11 @@ public class ApiUserManager extends ApiAbstractManager dependencies); void loadStorageDependencies(Set types); + void loadMessagingDependencies(Set types); ClassLoader obtainClassLoaderWith(Set dependencies); diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java index b2bfc39..060837d 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyManagerImpl.java @@ -5,10 +5,11 @@ import com.google.common.io.MoreFiles; import dev.xhyrom.lighteco.common.config.Config; import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation; import dev.xhyrom.lighteco.common.dependencies.relocation.RelocationHandler; +import dev.xhyrom.lighteco.common.messaging.MessagingType; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import dev.xhyrom.lighteco.common.plugin.logger.PluginLogger; -import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess; import dev.xhyrom.lighteco.common.storage.StorageType; +import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import java.io.IOException; @@ -17,8 +18,6 @@ import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; public class DependencyManagerImpl implements DependencyManager { private final EnumMap loaded = new EnumMap<>(Dependency.class); @@ -49,42 +48,29 @@ public class DependencyManagerImpl implements DependencyManager { @Override public void loadDependencies(Set dependencies) { - CountDownLatch latch = new CountDownLatch(dependencies.size()); - if (this.config.debug) this.logger.info("Loading dependencies: " + dependencies); for (Dependency dependency : dependencies) { if (this.loaded.containsKey(dependency)) { - latch.countDown(); continue; } - CompletableFuture.runAsync(() -> { - if (this.config.debug) - this.logger.info("Loading dependency " + dependency); - - try { - loadDependency(dependency); - } catch (Exception e) { - throw new RuntimeException("Failed to load dependency " + dependency, e); - } finally { - latch.countDown(); - - if (this.config.debug) - this.logger.info("Loaded dependency " + dependency); - } - }); - } - - try { - latch.await(); - if (this.config.debug) - this.logger.info("Loaded dependencies: " + dependencies); - } catch (InterruptedException e) { - throw new RuntimeException(e); + this.logger.info("Loading dependency " + dependency); + + try { + loadDependency(dependency); + } catch (Exception e) { + throw new RuntimeException("Failed to load dependency " + dependency, e); + } finally { + if (this.config.debug) + this.logger.info("Loaded dependency " + dependency); + } } + + if (this.config.debug) + this.logger.info("Loaded dependencies: " + dependencies); } private void loadDependency(Dependency dependency) throws Exception { @@ -142,6 +128,11 @@ public class DependencyManagerImpl implements DependencyManager { loadDependencies(this.registry.resolveStorageDependencies(types)); } + @Override + public void loadMessagingDependencies(Set types) { + loadDependencies(this.registry.resolveMessagingDependencies(types)); + } + @Override public ClassLoader obtainClassLoaderWith(Set dependencies) { ImmutableSet set = ImmutableSet.copyOf(dependencies); diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java index b948bcb..88a0587 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/dependencies/DependencyRegistry.java @@ -2,6 +2,7 @@ package dev.xhyrom.lighteco.common.dependencies; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; +import dev.xhyrom.lighteco.common.messaging.MessagingType; import dev.xhyrom.lighteco.common.storage.StorageType; import java.util.LinkedHashSet; @@ -16,6 +17,10 @@ public class DependencyRegistry { .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.HIKARI) .build(); + private static final SetMultimap MESSAGING_DEPENDENCIES = ImmutableSetMultimap.builder() + .putAll(MessagingType.REDIS, Dependency.COMMONS_POOL_2, Dependency.JEDIS, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE) + .build(); + public Set resolveStorageDependencies(Set types) { Set dependencies = new LinkedHashSet<>(); @@ -26,6 +31,16 @@ public class DependencyRegistry { return dependencies; } + public Set resolveMessagingDependencies(Set types) { + Set dependencies = new LinkedHashSet<>(); + + for (MessagingType type : types) { + dependencies.addAll(MESSAGING_DEPENDENCIES.get(type)); + } + + return dependencies; + } + public boolean shouldAutoLoad(Dependency dependency) { return switch (dependency) { case H2_DRIVER, SQLITE_DRIVER -> false; diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/InternalMessagingService.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/InternalMessagingService.java new file mode 100644 index 0000000..a778455 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/InternalMessagingService.java @@ -0,0 +1,13 @@ +package dev.xhyrom.lighteco.common.messaging; + +import dev.xhyrom.lighteco.common.model.currency.Currency; +import dev.xhyrom.lighteco.common.model.user.User; +import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; + +public interface InternalMessagingService { + LightEcoPlugin getPlugin(); + + void pushUserUpdate(User user, Currency currency); + + void shutdown(); +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/LightEcoMessagingService.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/LightEcoMessagingService.java new file mode 100644 index 0000000..745a38d --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/LightEcoMessagingService.java @@ -0,0 +1,145 @@ +package dev.xhyrom.lighteco.common.messaging; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.xhyrom.lighteco.api.messenger.IncomingMessageConsumer; +import dev.xhyrom.lighteco.api.messenger.Messenger; +import dev.xhyrom.lighteco.api.messenger.MessengerProvider; +import dev.xhyrom.lighteco.api.messenger.message.Message; +import dev.xhyrom.lighteco.api.messenger.message.type.UserUpdateMessage; +import dev.xhyrom.lighteco.common.cache.ExpiringSet; +import dev.xhyrom.lighteco.common.messaging.message.MessageType; +import dev.xhyrom.lighteco.common.messaging.message.UserUpdateMessageImpl; +import dev.xhyrom.lighteco.common.model.currency.Currency; +import dev.xhyrom.lighteco.common.model.user.User; +import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; +import dev.xhyrom.lighteco.common.util.gson.GsonProvider; +import lombok.Getter; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +public class LightEcoMessagingService implements InternalMessagingService, IncomingMessageConsumer { + @Getter + private final LightEcoPlugin plugin; + private final ExpiringSet receivedMessages; + + private final Messenger messenger; + private final MessengerProvider provider; + + public LightEcoMessagingService(LightEcoPlugin plugin, MessengerProvider provider) { + this.plugin = plugin; + + this.provider = provider; + this.messenger = provider.obtain(this); + this.receivedMessages = new ExpiringSet<>(30, TimeUnit.MINUTES); + } + + private UUID generateMessageId() { + UUID id = UUID.randomUUID(); + + this.receivedMessages.add(id); + return id; + } + + @Override + public void pushUserUpdate(User user, Currency currency) { + this.plugin.getBootstrap().getScheduler().async().execute(() -> + this.messenger.sendOutgoingMessage( + new UserUpdateMessageImpl(generateMessageId(), user.getUniqueId(), currency.getIdentifier(), user.getBalance(currency)), + currency.getType() == dev.xhyrom.lighteco.api.model.currency.Currency.Type.GLOBAL + ) + ); + } + + public static @NonNull String serialize(MessageType type, UUID id, JsonElement content) { + JsonObject data = new JsonObject(); + + data.add("i", new JsonPrimitive(id.toString())); + data.add("t", new JsonPrimitive(type.name())); + data.add("c", content); + + return GsonProvider.get().toJson(data); + } + + @Override + public void consumeIncomingMessage(@NonNull Message message) { + if (!this.receivedMessages.add(message.getId())) { + return; + } + + this.processIncomingMessage(message); + } + + @Override + public void consumeRawIncomingMessage(@NonNull String message) { + try { + deserializeAndConsumeRawIncomingMessage(message); + } catch (Exception e) { + this.plugin.getBootstrap().getLogger().warn("Failed to deserialize incoming message: " + message, e); + } + } + + private void deserializeAndConsumeRawIncomingMessage(@NonNull String message) { + JsonObject parsed = GsonProvider.get().fromJson(message, JsonObject.class); + + JsonElement idElement = parsed.get("i"); + if (idElement == null) { + throw new IllegalStateException("Missing message id: " + message); + } + + UUID id = UUID.fromString(idElement.getAsString()); + + if (!this.receivedMessages.add(id)) { + return; + } + + JsonElement typeElement = parsed.get("t"); + if (typeElement == null) { + throw new IllegalStateException("Missing message type: " + message); + } + + MessageType type = MessageType.valueOf(typeElement.getAsString()); + + @Nullable JsonElement contentElement = parsed.get("c"); + + Message deserialized; + switch (type) { + case USER_UPDATE: + deserialized = UserUpdateMessageImpl.deserialize(id, contentElement); + break; + default: + return; + } + + this.processIncomingMessage(deserialized); + } + + private void processIncomingMessage(Message message) { + if (message instanceof UserUpdateMessage userUpdateMessage) { + this.plugin.getBootstrap().getScheduler().async().execute(() -> { + User user = this.plugin.getUserManager().getIfLoaded(userUpdateMessage.getUserUniqueId()); + if (user == null) { + return; + } + + Currency currency = this.plugin.getCurrencyManager().getIfLoaded(userUpdateMessage.getCurrencyIdentifier()); + if (currency == null) { + return; + } + + user.setBalance(currency, userUpdateMessage.getNewBalance(), false, false); + }); + } else { + throw new IllegalStateException("Unknown message type: " + message.getClass().getName()); + } + } + + @Override + public void shutdown() { + this.messenger.close(); + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingFactory.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingFactory.java new file mode 100644 index 0000000..155d600 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingFactory.java @@ -0,0 +1,38 @@ +package dev.xhyrom.lighteco.common.messaging; + +import dev.xhyrom.lighteco.api.messenger.MessengerProvider; +import dev.xhyrom.lighteco.common.messaging.type.redis.RedisMessengerProvider; +import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; + +import java.util.Set; + +public class MessagingFactory { + private final LightEcoPlugin plugin; + + public MessagingFactory(LightEcoPlugin plugin) { + this.plugin = plugin; + } + + protected LightEcoPlugin getPlugin() { + return this.plugin; + } + + public Set getRequiredTypes() { + return Set.of(this.plugin.getConfig().messaging.provider); + } + + public InternalMessagingService get() { + MessagingType type = this.plugin.getConfig().messaging.provider; + MessengerProvider provider = this.createProvider(type); + if (provider == null) return null; + + return new LightEcoMessagingService(this.plugin, provider); + } + + private MessengerProvider createProvider(MessagingType type) { + return switch (type) { + case REDIS -> new RedisMessengerProvider(this.plugin); + default -> null; + }; + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingType.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingType.java new file mode 100644 index 0000000..7eb1dd9 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/MessagingType.java @@ -0,0 +1,6 @@ +package dev.xhyrom.lighteco.common.messaging; + +public enum MessagingType { + NONE, + REDIS +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/AbstractMessage.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/AbstractMessage.java new file mode 100644 index 0000000..e8c9cf2 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/AbstractMessage.java @@ -0,0 +1,20 @@ +package dev.xhyrom.lighteco.common.messaging.message; + +import dev.xhyrom.lighteco.api.messenger.message.Message; +import dev.xhyrom.lighteco.api.messenger.message.OutgoingMessage; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.util.UUID; + +public abstract class AbstractMessage implements Message, OutgoingMessage { + private final UUID id; + + protected AbstractMessage(UUID id) { + this.id = id; + } + + @Override + public @NonNull UUID getId() { + return this.id; + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/MessageType.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/MessageType.java new file mode 100644 index 0000000..e25af3c --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/MessageType.java @@ -0,0 +1,5 @@ +package dev.xhyrom.lighteco.common.messaging.message; + +public enum MessageType { + USER_UPDATE; +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/UserUpdateMessageImpl.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/UserUpdateMessageImpl.java new file mode 100644 index 0000000..1604865 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/message/UserUpdateMessageImpl.java @@ -0,0 +1,49 @@ +package dev.xhyrom.lighteco.common.messaging.message; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import dev.xhyrom.lighteco.api.messenger.message.type.UserUpdateMessage; +import dev.xhyrom.lighteco.common.messaging.LightEcoMessagingService; +import lombok.Getter; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.math.BigDecimal; +import java.util.UUID; + +public class UserUpdateMessageImpl extends AbstractMessage implements UserUpdateMessage { + private static final MessageType TYPE = MessageType.USER_UPDATE; + + @Getter + private final UUID userUniqueId; + @Getter + private final String currencyIdentifier; + @Getter + private final BigDecimal newBalance; + + public static UserUpdateMessage deserialize(UUID id, @NonNull JsonElement data) { + JsonObject obj = data.getAsJsonObject(); + UUID userUniqueId = UUID.fromString(obj.get("u").getAsString()); + String currencyIdentifier = obj.get("c").getAsString(); + BigDecimal newBalance = obj.get("b").getAsBigDecimal(); + + return new UserUpdateMessageImpl(id, userUniqueId, currencyIdentifier, newBalance); + } + + public UserUpdateMessageImpl(UUID id, UUID userUniqueId, String currencyIdentifier, BigDecimal newBalance) { + super(id); + this.userUniqueId = userUniqueId; + this.currencyIdentifier = currencyIdentifier; + this.newBalance = newBalance; + } + + @Override + public @NonNull String serialize() { + JsonObject data = new JsonObject(); + data.add("u", new JsonPrimitive(this.userUniqueId.toString())); + data.add("c", new JsonPrimitive(this.currencyIdentifier)); + data.add("b", new JsonPrimitive(this.newBalance)); + + return LightEcoMessagingService.serialize(TYPE, getId(), data); + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessenger.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessenger.java new file mode 100644 index 0000000..82fe986 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessenger.java @@ -0,0 +1,94 @@ +package dev.xhyrom.lighteco.common.messaging.type.redis; + +import dev.xhyrom.lighteco.api.messenger.IncomingMessageConsumer; +import dev.xhyrom.lighteco.api.messenger.Messenger; +import dev.xhyrom.lighteco.api.messenger.message.OutgoingMessage; +import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; +import lombok.Getter; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import redis.clients.jedis.*; + +public class RedisMessenger implements Messenger { + private static final String CHANNEL = "lighteco:{}:messages"; + @Getter + private final String[] channels; + + private final LightEcoPlugin plugin; + private final IncomingMessageConsumer consumer; + + private UnifiedJedis jedis; + private Subscription sub; + + public RedisMessenger(LightEcoPlugin plugin, IncomingMessageConsumer consumer) { + this.plugin = plugin; + this.consumer = consumer; + + this.channels = new String[] { + CHANNEL.replace("{}:", ""), + CHANNEL.replace("{}", this.plugin.getConfig().server) + }; + } + + public void init(@Nullable String address, @Nullable String username, String password, boolean ssl) { + this.init(new JedisPooled( + parseAddress(address), + jedisConfig(username, password, ssl) + )); + } + + private void init(UnifiedJedis jedis) { + this.jedis = jedis; + this.sub = new Subscription(this); + + this.plugin.getBootstrap().getScheduler().async().execute(() -> { + this.jedis.subscribe(this.sub, this.getChannels()); + }); + } + + private static JedisClientConfig jedisConfig(@Nullable String username, @Nullable String password, boolean ssl) { + return DefaultJedisClientConfig.builder() + .user(username) + .password(password) + .ssl(ssl) + .timeoutMillis(Protocol.DEFAULT_TIMEOUT) + .build(); + } + + private static HostAndPort parseAddress(String address) { + String[] addressSplit = address.split(":"); + String host = addressSplit[0]; + int port = addressSplit.length > 1 ? Integer.parseInt(addressSplit[1]) : Protocol.DEFAULT_PORT; + + return new HostAndPort(host, port); + } + + @Override + public void sendOutgoingMessage(@NonNull OutgoingMessage message, boolean global) { + this.jedis.publish(global ? getChannels()[0] : getChannels()[1], message.serialize()); + } + + @Override + public void close() { + this.sub.unsubscribe(); + this.jedis.close(); + } + + private static class Subscription extends JedisPubSub { + private final RedisMessenger messenger; + + public Subscription(RedisMessenger messenger) { + this.messenger = messenger; + } + + @Override + public void onMessage(String channel, String message) { + String[] channels = this.messenger.getChannels(); + if (!channel.equals(channels[0]) && !channel.equals(channels[1])) { + return; + } + + this.messenger.consumer.consumeRawIncomingMessage(message); + } + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessengerProvider.java b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessengerProvider.java new file mode 100644 index 0000000..2257453 --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/messaging/type/redis/RedisMessengerProvider.java @@ -0,0 +1,33 @@ +package dev.xhyrom.lighteco.common.messaging.type.redis; + +import dev.xhyrom.lighteco.api.messenger.IncomingMessageConsumer; +import dev.xhyrom.lighteco.api.messenger.Messenger; +import dev.xhyrom.lighteco.api.messenger.MessengerProvider; +import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; +import org.checkerframework.checker.nullness.qual.NonNull; + +public class RedisMessengerProvider implements MessengerProvider { + private final LightEcoPlugin plugin; + + public RedisMessengerProvider(LightEcoPlugin plugin) { + this.plugin = plugin; + } + + @Override + public @NonNull Messenger obtain(@NonNull IncomingMessageConsumer consumer) { + RedisMessenger messenger = new RedisMessenger(this.plugin, consumer); + + String address = this.plugin.getConfig().messaging.data.address; + String username = this.plugin.getConfig().messaging.data.username; + String password = this.plugin.getConfig().messaging.data.password; + + if (password.isEmpty()) password = null; + if (username.isEmpty()) username = null; + + boolean ssl = this.plugin.getConfig().messaging.data.ssl; + + messenger.init(address, username, password, ssl); + + return messenger; + } +} diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/model/user/User.java b/common/src/main/java/dev/xhyrom/lighteco/common/model/user/User.java index 83069dc..c3544f3 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/model/user/User.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/model/user/User.java @@ -4,6 +4,7 @@ import dev.xhyrom.lighteco.api.exception.CannotBeGreaterThan; import dev.xhyrom.lighteco.api.exception.CannotBeNegative; import dev.xhyrom.lighteco.common.api.impl.ApiUser; import dev.xhyrom.lighteco.common.cache.RedisBackedMap; +import dev.xhyrom.lighteco.common.messaging.InternalMessagingService; import dev.xhyrom.lighteco.common.model.currency.Currency; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import lombok.Getter; @@ -14,6 +15,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.HashMap; +import java.util.Optional; import java.util.UUID; @Getter @@ -50,10 +52,14 @@ public class User { } public void setBalance(@NonNull Currency currency, @NonNull BigDecimal balance) throws CannotBeNegative, CannotBeGreaterThan { - this.setBalance(currency, balance, false); + this.setBalance(currency, balance, false, true); } public void setBalance(@NonNull Currency currency, @NonNull BigDecimal balance, boolean force) throws CannotBeNegative, CannotBeGreaterThan { + this.setBalance(currency, balance, force, true); + } + + public void setBalance(@NonNull Currency currency, @NonNull BigDecimal balance, boolean force, boolean publish) throws CannotBeNegative, CannotBeGreaterThan { if (balance.compareTo(BigDecimal.ZERO) < 0) { throw new CannotBeNegative("Balance cannot be negative"); } @@ -67,6 +73,11 @@ public class User { if (!force) this.setDirty(true); + + if (publish) { + @NonNull Optional messagingService = this.plugin.getMessagingService(); + messagingService.ifPresent(internalMessagingService -> internalMessagingService.pushUserUpdate(this, currency)); + } } public void deposit(@NonNull Currency currency, @NonNull BigDecimal amount) throws CannotBeNegative, CannotBeGreaterThan { diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/plugin/AbstractLightEcoPlugin.java b/common/src/main/java/dev/xhyrom/lighteco/common/plugin/AbstractLightEcoPlugin.java index 4908b9c..ab03d48 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/plugin/AbstractLightEcoPlugin.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/plugin/AbstractLightEcoPlugin.java @@ -6,13 +6,17 @@ import dev.xhyrom.lighteco.common.api.LightEcoApi; import dev.xhyrom.lighteco.common.config.Config; import dev.xhyrom.lighteco.common.dependencies.DependencyManager; import dev.xhyrom.lighteco.common.dependencies.DependencyManagerImpl; +import dev.xhyrom.lighteco.common.messaging.MessagingFactory; +import dev.xhyrom.lighteco.common.messaging.InternalMessagingService; import dev.xhyrom.lighteco.common.storage.Storage; import dev.xhyrom.lighteco.common.storage.StorageFactory; import dev.xhyrom.lighteco.common.task.UserSaveTask; import eu.okaeri.configs.ConfigManager; import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer; import lombok.Getter; +import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Optional; import java.util.concurrent.TimeUnit; @Getter @@ -23,6 +27,7 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin { @Getter private Storage storage; + private InternalMessagingService messagingService; private LightEcoApi api; private UserSaveTask userSaveTask; @@ -41,10 +46,18 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin { public final void enable() { // setup storage - StorageFactory factory = new StorageFactory(this); - this.dependencyManager.loadStorageDependencies(factory.getRequiredTypes()); + StorageFactory storageFactory = new StorageFactory(this); + this.dependencyManager.loadStorageDependencies( + storageFactory.getRequiredTypes() + ); - this.storage = factory.get(); + MessagingFactory messagingFactory = this.getMessagingFactory(); + this.dependencyManager.loadMessagingDependencies( + messagingFactory.getRequiredTypes() + ); + + this.storage = storageFactory.get(); + this.messagingService = messagingFactory.get(); // register listeners this.registerListeners(); @@ -74,6 +87,9 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin { // shutdown storage this.storage.shutdown(); + if (this.messagingService != null) + this.messagingService.shutdown(); + // close isolated class loaders this.dependencyManager.close(); } @@ -81,8 +97,14 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin { protected abstract void registerListeners(); protected abstract void setupManagers(); + protected abstract MessagingFactory getMessagingFactory(); protected abstract void registerApiOnPlatform(LightEco api); protected abstract void registerPlatformHooks(); protected abstract void removePlatformHooks(); + + @Override + public @NonNull Optional getMessagingService() { + return Optional.ofNullable(this.messagingService); + } } diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/plugin/LightEcoPlugin.java b/common/src/main/java/dev/xhyrom/lighteco/common/plugin/LightEcoPlugin.java index fe96969..0791a64 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/plugin/LightEcoPlugin.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/plugin/LightEcoPlugin.java @@ -7,10 +7,13 @@ import dev.xhyrom.lighteco.common.dependencies.DependencyManager; import dev.xhyrom.lighteco.common.manager.command.CommandManager; import dev.xhyrom.lighteco.common.manager.currency.CurrencyManager; import dev.xhyrom.lighteco.common.manager.user.UserManager; +import dev.xhyrom.lighteco.common.messaging.InternalMessagingService; import dev.xhyrom.lighteco.common.plugin.bootstrap.LightEcoBootstrap; import dev.xhyrom.lighteco.common.storage.Storage; import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Optional; + public interface LightEcoPlugin { Platform.@NonNull Type getPlatformType(); @@ -24,5 +27,7 @@ public interface LightEcoPlugin { @NonNull DependencyManager getDependencyManager(); + @NonNull Optional getMessagingService(); + @NonNull Storage getStorage(); } diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/storage/StorageFactory.java b/common/src/main/java/dev/xhyrom/lighteco/common/storage/StorageFactory.java index fce72c5..9c774e1 100644 --- a/common/src/main/java/dev/xhyrom/lighteco/common/storage/StorageFactory.java +++ b/common/src/main/java/dev/xhyrom/lighteco/common/storage/StorageFactory.java @@ -1,6 +1,5 @@ package dev.xhyrom.lighteco.common.storage; -import com.google.common.collect.ImmutableSet; import dev.xhyrom.lighteco.api.storage.StorageProvider; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import dev.xhyrom.lighteco.common.storage.provider.memory.MemoryStorageProvider; @@ -21,7 +20,7 @@ public class StorageFactory { } public Set getRequiredTypes() { - return ImmutableSet.of(this.plugin.getConfig().storage.provider); + return Set.of(this.plugin.getConfig().storage.provider); } public Storage get() { diff --git a/common/src/main/java/dev/xhyrom/lighteco/common/util/gson/GsonProvider.java b/common/src/main/java/dev/xhyrom/lighteco/common/util/gson/GsonProvider.java new file mode 100644 index 0000000..be397dd --- /dev/null +++ b/common/src/main/java/dev/xhyrom/lighteco/common/util/gson/GsonProvider.java @@ -0,0 +1,12 @@ +package dev.xhyrom.lighteco.common.util.gson; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public final class GsonProvider { + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + + public static Gson get() { + return GSON; + } +} diff --git a/sponge-8/src/main/java/dev/xhyrom/lighteco/sponge/SpongeLightEcoPlugin.java b/sponge-8/src/main/java/dev/xhyrom/lighteco/sponge/SpongeLightEcoPlugin.java index ae1fef7..89646bb 100644 --- a/sponge-8/src/main/java/dev/xhyrom/lighteco/sponge/SpongeLightEcoPlugin.java +++ b/sponge-8/src/main/java/dev/xhyrom/lighteco/sponge/SpongeLightEcoPlugin.java @@ -6,6 +6,7 @@ import dev.xhyrom.lighteco.api.platform.Platform; import dev.xhyrom.lighteco.common.manager.command.CommandManager; import dev.xhyrom.lighteco.common.manager.currency.StandardCurrencyManager; import dev.xhyrom.lighteco.common.manager.user.StandardUserManager; +import dev.xhyrom.lighteco.common.messaging.MessagingFactory; import dev.xhyrom.lighteco.common.plugin.AbstractLightEcoPlugin; import lombok.Getter; import org.checkerframework.checker.nullness.qual.NonNull; @@ -41,6 +42,11 @@ public class SpongeLightEcoPlugin extends AbstractLightEcoPlugin { //this.contextManager = new BukkitContextManager(); } + @Override + protected MessagingFactory getMessagingFactory() { + return new MessagingFactory(this); + } + @Override protected void registerApiOnPlatform(LightEco api) { //this.getBootstrap().getServer().getServicesManager().register(LightEco.class, api, this.getBootstrap(), ServicePriority.Normal);