Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified gradlew
100644 → 100755
Empty file.
31 changes: 27 additions & 4 deletions src/main/java/dev/unnm3d/rediseconomy/RedisEconomyPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
import dev.unnm3d.rediseconomy.config.ConfigManager;
import dev.unnm3d.rediseconomy.config.Langs;
import dev.unnm3d.rediseconomy.config.Settings;
import dev.unnm3d.rediseconomy.config.Settings.StorageType;
import dev.unnm3d.rediseconomy.currency.CurrenciesManager;
import dev.unnm3d.rediseconomy.migrators.*;
import dev.unnm3d.rediseconomy.redis.RedisKeys;
import dev.unnm3d.rediseconomy.redis.RedisManager;
import dev.unnm3d.rediseconomy.storage.FileStorageService;
import dev.unnm3d.rediseconomy.utils.AdventureWebuiEditorAPI;
import dev.unnm3d.rediseconomy.utils.Metrics;
import dev.unnm3d.rediseconomy.utils.PlaceholderAPIHook;
Expand Down Expand Up @@ -53,6 +55,8 @@ public final class RedisEconomyPlugin extends JavaPlugin {
@Getter
private CurrenciesManager currenciesManager;
private RedisManager redisManager;
@Getter
private FileStorageService fileStorageService;
@Nullable
@Getter
private PlayerListManager playerListManager;
Expand Down Expand Up @@ -80,7 +84,11 @@ public void onLoad() {

this.configManager = new ConfigManager(this);

if (!setupRedis()) {
if (settings().storageType == StorageType.FILE) {
getDataFolder().mkdirs();
this.fileStorageService = new FileStorageService(this);
getLogger().info("File storage mode enabled. Redis features are disabled.");
} else if (!setupRedis()) {
this.getLogger().severe("Disabling: redis server unreachable!");
this.getLogger().severe("Please setup a redis server before running this plugin!");
this.getServer().getPluginManager().disablePlugin(this);
Expand All @@ -91,7 +99,7 @@ public void onLoad() {

@Override
public void onEnable() {
if (redisManager == null) return;
if (settings().storageType == StorageType.REDIS && redisManager == null) return;
loadDebugFile();

this.scheduler = UniversalScheduler.getScheduler(this);
Expand All @@ -104,7 +112,7 @@ public void onEnable() {
}
loadPlayerList();

this.currenciesManager = new CurrenciesManager(redisManager, this, configManager);
this.currenciesManager = new CurrenciesManager(redisManager, fileStorageService, this, configManager);
this.getLogger().info("Hooked into Vault!");

if (settings().migrationEnabled) {
Expand Down Expand Up @@ -161,6 +169,14 @@ public void onEnable() {
}

new Metrics(this, 16802);

if (settings().storageType == StorageType.FILE && fileStorageService != null) {
int intervalSeconds = Math.max(1, settings().fileSaveSeconds);
getServer().getScheduler().runTaskTimerAsynchronously(this,
() -> fileStorageService.saveSnapshot(currenciesManager),
intervalSeconds * 20L, intervalSeconds * 20L);
fileStorageService.saveSnapshot(currenciesManager);
}
}

@Override
Expand All @@ -173,14 +189,17 @@ public void onDisable() {
this.getServer().getServicesManager().unregister(Economy.class, currenciesManager.getDefaultCurrency());
currenciesManager.terminate();
}
if (settings().storageType == StorageType.FILE && fileStorageService != null && currenciesManager != null) {
fileStorageService.saveSnapshot(currenciesManager);
}
getLogger().info("RedisEconomy disabled successfully!");
}

/**
* Load the player list manager if the tabOnlinePlayers setting is enabled
*/
public void loadPlayerList() {
if (configManager.getSettings().tabOnlinePlayers) {
if (configManager.getSettings().tabOnlinePlayers && settings().storageType == StorageType.REDIS) {
this.playerListManager = new PlayerListManager(this.redisManager, this);
} else if (playerListManager != null) {
playerListManager.stop();
Expand Down Expand Up @@ -220,6 +239,10 @@ private boolean setupRedis() {
}
}

public boolean isFileStorage() {
return settings().storageType == StorageType.FILE;
}

private void loadCommand(String cmdName, CommandExecutor executor, TabCompleter tabCompleter) {
PluginCommand cmd = getServer().getPluginCommand(cmdName);
if (cmd != null) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/dev/unnm3d/rediseconomy/command/MainCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command
return true;
}
if (args[0].equalsIgnoreCase("test")) {
if (plugin.isFileStorage()) {
plugin.getConfigManager().getLangs().send(sender, "§cTest command is unavailable with file storage.");
return true;
}
if (sender.hasPermission("rediseconomy.admin.test")) {
long init = System.currentTimeMillis();
sender.sendMessage("§6Processing 10000 transactions...");
Expand Down Expand Up @@ -78,6 +82,10 @@ public void run() {

if (args[0].equalsIgnoreCase("expandpool")) {
if (sender.hasPermission("rediseconomy.admin.expandpool")) {
if (plugin.isFileStorage()) {
plugin.getConfigManager().getLangs().send(sender, "§cPool expansion is not available with file storage.");
return true;
}
expandPool(sender, args[1]);
} else {
plugin.getConfigManager().getLangs().send(sender, plugin.getConfigManager().getLangs().noPermission);
Expand Down Expand Up @@ -113,6 +121,10 @@ private void reloadPlugin(CommandSender sender) {

private void expandPool(CommandSender sender, String arg) {
try {
if (plugin.getCurrenciesManager().getRedisManager() == null) {
plugin.getConfigManager().getLangs().send(sender, "§cRedis storage is not enabled.");
return;
}
plugin.getCurrenciesManager().getRedisManager().expandPool(Integer.parseInt(arg));
plugin.getConfigManager().getLangs().send(sender, "§aPool expanded successfully!");
} catch (Exception e) {
Expand Down
39 changes: 30 additions & 9 deletions src/main/java/dev/unnm3d/rediseconomy/command/PayCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,27 @@ private void payCurrency(Player sender, Currency currency, String[] args) {
.replace("%tax_percentage%", (currency.getTransactionTax() * 100) + "%")
.replace("%tax_applied%", currency.format(currency.getTransactionTax() * amount))
);
String reason = "Payment";
if (args.length >= 4) {
reason = String.join(" ", Arrays.copyOfRange(args, 3, args.length));
}
if (plugin.isFileStorage()) {
Player targetPlayer = plugin.getServer().getPlayerExact(target);
if (targetPlayer != null && targetPlayer.isOnline()) {
plugin.langs().send(targetPlayer, plugin.langs().payReceived
.replace("%player%", sender.getName())
.replace("%amount%", currency.format(amount)));
}
currenciesManager.getExchange().savePaymentTransaction(sender.getUniqueId(), targetUUID, amount, currency, reason);
RedisEconomyPlugin.debug("02 Pay msg sent locally in " + (System.currentTimeMillis() - init) + "ms. current timestamp" + System.currentTimeMillis());
return;
}
//Send msg to target
currenciesManager.getRedisManager().getConnectionAsync(commands -> {
commands.publish(MSG_CHANNEL.toString(), sender.getName() + ";;" + target + ";;" + currency.format(amount));

RedisEconomyPlugin.debug("02 Pay msg sent in " + (System.currentTimeMillis() - init) + "ms. current timestamp" + System.currentTimeMillis());
//Register transaction
String reason = "Payment";
if (args.length >= 4) {
reason = String.join(" ", Arrays.copyOfRange(args, 3, args.length));
}
return currenciesManager.getExchange().savePaymentTransaction(sender.getUniqueId(), targetUUID, amount, currency, reason);
});
}
Expand Down Expand Up @@ -170,11 +181,21 @@ private void payCurrencyAll(Player sender, Currency currency, double amount) {
.replace("%tax_applied%", currency.format(currency.getTransactionTax() * amount))
);
//Send msg to target
currenciesManager.getRedisManager().getConnectionAsync(commands -> {
commands.publish(MSG_CHANNEL.toString(), sender.getName() + ";;" + onlinePlayer.getName() + ";;" + currency.format(amount));
//Register transaction
return currenciesManager.getExchange().savePaymentTransaction(sender.getUniqueId(), onlinePlayer.getUniqueId(), amount, currency, "Payment to all online players");
});
if (plugin.isFileStorage()) {
Player targetPlayer = plugin.getServer().getPlayerExact(onlinePlayer.getName());
if (targetPlayer != null && targetPlayer.isOnline()) {
plugin.langs().send(targetPlayer, plugin.langs().payReceived
.replace("%player%", sender.getName())
.replace("%amount%", currency.format(amount)));
}
currenciesManager.getExchange().savePaymentTransaction(sender.getUniqueId(), onlinePlayer.getUniqueId(), amount, currency, "Payment to all online players");
} else {
currenciesManager.getRedisManager().getConnectionAsync(commands -> {
commands.publish(MSG_CHANNEL.toString(), sender.getName() + ";;" + onlinePlayer.getName() + ";;" + currency.format(amount));
//Register transaction
return currenciesManager.getExchange().savePaymentTransaction(sender.getUniqueId(), onlinePlayer.getUniqueId(), amount, currency, "Payment to all online players");
});
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/main/java/dev/unnm3d/rediseconomy/config/Settings.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public class Settings {
public boolean debug = false;
@Comment("A specific debug for cache update")
public boolean debugUpdateCache = false;
@Comment({"Data storage type. Use REDIS for clustered environments or FILE for standalone servers"})
public StorageType storageType = StorageType.REDIS;
@Comment("How often (in seconds) file storage should be flushed to disk. Only used with FILE storage type")
public int fileSaveSeconds = 300;
@Comment({"If 0 registerCalls is disabled, if 1 it will log only the calling class",
"if 2 it will log the calling class and method, if 3 it will log the calling class, method and line number"})
public int registerCallsVerbosity = 0;
Expand Down Expand Up @@ -72,4 +76,8 @@ public int getTryAgainCount() {
return tryAgainCount == 0 ? 3 : tryAgainCount;
}
}

public enum StorageType {
REDIS, FILE
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import dev.unnm3d.rediseconomy.redis.RedisKeys;
import dev.unnm3d.rediseconomy.redis.RedisManager;
import dev.unnm3d.rediseconomy.transaction.EconomyExchange;
import dev.unnm3d.rediseconomy.storage.FileStorageService;
import io.lettuce.core.ScoredValue;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import lombok.Getter;
Expand Down Expand Up @@ -35,25 +36,37 @@ public class CurrenciesManager extends RedisEconomyAPI implements Listener {
private final CompletableFuture<Void> completeMigration;
private final ConfigManager configManager;
@Getter
@Nullable
private final RedisManager redisManager;
private final FileStorageService fileStorageService;
private final boolean fileStorage;
private final EconomyExchange exchange;
private final HashMap<String, Currency> currencies;
@Getter
private final ConcurrentHashMap<String, UUID> nameUniqueIds;
private final ConcurrentHashMap<UUID, List<UUID>> lockedAccounts;
private volatile boolean dirty;


public CurrenciesManager(RedisManager redisManager, RedisEconomyPlugin plugin, ConfigManager configManager) {
public CurrenciesManager(@Nullable RedisManager redisManager, @Nullable FileStorageService fileStorageService, RedisEconomyPlugin plugin, ConfigManager configManager) {
INSTANCE = this;
this.completeMigration = new CompletableFuture<>();
this.redisManager = redisManager;
this.fileStorageService = fileStorageService;
this.fileStorage = plugin.isFileStorage();
this.exchange = new EconomyExchange(plugin);
this.plugin = plugin;
this.configManager = configManager;
this.currencies = new HashMap<>();
try {
this.nameUniqueIds = loadRedisNameUniqueIds().toCompletableFuture().get(plugin.getConfigManager().getSettings().redis.timeout(), TimeUnit.MILLISECONDS);
this.lockedAccounts = loadLockedAccounts().toCompletableFuture().get(plugin.getConfigManager().getSettings().redis.timeout(), TimeUnit.MILLISECONDS);
if (fileStorage && fileStorageService != null) {
this.nameUniqueIds = new ConcurrentHashMap<>(fileStorageService.loadNameUniqueIds());
this.lockedAccounts = new ConcurrentHashMap<>(fileStorageService.loadLockedAccounts());
this.dirty = true;
} else {
this.nameUniqueIds = loadRedisNameUniqueIds().toCompletableFuture().get(plugin.getConfigManager().getSettings().redis.timeout(), TimeUnit.MILLISECONDS);
this.lockedAccounts = loadLockedAccounts().toCompletableFuture().get(plugin.getConfigManager().getSettings().redis.timeout(), TimeUnit.MILLISECONDS);
}
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e);
}
Expand All @@ -69,9 +82,31 @@ public CurrenciesManager(RedisManager redisManager, RedisEconomyPlugin plugin, C
}
loadDefaultCurrency(plugin.getVaultPlugin());

registerPayMsgChannel();
registerBlockAccountChannel();
registerUpdateChannelPattern();
if (!fileStorage) {
registerPayMsgChannel();
registerBlockAccountChannel();
registerUpdateChannelPattern();
}
}

public boolean isFileStorage() {
return fileStorage;
}

public void markDirty() {
if (fileStorage) {
dirty = true;
}
}

public boolean consumeDirtyFlag() {
boolean current = dirty;
dirty = false;
return current;
}

public ConcurrentHashMap<UUID, List<UUID>> getLockedAccountsMap() {
return lockedAccounts;
}

public void loadCurrencySystem(boolean isReload) {
Expand All @@ -82,9 +117,9 @@ public void loadCurrencySystem(boolean isReload) {

Currency currency;
if (currencySettings.isBankEnabled()) {
currency = new CurrencyWithBanks(this, currencySettings);
currency = new CurrencyWithBanks(this, currencySettings, fileStorage ? fileStorageService : null);
} else {
currency = new Currency(this, currencySettings);
currency = new Currency(this, currencySettings, fileStorage ? fileStorageService : null);
}

//Replace existing currency with updated settings and terminate the old executors
Expand Down Expand Up @@ -114,7 +149,10 @@ public void loadCurrencySystem(boolean isReload) {
0.0, Double.POSITIVE_INFINITY,
0.0, true,
-1, true,
false, 2)));
false, 2), fileStorage ? fileStorageService : null));
}
if (fileStorage) {
markDirty();
}
}

Expand Down Expand Up @@ -159,6 +197,7 @@ public void loadDefaultCurrency(Plugin vaultPlugin) {

void updateNameUniqueId(String name, UUID uuid) {
nameUniqueIds.put(name, uuid);
markDirty();
}

/**
Expand All @@ -183,6 +222,7 @@ public HashMap<String, UUID> removeNamePattern(String namePattern, boolean reset
removed.forEach((name, uuid) -> currency.setPlayerBalance(uuid, name, 0.0));
}
}
markDirty();
}
return removed;
}
Expand Down Expand Up @@ -300,6 +340,10 @@ private CompletionStage<ConcurrentHashMap<UUID, List<UUID>>> loadLockedAccounts(
}

private void removeRedisNameUniqueIds(Map<String, UUID> toRemove) {
if (fileStorage) {
markDirty();
return;
}
String[] toRemoveArray = toRemove.keySet().toArray(new String[0]);
for (int i = 0; i < toRemoveArray.length; i++) {
if (toRemoveArray[i] == null) {
Expand Down Expand Up @@ -344,6 +388,10 @@ public void message(String channel, String message) {
* @param newCurrency The new currency to switch to
*/
public void switchCurrency(Currency currency, Currency newCurrency) {
if (fileStorage) {
plugin.getLogger().warning("Switch currency is not supported in file storage mode.");
return;
}
redisManager.getConnectionPipeline(asyncCommands -> {
asyncCommands.copy(RedisKeys.BALANCE_PREFIX + currency.getCurrencyName(),
RedisKeys.BALANCE_PREFIX + currency.getCurrencyName() + "_backup")
Expand Down Expand Up @@ -378,6 +426,13 @@ public CompletableFuture<Boolean> toggleAccountLock(@NotNull UUID uuid, UUID tar
locked.add(target);
}

if (fileStorage) {
lockedAccounts.put(uuid, locked);
markDirty();
future.complete(!isLocked);
return future;
}

//The string to be stored into Redis
String redisString = locked.stream().map(UUID::toString).collect(Collectors.joining(","));

Expand Down
Loading