mirror of
https://github.com/EssentialsX/Essentials.git
synced 2024-11-15 07:05:52 +01:00
Add Baltop API (#3702)
Co-authored-by: Mariell <proximyst@proximyst.com> Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com> This moves storage of balances from the baltop command into the UserMap. This was needed by Glare to able to get a hold of all users balances without causing jvm hell on the usermap. To access this API as an end user; ```java import net.essentialsx.api.v2.services.BalanceTop; //... BalanceTop api = Bukkit.getServer().getServicesManager().load(BalanceTop.class); ``` Closes #3100, closes #3540
This commit is contained in:
parent
191cea7fb3
commit
36422ab22b
@ -0,0 +1,87 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import net.ess3.api.IEssentials;
|
||||
import net.essentialsx.api.v2.services.BalanceTop;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class BalanceTopImpl implements BalanceTop {
|
||||
private final IEssentials ess;
|
||||
private LinkedHashMap<UUID, BalanceTop.Entry> topCache = new LinkedHashMap<>();
|
||||
private BigDecimal balanceTopTotal = BigDecimal.ZERO;
|
||||
private long cacheAge = 0;
|
||||
private CompletableFuture<Void> cacheLock;
|
||||
|
||||
public BalanceTopImpl(IEssentials ess) {
|
||||
this.ess = ess;
|
||||
ess.getServer().getServicesManager().register(BalanceTop.class, this, ess, ServicePriority.Normal);
|
||||
}
|
||||
|
||||
private void calculateBalanceTopMap() {
|
||||
final List<Entry> entries = new LinkedList<>();
|
||||
BigDecimal newTotal = BigDecimal.ZERO;
|
||||
for (UUID u : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
if (user != null) {
|
||||
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
|
||||
// Don't list NPCs in output
|
||||
continue;
|
||||
}
|
||||
if (!user.isBaltopExempt()) {
|
||||
final BigDecimal userMoney = user.getMoney();
|
||||
user.updateMoneyCache(userMoney);
|
||||
newTotal = newTotal.add(userMoney);
|
||||
final String name = user.isHidden() ? user.getName() : user.getDisplayName();
|
||||
entries.add(new BalanceTop.Entry(user.getBase().getUniqueId(), name, userMoney));
|
||||
}
|
||||
}
|
||||
}
|
||||
final LinkedHashMap<UUID, Entry> sortedMap = new LinkedHashMap<>();
|
||||
entries.sort((entry1, entry2) -> entry2.getBalance().compareTo(entry1.getBalance()));
|
||||
for (Entry entry : entries) {
|
||||
sortedMap.put(entry.getUuid(), entry);
|
||||
}
|
||||
topCache = sortedMap;
|
||||
balanceTopTotal = newTotal;
|
||||
cacheAge = System.currentTimeMillis();
|
||||
cacheLock.complete(null);
|
||||
cacheLock = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> calculateBalanceTopMapAsync() {
|
||||
if (cacheLock != null) {
|
||||
return cacheLock;
|
||||
}
|
||||
cacheLock = new CompletableFuture<>();
|
||||
ess.runTaskAsynchronously(this::calculateBalanceTopMap);
|
||||
return cacheLock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<UUID, Entry> getBalanceTopCache() {
|
||||
return Collections.unmodifiableMap(topCache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCacheAge() {
|
||||
return cacheAge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getBalanceTopTotal() {
|
||||
return balanceTopTotal;
|
||||
}
|
||||
|
||||
public boolean isCacheLocked() {
|
||||
return cacheLock != null;
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ import net.ess3.provider.providers.PaperKnownCommandsProvider;
|
||||
import net.ess3.provider.providers.PaperMaterialTagProvider;
|
||||
import net.ess3.provider.providers.PaperRecipeBookListener;
|
||||
import net.ess3.provider.providers.PaperServerStateProvider;
|
||||
import net.essentialsx.api.v2.services.BalanceTop;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
@ -127,6 +128,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
private transient PermissionsHandler permissionsHandler;
|
||||
private transient AlternativeCommandsHandler alternativeCommandsHandler;
|
||||
private transient UserMap userMap;
|
||||
private transient BalanceTopImpl balanceTop;
|
||||
private transient ExecuteTimer execTimer;
|
||||
private transient I18n i18n;
|
||||
private transient MetricsWrapper metrics;
|
||||
@ -181,6 +183,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
LOGGER.log(Level.INFO, dataFolder.toString());
|
||||
settings = new Settings(this);
|
||||
userMap = new UserMap(this);
|
||||
balanceTop = new BalanceTopImpl(this);
|
||||
permissionsHandler = new PermissionsHandler(this, false);
|
||||
Economy.setEss(this);
|
||||
confList = new ArrayList<>();
|
||||
@ -246,6 +249,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
confList.add(userMap);
|
||||
execTimer.mark("Init(Usermap)");
|
||||
|
||||
balanceTop = new BalanceTopImpl(this);
|
||||
execTimer.mark("Init(BalanceTop)");
|
||||
|
||||
kits = new Kits(this);
|
||||
confList.add(kits);
|
||||
upgrade.convertKits();
|
||||
@ -968,6 +974,11 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
return userMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BalanceTop getBalanceTop() {
|
||||
return balanceTop;
|
||||
}
|
||||
|
||||
@Override
|
||||
public I18n getI18n() {
|
||||
return i18n;
|
||||
|
@ -10,6 +10,7 @@ import net.ess3.provider.KnownCommandsProvider;
|
||||
import net.ess3.provider.ServerStateProvider;
|
||||
import net.ess3.provider.SpawnerBlockProvider;
|
||||
import net.ess3.provider.SpawnerItemProvider;
|
||||
import net.essentialsx.api.v2.services.BalanceTop;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -95,6 +96,8 @@ public interface IEssentials extends Plugin {
|
||||
|
||||
UserMap getUserMap();
|
||||
|
||||
BalanceTop getBalanceTop();
|
||||
|
||||
EssentialsTimer getTimer();
|
||||
|
||||
/**
|
||||
|
@ -1041,6 +1041,15 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
this.lastHomeConfirmationTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isBaltopExempt() {
|
||||
if (getBase().isOnline()) {
|
||||
final boolean exempt = isAuthorized("essentials.balancetop.exclude");
|
||||
setBaltopExemptCache(exempt);
|
||||
return exempt;
|
||||
}
|
||||
return isBaltopExcludeCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getTargetBlock(int maxDistance) {
|
||||
final Block block;
|
||||
|
@ -70,6 +70,7 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
private Boolean confirmPay;
|
||||
private Boolean confirmClear;
|
||||
private boolean lastMessageReplyRecipient;
|
||||
private boolean baltopExemptCache;
|
||||
|
||||
protected UserData(final Player base, final IEssentials ess) {
|
||||
super(base);
|
||||
@ -141,6 +142,7 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
confirmPay = _getConfirmPay();
|
||||
confirmClear = _getConfirmClear();
|
||||
lastMessageReplyRecipient = _getLastMessageReplyRecipient();
|
||||
baltopExemptCache = _getBaltopExcludeCache();
|
||||
}
|
||||
|
||||
private BigDecimal _getMoney() {
|
||||
@ -1027,6 +1029,20 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
save();
|
||||
}
|
||||
|
||||
public boolean _getBaltopExcludeCache() {
|
||||
return config.getBoolean("baltop-exempt", false);
|
||||
}
|
||||
|
||||
public boolean isBaltopExcludeCache() {
|
||||
return baltopExemptCache;
|
||||
}
|
||||
|
||||
public void setBaltopExemptCache(boolean baltopExempt) {
|
||||
this.baltopExemptCache = baltopExempt;
|
||||
config.setProperty("baltop-exempt", baltopExempt);
|
||||
config.save();
|
||||
}
|
||||
|
||||
public UUID getConfigUUID() {
|
||||
return config.uuid;
|
||||
}
|
||||
|
@ -1,40 +1,35 @@
|
||||
package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.textreader.SimpleTextInput;
|
||||
import com.earth2me.essentials.textreader.TextPager;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.essentialsx.api.v2.services.BalanceTop;
|
||||
import org.bukkit.Server;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
public class Commandbalancetop extends EssentialsCommand {
|
||||
public static final int MINUSERS = 50;
|
||||
private static final int CACHETIME = 2 * 60 * 1000;
|
||||
private static final SimpleTextInput cache = new SimpleTextInput();
|
||||
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private static long cacheage = 0;
|
||||
private static SimpleTextInput cache = new SimpleTextInput();
|
||||
|
||||
public Commandbalancetop() {
|
||||
super("balancetop");
|
||||
}
|
||||
|
||||
private static void outputCache(final CommandSource sender, final int page) {
|
||||
private void outputCache(final CommandSource sender, final int page) {
|
||||
final Calendar cal = Calendar.getInstance();
|
||||
cal.setTimeInMillis(cacheage);
|
||||
cal.setTimeInMillis(ess.getBalanceTop().getCacheAge());
|
||||
final DateFormat format = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
|
||||
sender.sendMessage(tl("balanceTop", format.format(cal.getTime())));
|
||||
new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender);
|
||||
@ -54,25 +49,17 @@ public class Commandbalancetop extends EssentialsCommand {
|
||||
}
|
||||
}
|
||||
|
||||
if (!force && lock.readLock().tryLock()) {
|
||||
try {
|
||||
if (cacheage > System.currentTimeMillis() - CACHETIME) {
|
||||
outputCache(sender, page);
|
||||
return;
|
||||
}
|
||||
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
||||
}
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
} else {
|
||||
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
||||
}
|
||||
if (!force && ess.getBalanceTop().getCacheAge() > System.currentTimeMillis() - CACHETIME) {
|
||||
outputCache(sender, page);
|
||||
return;
|
||||
}
|
||||
ess.runTaskAsynchronously(new Viewer(sender, commandLabel, page, force));
|
||||
|
||||
// If there are less than 50 users in our usermap, there is no need to display a warning as these calculations should be done quickly
|
||||
if (ess.getUserMap().getUniqueUsers() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
||||
}
|
||||
|
||||
ess.runTaskAsynchronously(new Viewer(sender, page, force));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -88,89 +75,42 @@ public class Commandbalancetop extends EssentialsCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private class Calculator implements Runnable {
|
||||
private final transient Viewer viewer;
|
||||
private final boolean force;
|
||||
|
||||
Calculator(final Viewer viewer, final boolean force) {
|
||||
this.viewer = viewer;
|
||||
this.force = force;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
if (force || cacheage <= System.currentTimeMillis() - CACHETIME) {
|
||||
cache.getLines().clear();
|
||||
final Map<String, BigDecimal> balances = new HashMap<>();
|
||||
BigDecimal totalMoney = BigDecimal.ZERO;
|
||||
if (ess.getSettings().isEcoDisabled()) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Internal economy functions disabled, aborting baltop.");
|
||||
}
|
||||
} else {
|
||||
for (final UUID u : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
if (user != null) {
|
||||
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
|
||||
// Don't list NPCs in output
|
||||
continue;
|
||||
}
|
||||
if (!user.isAuthorized("essentials.balancetop.exclude")) {
|
||||
final BigDecimal userMoney = user.getMoney();
|
||||
user.updateMoneyCache(userMoney);
|
||||
totalMoney = totalMoney.add(userMoney);
|
||||
final String name = user.isHidden() ? user.getName() : user.getDisplayName();
|
||||
balances.put(name, userMoney);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final List<Map.Entry<String, BigDecimal>> sortedEntries = new ArrayList<>(balances.entrySet());
|
||||
sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));
|
||||
|
||||
cache.getLines().add(tl("serverTotal", NumberUtil.displayCurrency(totalMoney, ess)));
|
||||
int pos = 1;
|
||||
for (final Map.Entry<String, BigDecimal> entry : sortedEntries) {
|
||||
cache.getLines().add(tl("balanceTopLine", pos, entry.getKey(), NumberUtil.displayCurrency(entry.getValue(), ess)));
|
||||
pos++;
|
||||
}
|
||||
cacheage = System.currentTimeMillis();
|
||||
}
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
ess.runTaskAsynchronously(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
private class Viewer implements Runnable {
|
||||
private final transient CommandSource sender;
|
||||
private final transient int page;
|
||||
private final transient boolean force;
|
||||
private final transient String commandLabel;
|
||||
|
||||
Viewer(final CommandSource sender, final String commandLabel, final int page, final boolean force) {
|
||||
Viewer(final CommandSource sender, final int page, final boolean force) {
|
||||
this.sender = sender;
|
||||
this.page = page;
|
||||
this.force = force;
|
||||
this.commandLabel = commandLabel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
lock.readLock().lock();
|
||||
try {
|
||||
if (!force && cacheage > System.currentTimeMillis() - CACHETIME) {
|
||||
outputCache(sender, page);
|
||||
return;
|
||||
if (ess.getSettings().isEcoDisabled()) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Internal economy functions disabled, aborting baltop.");
|
||||
}
|
||||
} finally {
|
||||
lock.readLock().unlock();
|
||||
return;
|
||||
}
|
||||
ess.runTaskAsynchronously(new Calculator(new Viewer(sender, commandLabel, page, false), force));
|
||||
|
||||
final boolean fresh = force || ess.getBalanceTop().isCacheLocked() || ess.getBalanceTop().getCacheAge() <= System.currentTimeMillis() - CACHETIME;
|
||||
final CompletableFuture<Void> future = fresh ? ess.getBalanceTop().calculateBalanceTopMapAsync() : CompletableFuture.completedFuture(null);
|
||||
future.thenRun(() -> {
|
||||
if (fresh) {
|
||||
final SimpleTextInput newCache = new SimpleTextInput();
|
||||
newCache.getLines().add(tl("serverTotal", NumberUtil.displayCurrency(ess.getBalanceTop().getBalanceTopTotal(), ess)));
|
||||
int pos = 1;
|
||||
for (final Map.Entry<UUID, BalanceTop.Entry> entry : ess.getBalanceTop().getBalanceTopCache().entrySet()) {
|
||||
newCache.getLines().add(tl("balanceTopLine", pos, entry.getValue().getDisplayName(), NumberUtil.displayCurrency(entry.getValue().getBalance(), ess)));
|
||||
pos++;
|
||||
}
|
||||
cache = newCache;
|
||||
}
|
||||
|
||||
outputCache(sender, page);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
package net.essentialsx.api.v2.services;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A class which provides numerous methods to interact with Essentials' balance top calculations.
|
||||
* <p>
|
||||
* Note: Implementations of this class should be thread-safe and thus do not need to be called from the server thread.
|
||||
*/
|
||||
public interface BalanceTop {
|
||||
/**
|
||||
* Re-calculates the balance top cache asynchronously.
|
||||
* <p>
|
||||
* This method will return a {@link CompletableFuture CompletableFuture<Void>} which
|
||||
* will be completed upon the recalculation of the balance top map.
|
||||
* After which you should run {@link BalanceTop#getBalanceTopCache()}
|
||||
* to get the newly updated cache
|
||||
*
|
||||
* @return A future which completes after the balance top cache has been calculated.
|
||||
*/
|
||||
CompletableFuture<Void> calculateBalanceTopMapAsync();
|
||||
|
||||
/**
|
||||
* Gets the balance top cache or an empty list if one has not been calculated yet. The balance top cache is a {@link Map}
|
||||
* which maps the UUID of the player to a {@link BalanceTop.Entry} object which stores the user's display name and balance.
|
||||
* The returned map is sorted by greatest to least wealth.
|
||||
* <p>
|
||||
* There is no guarantee the returned cache is up to date. The balancetop command is directly responsible for updating
|
||||
* this cache and does so every two minutes (if executed). See {@link BalanceTop#calculateBalanceTopMapAsync()} to
|
||||
* manually update this cache yourself.
|
||||
*
|
||||
* @return The balance top cache.
|
||||
* @see BalanceTop#calculateBalanceTopMapAsync()
|
||||
*/
|
||||
Map<UUID, Entry> getBalanceTopCache();
|
||||
|
||||
/**
|
||||
* Gets the epoch time (in mills.) that the baltop cache was last updated at. A value of zero indicates the cache
|
||||
* has not been calculated yet at all.
|
||||
*
|
||||
* @return The epoch time (in mills.) since last cache update or zero.
|
||||
*/
|
||||
long getCacheAge();
|
||||
|
||||
/**
|
||||
* Gets the total amount of money in the economy at the point of the last balance top cache calculation or returns zero
|
||||
* if no baltop calculation has been made as of yet.
|
||||
*
|
||||
* @return The total amount of money in the economy or zero.
|
||||
* @see BalanceTop#getCacheAge() to find last baltop cache calculation
|
||||
*/
|
||||
BigDecimal getBalanceTopTotal();
|
||||
|
||||
/**
|
||||
* Checks to see if {@link BalanceTop#calculateBalanceTopMapAsync()} is still in the process of calculating the map.
|
||||
*
|
||||
* @return true if the balance top cache is still in the process of being calculated, otherwise false.
|
||||
*/
|
||||
boolean isCacheLocked();
|
||||
|
||||
/**
|
||||
* This class represents a user's name/balance in the balancetop cache.
|
||||
*/
|
||||
class Entry {
|
||||
private final UUID uuid;
|
||||
private final String displayName;
|
||||
private final BigDecimal balance;
|
||||
|
||||
public Entry(UUID uuid, String displayName, BigDecimal balance) {
|
||||
this.uuid = uuid;
|
||||
this.displayName = displayName;
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the UUID of the user.
|
||||
* @return The uuid of this user.
|
||||
*/
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display name of the user at the time of cache population.
|
||||
* @return The display name of this user.
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the balance of the user at the time of cache population.
|
||||
* @return The balance of this user.
|
||||
*/
|
||||
public BigDecimal getBalance() {
|
||||
return balance;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user