1
0
Fork 0
mirror of https://github.com/xHyroM/lighteco.git synced 2024-11-22 15:01:05 +01:00

Merge branch 'main' into feat/better-command-abstraction

This commit is contained in:
Jozef Steinhübl 2024-05-08 18:06:17 +02:00
commit e22e370986
No known key found for this signature in database
GPG key ID: E6BC90C91973B08F
44 changed files with 752 additions and 90 deletions

View file

@ -3,10 +3,13 @@ package dev.xhyrom.lighteco.api;
import dev.xhyrom.lighteco.api.manager.CommandManager; import dev.xhyrom.lighteco.api.manager.CommandManager;
import dev.xhyrom.lighteco.api.manager.CurrencyManager; import dev.xhyrom.lighteco.api.manager.CurrencyManager;
import dev.xhyrom.lighteco.api.manager.UserManager; 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.Platform;
import dev.xhyrom.lighteco.api.platform.PlayerAdapter; import dev.xhyrom.lighteco.api.platform.PlayerAdapter;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
public interface LightEco { public interface LightEco {
/** /**
* Gets the {@link Platform}, which represents the current platform the * Gets the {@link Platform}, which represents the current platform the
@ -37,6 +40,13 @@ public interface LightEco {
*/ */
@NonNull CommandManager getCommandManager(); @NonNull CommandManager getCommandManager();
/**
* Gets the {@link MessagingService}, which manages the messaging.
*
* @return the messaging service
*/
@NonNull Optional<MessagingService> getMessagingService();
/** /**
* Gets the {@link PlayerAdapter} for a player class. * Gets the {@link PlayerAdapter} for a player class.
* *

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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() {
}
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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();
}

View file

@ -13,7 +13,7 @@ repositories {
dependencies { dependencies {
implementation(project(":lighteco-common")) implementation(project(":lighteco-common"))
implementation("dev.jorel:commandapi-bukkit-shade:9.2.0") implementation("dev.jorel:commandapi-bukkit-shade:9.3.0")
implementation("net.kyori:adventure-platform-bukkit:4.2.0") implementation("net.kyori:adventure-platform-bukkit:4.2.0")
compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT")

View file

@ -8,6 +8,7 @@ import dev.xhyrom.lighteco.bukkit.listeners.BukkitConnectionListener;
import dev.xhyrom.lighteco.bukkit.manager.BukkitCommandManager; import dev.xhyrom.lighteco.bukkit.manager.BukkitCommandManager;
import dev.xhyrom.lighteco.bukkit.manager.BukkitContextManager; import dev.xhyrom.lighteco.bukkit.manager.BukkitContextManager;
import dev.xhyrom.lighteco.common.manager.currency.StandardCurrencyManager; 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.plugin.AbstractLightEcoPlugin;
import dev.xhyrom.lighteco.common.manager.user.StandardUserManager; import dev.xhyrom.lighteco.common.manager.user.StandardUserManager;
import lombok.Getter; import lombok.Getter;
@ -45,6 +46,11 @@ public class BukkitLightEcoPlugin extends AbstractLightEcoPlugin {
this.contextManager = new BukkitContextManager(); this.contextManager = new BukkitContextManager();
} }
@Override
protected MessagingFactory getMessagingFactory() {
return new MessagingFactory(this);
}
@Override @Override
protected void registerApiOnPlatform(LightEco api) { protected void registerApiOnPlatform(LightEco api) {
this.getBootstrap().getLoader().getServer().getServicesManager().register(LightEco.class, api, this.getBootstrap().getLoader(), ServicePriority.Normal); this.getBootstrap().getLoader().getServer().getServicesManager().register(LightEco.class, api, this.getBootstrap().getLoader(), ServicePriority.Normal);

View file

@ -34,7 +34,7 @@ public class BalanceCommand implements Command {
}; };
} }
private void handleBalance(CommandSender originalSender, CommandArguments args, Currency currency) { public void handleBalance(CommandSender originalSender, CommandArguments args, Currency currency) {
BukkitCommandSender sender = new BukkitCommandSender(originalSender, this.manager.audienceFactory); BukkitCommandSender sender = new BukkitCommandSender(originalSender, this.manager.audienceFactory);
OfflinePlayer target = (OfflinePlayer) args.get("target"); OfflinePlayer target = (OfflinePlayer) args.get("target");

View file

@ -1,11 +1,11 @@
package dev.xhyrom.lighteco.bukkit.listeners; package dev.xhyrom.lighteco.bukkit.listeners;
import dev.xhyrom.lighteco.bukkit.BukkitLightEcoPlugin; import dev.xhyrom.lighteco.bukkit.BukkitLightEcoPlugin;
import dev.xhyrom.lighteco.common.model.currency.Currency;
import dev.xhyrom.lighteco.common.model.user.User; import dev.xhyrom.lighteco.common.model.user.User;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -58,11 +58,10 @@ public class BukkitConnectionListener implements Listener {
} }
this.plugin.getUserManager().saveUser(user) this.plugin.getUserManager().saveUser(user)
.thenAccept(v -> { .thenAccept(v -> this.plugin.getMessagingService().ifPresent(service -> {
// make sure the player is offline before unloading for (Currency currency : this.plugin.getCurrencyManager().getRegisteredCurrencies()) {
if (Bukkit.getPlayer(uniqueId) != null) return; service.pushUserUpdate(user, currency);
}
this.plugin.getUserManager().unload(uniqueId); }));
});
} }
} }

View file

@ -57,21 +57,17 @@ public class BukkitCommandManager extends AbstractCommandManager {
private void registerCommands(@NonNull String name, @NonNull Currency currency) { private void registerCommands(@NonNull String name, @NonNull Currency currency) {
String permissionBase = "lighteco.currency." + currency.getIdentifier() + ".command."; String permissionBase = "lighteco.currency." + currency.getIdentifier() + ".command.";
// Balance BalanceCommand balanceCommand = new BalanceCommand(this, "balance", currency, permissionBase);
for (CommandAPICommand cmd : new BalanceCommand(
this,
name,
currency,
permissionBase
).multipleBuild()) {
cmd.register();
}
CommandAPICommand cmd = new CommandAPICommand(name) CommandAPICommand cmd = new CommandAPICommand(name)
.withSubcommand(new SetCommand(this, currency, permissionBase).build()) .withSubcommand(new SetCommand(this, currency, permissionBase).build())
.withSubcommand(new GiveCommand(this, currency, permissionBase).build()) .withSubcommand(new GiveCommand(this, currency, permissionBase).build())
.withSubcommand(new TakeCommand(this, currency, permissionBase).build()) .withSubcommand(new TakeCommand(this, currency, permissionBase).build())
.withSubcommands(new BalanceCommand(this, "balance", currency, permissionBase).multipleBuild()); .withSubcommands(balanceCommand.multipleBuild())
// We want balance to be the default command
.executesPlayer((sender, args) -> {
balanceCommand.handleBalance(sender, args, currency);
});
if (currency.isPayable()) if (currency.isPayable())
cmd = cmd.withSubcommand(new PayCommand(this, currency, permissionBase).build()); cmd = cmd.withSubcommand(new PayCommand(this, currency, permissionBase).build());

View file

@ -20,6 +20,7 @@ dependencies {
implementation("eu.okaeri:okaeri-configs-validator-okaeri:5.0.0-beta.5") implementation("eu.okaeri:okaeri-configs-validator-okaeri:5.0.0-beta.5")
compileOnly("com.zaxxer:HikariCP:5.0.1") compileOnly("com.zaxxer:HikariCP:5.0.1")
compileOnly("redis.clients:jedis:5.1.0")
compileOnly("org.projectlombok:lombok:1.18.28") compileOnly("org.projectlombok:lombok:1.18.28")
annotationProcessor("org.projectlombok:lombok:1.18.28") annotationProcessor("org.projectlombok:lombok:1.18.28")

View file

@ -4,12 +4,15 @@ import dev.xhyrom.lighteco.api.LightEco;
import dev.xhyrom.lighteco.api.manager.CommandManager; import dev.xhyrom.lighteco.api.manager.CommandManager;
import dev.xhyrom.lighteco.api.manager.CurrencyManager; import dev.xhyrom.lighteco.api.manager.CurrencyManager;
import dev.xhyrom.lighteco.api.manager.UserManager; 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.Platform;
import dev.xhyrom.lighteco.api.platform.PlayerAdapter; import dev.xhyrom.lighteco.api.platform.PlayerAdapter;
import dev.xhyrom.lighteco.common.api.impl.*; import dev.xhyrom.lighteco.common.api.impl.*;
import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
public class LightEcoApi implements LightEco { public class LightEcoApi implements LightEco {
private final LightEcoPlugin plugin; private final LightEcoPlugin plugin;
@ -49,6 +52,11 @@ public class LightEcoApi implements LightEco {
return this.commandManager; return this.commandManager;
} }
@Override
public @NonNull Optional<MessagingService> getMessagingService() {
return this.plugin.getMessagingService().map(ApiMessagingService::new);
}
@Override @Override
public @NonNull <T> PlayerAdapter<T> getPlayerAdapter(@NonNull Class<T> playerClass) { public @NonNull <T> PlayerAdapter<T> getPlayerAdapter(@NonNull Class<T> playerClass) {
Class<?> expected = this.plugin.getContextManager().getPlayerClass(); Class<?> expected = this.plugin.getContextManager().getPlayerClass();

View file

@ -4,10 +4,10 @@ import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin;
public abstract class ApiAbstractManager<H> { public abstract class ApiAbstractManager<H> {
protected final LightEcoPlugin plugin; 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.plugin = plugin;
this.handler = handler; this.handle = handle;
} }
} }

View file

@ -6,8 +6,8 @@ import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
public class ApiCommandManager extends ApiAbstractManager<dev.xhyrom.lighteco.common.manager.command.CommandManager> implements CommandManager { public class ApiCommandManager extends ApiAbstractManager<dev.xhyrom.lighteco.common.manager.command.CommandManager> implements CommandManager {
public ApiCommandManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.command.CommandManager handler) { public ApiCommandManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.command.CommandManager handle) {
super(plugin, handler); super(plugin, handle);
} }
@Override @Override
@ -15,7 +15,7 @@ public class ApiCommandManager extends ApiAbstractManager<dev.xhyrom.lighteco.co
dev.xhyrom.lighteco.common.model.currency.Currency internal = this.plugin.getCurrencyManager() dev.xhyrom.lighteco.common.model.currency.Currency internal = this.plugin.getCurrencyManager()
.getIfLoaded(currency.getIdentifier()); .getIfLoaded(currency.getIdentifier());
this.handler.registerCurrencyCommand(internal); this.handle.registerCurrencyCommand(internal);
} }
@Override @Override
@ -23,6 +23,6 @@ public class ApiCommandManager extends ApiAbstractManager<dev.xhyrom.lighteco.co
dev.xhyrom.lighteco.common.model.currency.Currency internal = this.plugin.getCurrencyManager() dev.xhyrom.lighteco.common.model.currency.Currency internal = this.plugin.getCurrencyManager()
.getIfLoaded(currency.getIdentifier()); .getIfLoaded(currency.getIdentifier());
this.handler.registerCurrencyCommand(internal, main); this.handle.registerCurrencyCommand(internal, main);
} }
} }

View file

@ -8,29 +8,29 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection; import java.util.Collection;
public class ApiCurrencyManager extends ApiAbstractManager<dev.xhyrom.lighteco.common.manager.currency.CurrencyManager> implements CurrencyManager { public class ApiCurrencyManager extends ApiAbstractManager<dev.xhyrom.lighteco.common.manager.currency.CurrencyManager> implements CurrencyManager {
public ApiCurrencyManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.currency.CurrencyManager handler) { public ApiCurrencyManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.currency.CurrencyManager handle) {
super(plugin, handler); super(plugin, handle);
} }
private Currency wrap(dev.xhyrom.lighteco.common.model.currency.Currency handler) { private Currency wrap(dev.xhyrom.lighteco.common.model.currency.Currency handle) {
return handler.getProxy(); return handle.getProxy();
} }
@Override @Override
public @NonNull Collection<Currency> getRegisteredCurrencies() { public @NonNull Collection<Currency> getRegisteredCurrencies() {
return this.handler.values() return this.handle.values()
.stream().map(this::wrap) .stream().map(this::wrap)
.toList(); .toList();
} }
@Override @Override
public Currency getCurrency(@NonNull String identifier) { public Currency getCurrency(@NonNull String identifier) {
return wrap(this.handler.getIfLoaded(identifier)); return wrap(this.handle.getIfLoaded(identifier));
} }
@Override @Override
public void registerCurrency(@NonNull Currency currency) { public void registerCurrency(@NonNull Currency currency) {
dev.xhyrom.lighteco.common.model.currency.Currency internal = new dev.xhyrom.lighteco.common.model.currency.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);
} }
} }

View file

@ -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);
}
}

View file

@ -10,60 +10,68 @@ import java.math.BigDecimal;
import java.util.UUID; import java.util.UUID;
public class ApiUser implements User { 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) { throw new IllegalArgumentException("Cannot cast " + u.getClass().getName() + " to " + ApiUser.class.getName());
this.handler = handler; }
private final dev.xhyrom.lighteco.common.model.user.User handle;
public ApiUser(dev.xhyrom.lighteco.common.model.user.User handle) {
this.handle = handle;
} }
@Override @Override
public @NonNull UUID getUniqueId() { public @NonNull UUID getUniqueId() {
return this.handler.getUniqueId(); return this.handle.getUniqueId();
} }
@Override @Override
public @Nullable String getUsername() { public @Nullable String getUsername() {
return this.handler.getUsername(); return this.handle.getUsername();
} }
@Override @Override
public @NonNull BigDecimal getBalance(@NonNull Currency currency) { 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() .getCurrencyManager()
.getIfLoaded(currency.getIdentifier()); .getIfLoaded(currency.getIdentifier());
return this.handler.getBalance(internal); return this.handle.getBalance(internal);
} }
@Override @Override
public void setBalance(@NonNull Currency currency, @NonNull BigDecimal balance) { 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() .getCurrencyManager()
.getIfLoaded(currency.getIdentifier()); .getIfLoaded(currency.getIdentifier());
this.handler.setBalance(internal, balance); this.handle.setBalance(internal, balance);
} }
@Override @Override
public void deposit(@NonNull Currency currency, @NonNull BigDecimal amount) { 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() .getCurrencyManager()
.getIfLoaded(currency.getIdentifier()); .getIfLoaded(currency.getIdentifier());
this.handler.deposit(internal, amount); this.handle.deposit(internal, amount);
} }
@Override @Override
public void withdraw(@NonNull Currency currency, @NonNull BigDecimal amount) { 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() .getCurrencyManager()
.getIfLoaded(currency.getIdentifier()); .getIfLoaded(currency.getIdentifier());
this.handler.withdraw(internal, amount); this.handle.withdraw(internal, amount);
} }
@Override @Override
public void sendMessage(Component message) { public void sendMessage(Component message) {
this.handler.sendMessage(message); this.handle.sendMessage(message);
} }
} }

View file

@ -10,13 +10,13 @@ import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class ApiUserManager extends ApiAbstractManager<dev.xhyrom.lighteco.common.manager.user.UserManager> implements UserManager { public class ApiUserManager extends ApiAbstractManager<dev.xhyrom.lighteco.common.manager.user.UserManager> implements UserManager {
public ApiUserManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.user.UserManager handler) { public ApiUserManager(LightEcoPlugin plugin, dev.xhyrom.lighteco.common.manager.user.UserManager handle) {
super(plugin, handler); super(plugin, handle);
} }
private User wrap(dev.xhyrom.lighteco.common.model.user.User handler) { private User wrap(dev.xhyrom.lighteco.common.model.user.User handle) {
this.plugin.getUserManager().getHousekeeper().registerUsage(handler.getUniqueId()); this.plugin.getUserManager().getHousekeeper().registerUsage(handle.getUniqueId());
return handler.getProxy(); return handle.getProxy();
} }
@Override @Override
@ -42,11 +42,11 @@ public class ApiUserManager extends ApiAbstractManager<dev.xhyrom.lighteco.commo
@Override @Override
public @Nullable User getUser(@NonNull UUID uniqueId) { public @Nullable User getUser(@NonNull UUID uniqueId) {
return wrap(this.handler.getIfLoaded(uniqueId)); return wrap(this.handle.getIfLoaded(uniqueId));
} }
@Override @Override
public boolean isLoaded(@NonNull UUID uniqueId) { public boolean isLoaded(@NonNull UUID uniqueId) {
return this.handler.isLoaded(uniqueId); return this.handle.isLoaded(uniqueId);
} }
} }

View file

@ -2,6 +2,7 @@ package dev.xhyrom.lighteco.common.config;
import dev.xhyrom.lighteco.common.config.housekeeper.HousekeeperConfig; import dev.xhyrom.lighteco.common.config.housekeeper.HousekeeperConfig;
import dev.xhyrom.lighteco.common.config.message.MessageConfig; import dev.xhyrom.lighteco.common.config.message.MessageConfig;
import dev.xhyrom.lighteco.common.config.messaging.MessagingConfig;
import dev.xhyrom.lighteco.common.config.storage.StorageConfig; import dev.xhyrom.lighteco.common.config.storage.StorageConfig;
import eu.okaeri.configs.OkaeriConfig; import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment; import eu.okaeri.configs.annotation.Comment;
@ -20,6 +21,9 @@ public class Config extends OkaeriConfig {
@Comment("Storage settings.") @Comment("Storage settings.")
public StorageConfig storage = new StorageConfig(); public StorageConfig storage = new StorageConfig();
@Comment("Messaging settings.")
public MessagingConfig messaging = new MessagingConfig();
@Comment("Save interval to storage in seconds.") @Comment("Save interval to storage in seconds.")
public long saveInterval = 5L; public long saveInterval = 5L;

View file

@ -0,0 +1,15 @@
package dev.xhyrom.lighteco.common.config.messaging;
import dev.xhyrom.lighteco.common.messaging.MessagingType;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
public class MessagingConfig extends OkaeriConfig {
@Comment("Messaging provider.")
@Comment("Available providers: redis")
public MessagingType provider = MessagingType.NONE;
@Comment("Data storage settings.")
@Comment("You don't need to worry about this if you're using plugin message.")
public MessagingDataConfig data = new MessagingDataConfig();
}

View file

@ -0,0 +1,16 @@
package dev.xhyrom.lighteco.common.config.messaging;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
public class MessagingDataConfig extends OkaeriConfig {
@Comment("Define the address and port of the messaging service.")
public String address = "localhost";
@Comment("Credentials for connecting to the messaging service.")
public String username = "root";
public String password = "password";
@Comment("Whether to use SSL to connect to the messaging service.")
public boolean ssl = false;
}

View file

@ -61,6 +61,29 @@ public enum Dependency {
"postgresql", "postgresql",
"42.6.0", "42.6.0",
Relocation.of("postgresql", "org{}postgresql") Relocation.of("postgresql", "org{}postgresql")
),
JEDIS(
"redis.clients",
"jedis",
"5.1.0",
Relocation.of("jedis", "redis{}clients{}jedis"),
Relocation.of("commonspool2", "org{}apache{}commons{}pool2")
),
SLF4J_SIMPLE(
"org.slf4j",
"slf4j-simple",
"1.7.30"
),
SLF4J_API(
"org.slf4j",
"slf4j-api",
"1.7.30"
),
COMMONS_POOL_2(
"org.apache.commons",
"commons-pool2",
"2.9.0",
Relocation.of("commonspool2", "org{}apache{}commons{}pool2")
); );
private final String fullPath; private final String fullPath;

View file

@ -1,5 +1,6 @@
package dev.xhyrom.lighteco.common.dependencies; package dev.xhyrom.lighteco.common.dependencies;
import dev.xhyrom.lighteco.common.messaging.MessagingType;
import dev.xhyrom.lighteco.common.storage.StorageType; import dev.xhyrom.lighteco.common.storage.StorageType;
import java.util.Set; import java.util.Set;
@ -8,6 +9,7 @@ public interface DependencyManager extends AutoCloseable {
void loadDependencies(Set<Dependency> dependencies); void loadDependencies(Set<Dependency> dependencies);
void loadStorageDependencies(Set<StorageType> types); void loadStorageDependencies(Set<StorageType> types);
void loadMessagingDependencies(Set<MessagingType> types);
ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies); ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies);

View file

@ -5,10 +5,11 @@ import com.google.common.io.MoreFiles;
import dev.xhyrom.lighteco.common.config.Config; import dev.xhyrom.lighteco.common.config.Config;
import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation; import dev.xhyrom.lighteco.common.dependencies.relocation.Relocation;
import dev.xhyrom.lighteco.common.dependencies.relocation.RelocationHandler; 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.LightEcoPlugin;
import dev.xhyrom.lighteco.common.plugin.logger.PluginLogger; 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.storage.StorageType;
import dev.xhyrom.lighteco.common.util.URLClassLoaderAccess;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import java.io.IOException; import java.io.IOException;
@ -17,8 +18,6 @@ import java.net.URLClassLoader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
public class DependencyManagerImpl implements DependencyManager { public class DependencyManagerImpl implements DependencyManager {
private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class); private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class);
@ -49,42 +48,29 @@ public class DependencyManagerImpl implements DependencyManager {
@Override @Override
public void loadDependencies(Set<Dependency> dependencies) { public void loadDependencies(Set<Dependency> dependencies) {
CountDownLatch latch = new CountDownLatch(dependencies.size());
if (this.config.debug) if (this.config.debug)
this.logger.info("Loading dependencies: " + dependencies); this.logger.info("Loading dependencies: " + dependencies);
for (Dependency dependency : dependencies) { for (Dependency dependency : dependencies) {
if (this.loaded.containsKey(dependency)) { if (this.loaded.containsKey(dependency)) {
latch.countDown();
continue; 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) if (this.config.debug)
this.logger.info("Loaded dependencies: " + dependencies); this.logger.info("Loading dependency " + dependency);
} catch (InterruptedException e) {
throw new RuntimeException(e); 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 { private void loadDependency(Dependency dependency) throws Exception {
@ -142,6 +128,11 @@ public class DependencyManagerImpl implements DependencyManager {
loadDependencies(this.registry.resolveStorageDependencies(types)); loadDependencies(this.registry.resolveStorageDependencies(types));
} }
@Override
public void loadMessagingDependencies(Set<MessagingType> types) {
loadDependencies(this.registry.resolveMessagingDependencies(types));
}
@Override @Override
public ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies) { public ClassLoader obtainClassLoaderWith(Set<Dependency> dependencies) {
ImmutableSet<Dependency> set = ImmutableSet.copyOf(dependencies); ImmutableSet<Dependency> set = ImmutableSet.copyOf(dependencies);

View file

@ -2,6 +2,7 @@ package dev.xhyrom.lighteco.common.dependencies;
import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.SetMultimap; import com.google.common.collect.SetMultimap;
import dev.xhyrom.lighteco.common.messaging.MessagingType;
import dev.xhyrom.lighteco.common.storage.StorageType; import dev.xhyrom.lighteco.common.storage.StorageType;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -16,6 +17,10 @@ public class DependencyRegistry {
.putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.HIKARI) .putAll(StorageType.POSTGRESQL, Dependency.POSTGRESQL_DRIVER, Dependency.HIKARI)
.build(); .build();
private static final SetMultimap<MessagingType, Dependency> MESSAGING_DEPENDENCIES = ImmutableSetMultimap.<MessagingType, Dependency>builder()
.putAll(MessagingType.REDIS, Dependency.COMMONS_POOL_2, Dependency.JEDIS, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE)
.build();
public Set<Dependency> resolveStorageDependencies(Set<StorageType> types) { public Set<Dependency> resolveStorageDependencies(Set<StorageType> types) {
Set<Dependency> dependencies = new LinkedHashSet<>(); Set<Dependency> dependencies = new LinkedHashSet<>();
@ -26,6 +31,16 @@ public class DependencyRegistry {
return dependencies; return dependencies;
} }
public Set<Dependency> resolveMessagingDependencies(Set<MessagingType> types) {
Set<Dependency> dependencies = new LinkedHashSet<>();
for (MessagingType type : types) {
dependencies.addAll(MESSAGING_DEPENDENCIES.get(type));
}
return dependencies;
}
public boolean shouldAutoLoad(Dependency dependency) { public boolean shouldAutoLoad(Dependency dependency) {
return switch (dependency) { return switch (dependency) {
case H2_DRIVER, SQLITE_DRIVER -> false; case H2_DRIVER, SQLITE_DRIVER -> false;

View file

@ -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();
}

View file

@ -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<UUID> 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();
}
}

View file

@ -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<MessagingType> 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;
};
}
}

View file

@ -0,0 +1,6 @@
package dev.xhyrom.lighteco.common.messaging;
public enum MessagingType {
NONE,
REDIS
}

View file

@ -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;
}
}

View file

@ -0,0 +1,5 @@
package dev.xhyrom.lighteco.common.messaging.message;
public enum MessageType {
USER_UPDATE;
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -4,6 +4,7 @@ import dev.xhyrom.lighteco.api.exception.CannotBeGreaterThan;
import dev.xhyrom.lighteco.api.exception.CannotBeNegative; import dev.xhyrom.lighteco.api.exception.CannotBeNegative;
import dev.xhyrom.lighteco.common.api.impl.ApiUser; import dev.xhyrom.lighteco.common.api.impl.ApiUser;
import dev.xhyrom.lighteco.common.cache.RedisBackedMap; 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.model.currency.Currency;
import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin;
import lombok.Getter; import lombok.Getter;
@ -14,6 +15,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.HashMap; import java.util.HashMap;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@Getter @Getter
@ -50,10 +52,14 @@ public class User {
} }
public void setBalance(@NonNull Currency currency, @NonNull BigDecimal balance) throws CannotBeNegative, CannotBeGreaterThan { 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 { 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) { if (balance.compareTo(BigDecimal.ZERO) < 0) {
throw new CannotBeNegative("Balance cannot be negative"); throw new CannotBeNegative("Balance cannot be negative");
} }
@ -67,6 +73,11 @@ public class User {
if (!force) if (!force)
this.setDirty(true); this.setDirty(true);
if (publish) {
@NonNull Optional<InternalMessagingService> messagingService = this.plugin.getMessagingService();
messagingService.ifPresent(internalMessagingService -> internalMessagingService.pushUserUpdate(this, currency));
}
} }
public void deposit(@NonNull Currency currency, @NonNull BigDecimal amount) throws CannotBeNegative, CannotBeGreaterThan { public void deposit(@NonNull Currency currency, @NonNull BigDecimal amount) throws CannotBeNegative, CannotBeGreaterThan {

View file

@ -6,13 +6,17 @@ import dev.xhyrom.lighteco.common.api.LightEcoApi;
import dev.xhyrom.lighteco.common.config.Config; import dev.xhyrom.lighteco.common.config.Config;
import dev.xhyrom.lighteco.common.dependencies.DependencyManager; import dev.xhyrom.lighteco.common.dependencies.DependencyManager;
import dev.xhyrom.lighteco.common.dependencies.DependencyManagerImpl; 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.Storage;
import dev.xhyrom.lighteco.common.storage.StorageFactory; import dev.xhyrom.lighteco.common.storage.StorageFactory;
import dev.xhyrom.lighteco.common.task.UserSaveTask; import dev.xhyrom.lighteco.common.task.UserSaveTask;
import eu.okaeri.configs.ConfigManager; import eu.okaeri.configs.ConfigManager;
import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer; import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer;
import lombok.Getter; import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Getter @Getter
@ -23,6 +27,7 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin {
@Getter @Getter
private Storage storage; private Storage storage;
private InternalMessagingService messagingService;
private LightEcoApi api; private LightEcoApi api;
private UserSaveTask userSaveTask; private UserSaveTask userSaveTask;
@ -41,10 +46,18 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin {
public final void enable() { public final void enable() {
// setup storage // setup storage
StorageFactory factory = new StorageFactory(this); StorageFactory storageFactory = new StorageFactory(this);
this.dependencyManager.loadStorageDependencies(factory.getRequiredTypes()); 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 // register listeners
this.registerListeners(); this.registerListeners();
@ -74,6 +87,9 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin {
// shutdown storage // shutdown storage
this.storage.shutdown(); this.storage.shutdown();
if (this.messagingService != null)
this.messagingService.shutdown();
// close isolated class loaders // close isolated class loaders
this.dependencyManager.close(); this.dependencyManager.close();
} }
@ -81,8 +97,14 @@ public abstract class AbstractLightEcoPlugin implements LightEcoPlugin {
protected abstract void registerListeners(); protected abstract void registerListeners();
protected abstract void setupManagers(); protected abstract void setupManagers();
protected abstract MessagingFactory getMessagingFactory();
protected abstract void registerApiOnPlatform(LightEco api); protected abstract void registerApiOnPlatform(LightEco api);
protected abstract void registerPlatformHooks(); protected abstract void registerPlatformHooks();
protected abstract void removePlatformHooks(); protected abstract void removePlatformHooks();
@Override
public @NonNull Optional<InternalMessagingService> getMessagingService() {
return Optional.ofNullable(this.messagingService);
}
} }

View file

@ -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.command.CommandManager;
import dev.xhyrom.lighteco.common.manager.currency.CurrencyManager; import dev.xhyrom.lighteco.common.manager.currency.CurrencyManager;
import dev.xhyrom.lighteco.common.manager.user.UserManager; 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.plugin.bootstrap.LightEcoBootstrap;
import dev.xhyrom.lighteco.common.storage.Storage; import dev.xhyrom.lighteco.common.storage.Storage;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Optional;
public interface LightEcoPlugin { public interface LightEcoPlugin {
Platform.@NonNull Type getPlatformType(); Platform.@NonNull Type getPlatformType();
@ -24,5 +27,7 @@ public interface LightEcoPlugin {
@NonNull DependencyManager getDependencyManager(); @NonNull DependencyManager getDependencyManager();
@NonNull Optional<InternalMessagingService> getMessagingService();
@NonNull Storage getStorage(); @NonNull Storage getStorage();
} }

View file

@ -1,6 +1,5 @@
package dev.xhyrom.lighteco.common.storage; package dev.xhyrom.lighteco.common.storage;
import com.google.common.collect.ImmutableSet;
import dev.xhyrom.lighteco.api.storage.StorageProvider; import dev.xhyrom.lighteco.api.storage.StorageProvider;
import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin; import dev.xhyrom.lighteco.common.plugin.LightEcoPlugin;
import dev.xhyrom.lighteco.common.storage.provider.memory.MemoryStorageProvider; import dev.xhyrom.lighteco.common.storage.provider.memory.MemoryStorageProvider;
@ -21,7 +20,7 @@ public class StorageFactory {
} }
public Set<StorageType> getRequiredTypes() { public Set<StorageType> getRequiredTypes() {
return ImmutableSet.of(this.plugin.getConfig().storage.provider); return Set.of(this.plugin.getConfig().storage.provider);
} }
public Storage get() { public Storage get() {

View file

@ -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;
}
}

View file

@ -1,6 +1,6 @@
#Sun Jul 30 15:47:43 CEST 2023 #Sun Jul 30 15:47:43 CEST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View file

@ -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.command.CommandManager;
import dev.xhyrom.lighteco.common.manager.currency.StandardCurrencyManager; import dev.xhyrom.lighteco.common.manager.currency.StandardCurrencyManager;
import dev.xhyrom.lighteco.common.manager.user.StandardUserManager; import dev.xhyrom.lighteco.common.manager.user.StandardUserManager;
import dev.xhyrom.lighteco.common.messaging.MessagingFactory;
import dev.xhyrom.lighteco.common.plugin.AbstractLightEcoPlugin; import dev.xhyrom.lighteco.common.plugin.AbstractLightEcoPlugin;
import lombok.Getter; import lombok.Getter;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@ -41,6 +42,11 @@ public class SpongeLightEcoPlugin extends AbstractLightEcoPlugin {
//this.contextManager = new BukkitContextManager(); //this.contextManager = new BukkitContextManager();
} }
@Override
protected MessagingFactory getMessagingFactory() {
return new MessagingFactory(this);
}
@Override @Override
protected void registerApiOnPlatform(LightEco api) { protected void registerApiOnPlatform(LightEco api) {
//this.getBootstrap().getServer().getServicesManager().register(LightEco.class, api, this.getBootstrap(), ServicePriority.Normal); //this.getBootstrap().getServer().getServicesManager().register(LightEco.class, api, this.getBootstrap(), ServicePriority.Normal);