Document and clean up StatsManager
This commit is contained in:
parent
d291521ad1
commit
87eaeabd7f
@ -1,6 +1,7 @@
|
|||||||
package mineplex.core.achievement.command;
|
package mineplex.core.achievement.command;
|
||||||
|
|
||||||
import mineplex.core.stats.PlayerStats;
|
import mineplex.core.stats.PlayerStats;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import mineplex.core.achievement.AchievementManager;
|
import mineplex.core.achievement.AchievementManager;
|
||||||
@ -59,35 +60,17 @@ public class StatsCommand extends CommandBase<AchievementManager>
|
|||||||
UtilPlayer.message(caller, F.main("Stats", "Attempting to look up offline stats..."));
|
UtilPlayer.message(caller, F.main("Stats", "Attempting to look up offline stats..."));
|
||||||
final String playerName = args[0];
|
final String playerName = args[0];
|
||||||
|
|
||||||
Plugin.runAsync(() -> {
|
Plugin.getStatsManager().getOfflinePlayerStats(args[0], stats ->
|
||||||
try
|
|
||||||
{
|
{
|
||||||
PlayerStats stats = Plugin.getStatsManager().getOfflinePlayerStats(playerName);
|
|
||||||
|
|
||||||
if (stats == null)
|
if (stats == null)
|
||||||
{
|
{
|
||||||
Plugin.runSync(() -> offlinePlayerNotFound(caller, playerName));
|
UtilPlayer.message(caller, F.main("Stats", "Offline Player " + F.elem(playerName) + " not found."));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Plugin.runSync(() -> openShop(caller, playerName, stats));
|
Plugin.openShop(caller, playerName, stats);
|
||||||
}
|
|
||||||
} catch (SQLException e)
|
|
||||||
{
|
|
||||||
Plugin.runSync(() -> UtilPlayer.message(caller, F.main("Stats", "There was an error trying to look up offline player " + F.elem(playerName))));
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void offlinePlayerNotFound(Player caller, String searchName)
|
|
||||||
{
|
|
||||||
UtilPlayer.message(caller, F.main("Stats", "Offline Player " + F.elem(searchName) + " not found."));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openShop(Player caller, String searchName, PlayerStats playerStats)
|
|
||||||
{
|
|
||||||
Plugin.openShop(caller, searchName, playerStats);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ public class DonationRepository extends MinecraftRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purchases a known sales package.. whatever that means
|
* Purchases a known sales package
|
||||||
*
|
*
|
||||||
* @param playerName The player name
|
* @param playerName The player name
|
||||||
* @param salesPackageId The package id
|
* @param salesPackageId The package id
|
||||||
@ -58,7 +58,7 @@ public class DonationRepository extends MinecraftRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Purchases an unknown sales package... or something
|
* Purchases an unknown sales package
|
||||||
*
|
*
|
||||||
* @param playerName The name of the player
|
* @param playerName The name of the player
|
||||||
* @param packageName The name of the unknown package
|
* @param packageName The name of the unknown package
|
||||||
|
@ -1,41 +1,72 @@
|
|||||||
package mineplex.core.stats;
|
package mineplex.core.stats;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import mineplex.core.common.util.NautHashMap;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a player's statistic information. This object is thread-safe
|
||||||
|
*/
|
||||||
public class PlayerStats
|
public class PlayerStats
|
||||||
{
|
{
|
||||||
private NautHashMap<String, Long> _statHash = new NautHashMap<String,Long>();
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public long addStat(String statName, long value)
|
private Map<String, Long> _stats = new HashMap<>();
|
||||||
{
|
|
||||||
value = Math.max(0L, value);
|
|
||||||
|
|
||||||
if (!_statHash.containsKey(statName))
|
/**
|
||||||
|
* Add a value to the specified stat
|
||||||
|
*
|
||||||
|
* @param statName The name of the stat
|
||||||
|
* @param value The value, must be positive
|
||||||
|
* @return The new value for the specified stat
|
||||||
|
*/
|
||||||
|
long addStat(String statName, long value)
|
||||||
{
|
{
|
||||||
_statHash.put(statName, 0L);
|
synchronized (lock)
|
||||||
|
{
|
||||||
|
return _stats.merge(statName, Math.max(0, value), Long::sum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_statHash.put(statName, _statHash.get(statName) + value);
|
/**
|
||||||
|
* Sets the value of the specified stat
|
||||||
return _statHash.get(statName);
|
*
|
||||||
}
|
* @param statName The name of the stat
|
||||||
|
* @param value The value, must be positive
|
||||||
public long setStat(String statName, long value)
|
* @return The new value for the specified stat
|
||||||
|
*/
|
||||||
|
long setStat(String statName, long value)
|
||||||
{
|
{
|
||||||
_statHash.put(statName, value);
|
synchronized (lock)
|
||||||
|
{
|
||||||
return _statHash.get(statName);
|
_stats.put(statName, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the value for the specified stat
|
||||||
|
*
|
||||||
|
* @param statName The name of the stat
|
||||||
|
* @return The value of the stat if it exists, or 0 if it does not
|
||||||
|
*/
|
||||||
public long getStat(String statName)
|
public long getStat(String statName)
|
||||||
{
|
{
|
||||||
return _statHash.containsKey(statName) ? _statHash.get(statName) : 0L;
|
synchronized (lock)
|
||||||
|
{
|
||||||
|
return _stats.getOrDefault(statName, 0L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getStatsNames()
|
/**
|
||||||
|
* Returns a view of the all the stats. This view will not be updated
|
||||||
|
*/
|
||||||
|
public Map<String, Long> getStats()
|
||||||
{
|
{
|
||||||
return _statHash.keySet();
|
synchronized (lock)
|
||||||
|
{
|
||||||
|
// make it unmodifiable so that people who try to edit it will get an exception instead of silently failing
|
||||||
|
return Collections.unmodifiableMap(new HashMap<>(_stats));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,22 @@ package mineplex.core.stats;
|
|||||||
|
|
||||||
public class Stat
|
public class Stat
|
||||||
{
|
{
|
||||||
public int Id;
|
private int id;
|
||||||
public String Name;
|
private String name;
|
||||||
|
|
||||||
|
public Stat(int id, String name)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,279 +2,229 @@ package mineplex.core.stats;
|
|||||||
|
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Iterator;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import mineplex.cache.player.PlayerInfo;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import mineplex.cache.player.PlayerCache;
|
|
||||||
import mineplex.core.MiniDbClientPlugin;
|
import mineplex.core.MiniDbClientPlugin;
|
||||||
|
import mineplex.core.account.CoreClient;
|
||||||
import mineplex.core.account.CoreClientManager;
|
import mineplex.core.account.CoreClientManager;
|
||||||
import mineplex.core.common.util.NautHashMap;
|
|
||||||
import mineplex.core.common.util.UtilServer;
|
import mineplex.core.common.util.UtilServer;
|
||||||
|
import mineplex.core.common.util.UtilTasks;
|
||||||
import mineplex.core.stats.command.GiveStatCommand;
|
import mineplex.core.stats.command.GiveStatCommand;
|
||||||
import mineplex.core.stats.command.MasterBuilderUnban;
|
import mineplex.core.stats.command.MasterBuilderUnban;
|
||||||
import mineplex.core.stats.command.TimeCommand;
|
import mineplex.core.stats.command.TimeCommand;
|
||||||
import mineplex.core.stats.event.StatChangeEvent;
|
import mineplex.core.stats.event.StatChangeEvent;
|
||||||
|
import mineplex.core.thread.ThreadPool;
|
||||||
|
import mineplex.core.updater.UpdateType;
|
||||||
|
import mineplex.core.utils.UtilScheduler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This manager handles player statistics
|
||||||
|
*/
|
||||||
public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
||||||
{
|
{
|
||||||
private static final Object _statSync = new Object();
|
private static final Object STATS_LOCK = new Object();
|
||||||
|
|
||||||
private StatsRepository _repository;
|
private final CoreClientManager _coreClientManager;
|
||||||
|
private final StatsRepository _repository;
|
||||||
|
|
||||||
private NautHashMap<String, Integer> _stats = new NautHashMap<String, Integer>();
|
private final Map<String, Integer> _stats = new HashMap<>();
|
||||||
private NautHashMap<UUID, NautHashMap<String, Long>> _statUploadQueue = new NautHashMap<>();
|
private final Map<CoreClient, Map<String, Long>> _statUploadQueue = new HashMap<>();
|
||||||
private NautHashMap<UUID, NautHashMap<String, Long>> _statUploadQueueOverRidable = new NautHashMap<>();
|
private final Map<CoreClient, Map<String, Long>> _statUploadQueueOverRidable = new HashMap<>();
|
||||||
|
|
||||||
private Runnable _saveRunnable;
|
|
||||||
|
|
||||||
public StatsManager(JavaPlugin plugin, CoreClientManager clientManager)
|
public StatsManager(JavaPlugin plugin, CoreClientManager clientManager)
|
||||||
{
|
{
|
||||||
super("Stats Manager", plugin, clientManager);
|
super("Stats Manager", plugin, clientManager);
|
||||||
|
|
||||||
_repository = new StatsRepository(plugin);
|
_repository = new StatsRepository();
|
||||||
|
_coreClientManager = clientManager;
|
||||||
|
|
||||||
if (_saveRunnable == null)
|
UtilScheduler.runAsyncEvery(UpdateType.SEC, () ->
|
||||||
{
|
{
|
||||||
_saveRunnable = new Runnable()
|
save(_statUploadQueue, _repository::saveStats, "normal");
|
||||||
{
|
save(_statUploadQueueOverRidable, map -> _repository.saveStats(map, true), "override");
|
||||||
public void run()
|
});
|
||||||
{
|
|
||||||
saveStats();
|
|
||||||
overRidableSaveStats();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, _saveRunnable, 20L, 20L);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Stat stat : _repository.retrieveStats())
|
for (Stat stat : _repository.retrieveStats())
|
||||||
{
|
{
|
||||||
_stats.put(stat.Name, stat.Id);
|
_stats.put(stat.getName(), stat.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
/**
|
||||||
public void onPlayerJoin(PlayerJoinEvent event)
|
* Gets offline stats for the specified player name
|
||||||
|
*
|
||||||
|
* @return A Future to listen to, if you are already off the main thread
|
||||||
|
*/
|
||||||
|
public Future<PlayerStats> getOfflinePlayerStats(String playerName)
|
||||||
{
|
{
|
||||||
PlayerStats playerStats = Get(event.getPlayer());
|
return getOfflinePlayerStats(playerName, null);
|
||||||
|
|
||||||
final int accountId = getClientManager().getAccountId(event.getPlayer());
|
|
||||||
|
|
||||||
for (String statName : playerStats.getStatsNames())
|
|
||||||
{
|
|
||||||
if (!_stats.containsKey(statName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
final int statId = _stats.get(statName);
|
|
||||||
|
|
||||||
if (playerStats.getStat(statName) == -1)
|
|
||||||
{
|
|
||||||
runAsync(new Runnable()
|
|
||||||
{
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
_repository.setStat(accountId, statId, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets offline stats for the specified player name
|
||||||
|
*
|
||||||
|
* @param action The action to perform with the fetched PlayerStats. This action will be performed on the main thread. Can be null
|
||||||
|
* @return A Future to listen to, should you already be off the main thread
|
||||||
|
*/
|
||||||
|
public Future<PlayerStats> getOfflinePlayerStats(String playerName, Consumer<PlayerStats> action)
|
||||||
|
{
|
||||||
|
return ThreadPool.ASYNC.submit(() ->
|
||||||
|
{
|
||||||
|
PlayerStats stats = _repository.loadOfflinePlayerStats(playerName);
|
||||||
|
UtilTasks.onMainThread(action).accept(stats);
|
||||||
|
return stats;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incrementStat(final Player player, final String statName, final long value)
|
|
||||||
{
|
|
||||||
incrementStat(player, statName, value, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void incrementStat(final Player player, final String statName, final long value, boolean overRide)
|
/**
|
||||||
|
* Increments a stat for the given player by the specified amount
|
||||||
|
*
|
||||||
|
* @param value The value, must be greater or equal to zero
|
||||||
|
*/
|
||||||
|
public void incrementStat(Player player, String statName, long value)
|
||||||
{
|
{
|
||||||
if (value <= 0)
|
if (value < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
long oldValue = Get(player).getStat(statName);
|
||||||
long newValue = Get(player).addStat(statName, value);
|
long newValue = Get(player).addStat(statName, value);
|
||||||
|
|
||||||
//Event
|
UtilServer.getServer().getPluginManager().callEvent(new StatChangeEvent(player.getName(), statName, oldValue, newValue));
|
||||||
UtilServer.getServer().getPluginManager().callEvent(new StatChangeEvent(player.getName(), statName, newValue - value, newValue));
|
registerNewStat(statName, () -> addToQueue(statName, player, value));
|
||||||
|
}
|
||||||
|
|
||||||
// Verify stat is in our local cache, if not add it remotely.
|
|
||||||
registerNewStat(statName, new Runnable()
|
/**
|
||||||
|
* Sets the value of a stat for the given player
|
||||||
|
*
|
||||||
|
* @param value The value, must be greater or equal to zero
|
||||||
|
*/
|
||||||
|
public void setStat(Player player, String statName, long value)
|
||||||
{
|
{
|
||||||
@Override
|
if (value < 0)
|
||||||
public void run()
|
return;
|
||||||
{
|
|
||||||
if(overRide)
|
long oldValue = Get(player).getStat(statName);
|
||||||
{
|
Get(player).setStat(statName, value);
|
||||||
addToOverRidableQueue(statName, player, value);
|
|
||||||
|
UtilServer.getServer().getPluginManager().callEvent(new StatChangeEvent(player.getName(), statName, oldValue, value));
|
||||||
|
registerNewStat(statName, () -> addToOverRidableQueue(statName, player, value));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
/**
|
||||||
|
* Increments a stat for the given account ID of an <b>offline player</b> by the specified amount
|
||||||
|
*/
|
||||||
|
public void incrementStat(final int accountId, final String statName, final long value)
|
||||||
{
|
{
|
||||||
addToQueue(statName, player, value);
|
registerNewStat(statName, () ->
|
||||||
}
|
{
|
||||||
|
Map<Integer, Map<Integer, Long>> uploadQueue = new HashMap<>();
|
||||||
|
uploadQueue.computeIfAbsent(accountId, key -> new HashMap<>()).put(_stats.get(statName), value);
|
||||||
|
|
||||||
|
_repository.saveStats(uploadQueue, false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value of a stat for the given account ID of an <b>offline player</b>
|
||||||
|
*
|
||||||
|
* @param value The value, must be greater or equal to zero
|
||||||
|
*/
|
||||||
|
public void setStat(final int accountId, final String statName, final long value)
|
||||||
|
{
|
||||||
|
if (value < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
registerNewStat(statName, () ->
|
||||||
|
{
|
||||||
|
Map<Integer, Map<Integer, Long>> uploadQueue = new HashMap<>();
|
||||||
|
uploadQueue.computeIfAbsent(accountId, key -> new HashMap<>()).put(_stats.get(statName), value);
|
||||||
|
|
||||||
|
_repository.saveStats(uploadQueue, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToOverRidableQueue(String statName, Player player, long value)
|
private void addToOverRidableQueue(String statName, Player player, long value)
|
||||||
{
|
{
|
||||||
synchronized (_statSync)
|
synchronized (STATS_LOCK)
|
||||||
{
|
{
|
||||||
if (!_statUploadQueueOverRidable.containsKey(player.getUniqueId()))
|
_statUploadQueueOverRidable
|
||||||
_statUploadQueueOverRidable.put(player.getUniqueId(), new NautHashMap<String, Long>());
|
.computeIfAbsent(_coreClientManager.Get(player), key -> new HashMap<>())
|
||||||
|
.put(statName, value);
|
||||||
if (!_statUploadQueueOverRidable.get(player.getUniqueId()).containsKey(statName))
|
|
||||||
_statUploadQueueOverRidable.get(player.getUniqueId()).put(statName, 0L);
|
|
||||||
|
|
||||||
_statUploadQueueOverRidable.get(player.getUniqueId()).put(statName, _statUploadQueueOverRidable.get(player.getUniqueId()).get(statName) + value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void overRidableSaveStats()
|
|
||||||
{
|
|
||||||
if (_statUploadQueueOverRidable.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
NautHashMap<Integer, NautHashMap<Integer, Long>> uploadQueue = new NautHashMap<Integer, NautHashMap<Integer, Long>>();
|
|
||||||
|
|
||||||
synchronized (_statSync)
|
|
||||||
{
|
|
||||||
for (Iterator<UUID> statIterator = _statUploadQueueOverRidable.keySet().iterator(); statIterator.hasNext();)
|
|
||||||
{
|
|
||||||
UUID player = statIterator.next();
|
|
||||||
|
|
||||||
if (Bukkit.getPlayer(player) != null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
PlayerInfo info = PlayerCache.getInstance().getPlayer(player);
|
|
||||||
|
|
||||||
uploadQueue.put(info.getAccountId(), new NautHashMap<>());
|
|
||||||
|
|
||||||
for (String statName : _statUploadQueueOverRidable.get(player).keySet())
|
|
||||||
{
|
|
||||||
int statId = _stats.get(statName);
|
|
||||||
uploadQueue.get(info.getAccountId()).put(statId, _statUploadQueueOverRidable.get(player).get(statName));
|
|
||||||
System.out.println(info.getName() + " saving stat : " + statName + " overriding " + _statUploadQueueOverRidable.get(player).get(statName));
|
|
||||||
}
|
|
||||||
|
|
||||||
statIterator.remove();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
//System.out.println("[StatsManager] AccountId was not set for " + player.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_repository.saveStats(uploadQueue, true);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
exception.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToQueue(String statName, Player player, long value)
|
private void addToQueue(String statName, Player player, long value)
|
||||||
{
|
{
|
||||||
synchronized (_statSync)
|
synchronized (STATS_LOCK)
|
||||||
{
|
{
|
||||||
if (!_statUploadQueue.containsKey(player.getUniqueId()))
|
_statUploadQueue
|
||||||
_statUploadQueue.put(player.getUniqueId(), new NautHashMap<String, Long>());
|
.computeIfAbsent(_coreClientManager.Get(player), key -> new HashMap<>())
|
||||||
|
.merge(statName, value, Long::sum);
|
||||||
if (!_statUploadQueue.get(player.getUniqueId()).containsKey(statName))
|
|
||||||
_statUploadQueue.get(player.getUniqueId()).put(statName, 0L);
|
|
||||||
|
|
||||||
_statUploadQueue.get(player.getUniqueId()).put(statName, _statUploadQueue.get(player.getUniqueId()).get(statName) + value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void saveStats()
|
protected void save(Map<CoreClient, Map<String, Long>> statsMap, Consumer<Map<Integer, Map<Integer, Long>>> action, String type)
|
||||||
{
|
{
|
||||||
if (_statUploadQueue.isEmpty())
|
if (statsMap.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
Map<Integer, Map<Integer, Long>> uploadQueue = new HashMap<>();
|
||||||
{
|
|
||||||
NautHashMap<Integer, NautHashMap<Integer, Long>> uploadQueue = new NautHashMap<Integer, NautHashMap<Integer, Long>>();
|
|
||||||
|
|
||||||
synchronized (_statSync)
|
|
||||||
{
|
|
||||||
for (Iterator<UUID> statIterator = _statUploadQueue.keySet().iterator(); statIterator.hasNext();)
|
|
||||||
{
|
|
||||||
UUID player = statIterator.next();
|
|
||||||
|
|
||||||
if (Bukkit.getPlayer(player) != null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PlayerInfo info = PlayerCache.getInstance().getPlayer(player);
|
synchronized (STATS_LOCK)
|
||||||
|
|
||||||
uploadQueue.put(info.getAccountId(), new NautHashMap<Integer, Long>());
|
|
||||||
|
|
||||||
for (String statName : _statUploadQueue.get(player).keySet())
|
|
||||||
{
|
{
|
||||||
int statId = _stats.get(statName);
|
statsMap.entrySet().removeIf(entry ->
|
||||||
uploadQueue.get(info.getAccountId()).put(statId, _statUploadQueue.get(player).get(statName));
|
|
||||||
System.out.println(info.getName() + " saving stat : " + statName + " adding " + _statUploadQueue.get(player).get(statName));
|
|
||||||
}
|
|
||||||
|
|
||||||
statIterator.remove();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
{
|
||||||
//System.out.println("[StatsManager] AccountId was not set for " + player.getName());
|
CoreClient client = entry.getKey();
|
||||||
}
|
if (Bukkit.getPlayer(client.getUniqueId()) != null)
|
||||||
}
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
_repository.saveStats(uploadQueue);
|
Map<Integer, Long> uploadStats = uploadQueue.computeIfAbsent(client.getAccountId(), key -> new HashMap<>());
|
||||||
|
|
||||||
|
entry.getValue().forEach((statName, amount) ->
|
||||||
|
{
|
||||||
|
// Sanity check
|
||||||
|
if (_stats.containsKey(statName))
|
||||||
|
{
|
||||||
|
uploadStats.merge(_stats.get(statName), amount, Long::sum);
|
||||||
|
System.out.println(String.format("Saving stat '%s' for '%s', value '%s', type '%s'", statName, client.getName(), uploadStats.get(_stats.get(statName)), type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
exception.printStackTrace();
|
exception.printStackTrace();
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
action.accept(uploadQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean incrementStat(final int accountId, final String statName, final long value)
|
|
||||||
{
|
|
||||||
return incrementStat(accountId, statName, value, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean incrementStat(final int accountId, final String statName, final long value, boolean overRide)
|
|
||||||
{
|
|
||||||
// This will register a new stat if we don't have one, otherwise it will just run the callback
|
|
||||||
registerNewStat(statName, new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
final NautHashMap<Integer, NautHashMap<Integer, Long>> uploadQueue = new NautHashMap<Integer, NautHashMap<Integer, Long>>();
|
|
||||||
uploadQueue.put(accountId, new NautHashMap<Integer, Long>());
|
|
||||||
uploadQueue.get(accountId).put(_stats.get(statName), value);
|
|
||||||
|
|
||||||
_repository.saveStats(uploadQueue, overRide);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerNewStat(final String statName, final Runnable callback)
|
private void registerNewStat(final String statName, final Runnable callback)
|
||||||
{
|
{
|
||||||
runAsync(new Runnable()
|
runAsync(() ->
|
||||||
{
|
{
|
||||||
public void run()
|
synchronized (STATS_LOCK)
|
||||||
{
|
|
||||||
synchronized (_statSync)
|
|
||||||
{
|
{
|
||||||
if (_stats.containsKey(statName))
|
if (_stats.containsKey(statName))
|
||||||
{
|
{
|
||||||
@ -282,37 +232,40 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_repository.addStat(statName);
|
_repository.registerNewStat(statName);
|
||||||
|
|
||||||
_stats.clear();
|
_stats.clear();
|
||||||
|
|
||||||
for (Stat stat : _repository.retrieveStats())
|
for (Stat stat : _repository.retrieveStats())
|
||||||
{
|
{
|
||||||
_stats.put(stat.Name, stat.Id);
|
_stats.put(stat.getName(), stat.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callback != null) callback.run();
|
if (callback != null) callback.run();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
private void onPlayerJoin(PlayerJoinEvent event)
|
||||||
public int getStatId(String statName)
|
|
||||||
{
|
{
|
||||||
return _stats.get(statName);
|
Set<String> statsToReset = new HashSet<>();
|
||||||
|
|
||||||
|
Get(event.getPlayer()).getStats().forEach((stat, amount) ->
|
||||||
|
{
|
||||||
|
if (!_stats.containsKey(stat))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (amount == -1)
|
||||||
|
{
|
||||||
|
statsToReset.add(stat);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
statsToReset.forEach(stat ->
|
||||||
protected PlayerStats addPlayer(UUID uuid)
|
|
||||||
{
|
{
|
||||||
return new PlayerStats();
|
setStat(event.getPlayer(), stat, 0);
|
||||||
}
|
});
|
||||||
|
|
||||||
public PlayerStats getOfflinePlayerStats(String playerName) throws SQLException
|
|
||||||
{
|
|
||||||
return _repository.loadOfflinePlayerStats(playerName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -323,10 +276,31 @@ public class StatsManager extends MiniDbClientPlugin<PlayerStats>
|
|||||||
addCommand(new MasterBuilderUnban(this));
|
addCommand(new MasterBuilderUnban(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PlayerStats addPlayer(UUID uuid)
|
||||||
|
{
|
||||||
|
return new PlayerStats();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processLoginResultSet(String playerName, UUID uuid, int accountId, ResultSet resultSet) throws SQLException
|
public void processLoginResultSet(String playerName, UUID uuid, int accountId, ResultSet resultSet) throws SQLException
|
||||||
{
|
{
|
||||||
Set(uuid, _repository.loadClientInformation(resultSet));
|
PlayerStats playerStats = new PlayerStats();
|
||||||
|
|
||||||
|
while (resultSet.next())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
playerStats.addStat(resultSet.getString(1), resultSet.getLong(2));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
playerStats.addStat(resultSet.getString(1), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set(uuid, playerStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,19 +1,10 @@
|
|||||||
package mineplex.core.stats;
|
package mineplex.core.stats;
|
||||||
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import mineplex.core.common.util.NautHashMap;
|
|
||||||
import mineplex.core.database.MinecraftRepository;
|
|
||||||
import mineplex.serverdata.database.DBPool;
|
|
||||||
import mineplex.serverdata.database.ResultSetCallable;
|
|
||||||
import mineplex.serverdata.database.column.ColumnVarChar;
|
|
||||||
import mineplex.database.Tables;
|
|
||||||
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
import org.jooq.DSLContext;
|
import org.jooq.DSLContext;
|
||||||
import org.jooq.Insert;
|
import org.jooq.Insert;
|
||||||
import org.jooq.Record2;
|
import org.jooq.Record2;
|
||||||
@ -23,53 +14,68 @@ import org.jooq.Update;
|
|||||||
import org.jooq.impl.DSL;
|
import org.jooq.impl.DSL;
|
||||||
import org.jooq.types.ULong;
|
import org.jooq.types.ULong;
|
||||||
|
|
||||||
|
import mineplex.core.database.MinecraftRepository;
|
||||||
|
import mineplex.database.Tables;
|
||||||
|
import mineplex.serverdata.database.DBPool;
|
||||||
|
import mineplex.serverdata.database.column.ColumnVarChar;
|
||||||
|
|
||||||
public class StatsRepository extends MinecraftRepository
|
public class StatsRepository extends MinecraftRepository
|
||||||
{
|
{
|
||||||
private static String RETRIEVE_STATS = "SELECT id, name FROM stats;";
|
private static final String RETRIEVE_STATS = "SELECT id, name FROM stats;";
|
||||||
private static String INSERT_STAT = "INSERT INTO stats (name) VALUES (?);";
|
private static final String INSERT_STAT = "INSERT INTO stats (name) VALUES (?);";
|
||||||
|
|
||||||
|
public StatsRepository()
|
||||||
public StatsRepository(JavaPlugin plugin)
|
|
||||||
{
|
{
|
||||||
super(DBPool.getAccount());
|
super(DBPool.getAccount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves all the remote registered stats
|
||||||
|
*
|
||||||
|
* @return The list of stats
|
||||||
|
*/
|
||||||
public List<Stat> retrieveStats()
|
public List<Stat> retrieveStats()
|
||||||
{
|
{
|
||||||
final List<Stat> stats = new ArrayList<Stat>();
|
List<Stat> stats = new ArrayList<>();
|
||||||
|
|
||||||
executeQuery(RETRIEVE_STATS, new ResultSetCallable()
|
executeQuery(RETRIEVE_STATS, resultSet ->
|
||||||
{
|
|
||||||
public void processResultSet(ResultSet resultSet) throws SQLException
|
|
||||||
{
|
{
|
||||||
while (resultSet.next())
|
while (resultSet.next())
|
||||||
{
|
{
|
||||||
Stat stat = new Stat();
|
stats.add(new Stat(resultSet.getInt(1), resultSet.getString(2)));
|
||||||
|
|
||||||
stat.Id = resultSet.getInt(1);
|
|
||||||
stat.Name = resultSet.getString(2);
|
|
||||||
|
|
||||||
stats.add(stat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStat(String name)
|
/**
|
||||||
|
* Registers a stat with the remote server
|
||||||
|
*
|
||||||
|
* @param name The name of the stat
|
||||||
|
*/
|
||||||
|
public void registerNewStat(String name)
|
||||||
{
|
{
|
||||||
executeUpdate(INSERT_STAT, new ColumnVarChar("name", 100, name));
|
executeUpdate(INSERT_STAT, new ColumnVarChar("name", 100, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveStats(NautHashMap<Integer, NautHashMap<Integer, Long>> uploadQueue)
|
/**
|
||||||
|
* Saves the given stats
|
||||||
|
*
|
||||||
|
* @param uploadQueue A map of account ID to a map of stat IDS to values
|
||||||
|
*/
|
||||||
|
public void saveStats(Map<Integer, Map<Integer, Long>> uploadQueue)
|
||||||
{
|
{
|
||||||
saveStats(uploadQueue, false);
|
saveStats(uploadQueue, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
@SuppressWarnings("rawtypes")
|
* Saves the given stats
|
||||||
public void saveStats(NautHashMap<Integer, NautHashMap<Integer, Long>> uploadQueue, boolean overRideStat)
|
*
|
||||||
|
* @param uploadQueue A map of account ID to a map of stat IDS to values
|
||||||
|
* @param overrideStat Whether to replace the remote value, or to add to it
|
||||||
|
*/
|
||||||
|
public void saveStats(Map<Integer, Map<Integer, Long>> uploadQueue, boolean overrideStat)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -82,7 +88,7 @@ public class StatsRepository extends MinecraftRepository
|
|||||||
{
|
{
|
||||||
for (Integer statId : uploadQueue.get(accountId).keySet())
|
for (Integer statId : uploadQueue.get(accountId).keySet())
|
||||||
{
|
{
|
||||||
if(overRideStat)
|
if (overrideStat)
|
||||||
{
|
{
|
||||||
Update update = context
|
Update update = context
|
||||||
.update(Tables.accountStat)
|
.update(Tables.accountStat)
|
||||||
@ -129,6 +135,10 @@ public class StatsRepository extends MinecraftRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets offline stats for the specified player name. This performs SQL on the current thread
|
||||||
|
*/
|
||||||
public PlayerStats loadOfflinePlayerStats(String playerName)
|
public PlayerStats loadOfflinePlayerStats(String playerName)
|
||||||
{
|
{
|
||||||
PlayerStats playerStats = null;
|
PlayerStats playerStats = null;
|
||||||
@ -161,43 +171,4 @@ public class StatsRepository extends MinecraftRepository
|
|||||||
|
|
||||||
return playerStats;
|
return playerStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerStats loadClientInformation(ResultSet resultSet) throws SQLException
|
|
||||||
{
|
|
||||||
final PlayerStats playerStats = new PlayerStats();
|
|
||||||
|
|
||||||
while (resultSet.next())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
playerStats.addStat(resultSet.getString(1), resultSet.getLong(2));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ex.printStackTrace();
|
|
||||||
playerStats.addStat(resultSet.getString(1), -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return playerStats;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStat(int accountId, int statId, long value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DSLContext context = DSL.using(getConnectionPool(), SQLDialect.MYSQL);
|
|
||||||
|
|
||||||
context
|
|
||||||
.update(Tables.accountStat)
|
|
||||||
.set(Tables.accountStat.value, ULong.valueOf(value))
|
|
||||||
.where(Tables.accountStat.accountId.eq(accountId))
|
|
||||||
.and(Tables.accountStat.statId.eq(statId))
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ public class GiveStatCommand extends CommandBase<StatsManager>
|
|||||||
Plugin.incrementStat(player, statName, Integer.parseInt(args[args.length - 1]));
|
Plugin.incrementStat(player, statName, Integer.parseInt(args[args.length - 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
UtilPlayer.message(caller, F.main("Stats", "Applied " + F.elem(Integer.parseInt(args[args.length - 1]) + " " + statName) + " to " + F.elem(player.getName()) + "."));
|
UtilPlayer.message(caller, F.main("Stats", "Applied " + F.elem(Integer.parseInt(args[args.length - 1]) + " " + statName) + " to " + F.elem(player == null ? args[0] : player.getName()) + "."));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -72,7 +72,7 @@ public class MasterBuilderUnban extends CommandBase<StatsManager>
|
|||||||
{
|
{
|
||||||
if(theClient != null)
|
if(theClient != null)
|
||||||
{
|
{
|
||||||
Plugin.incrementStat(theClient.getAccountId(), "Global.Build Draw Abuse", 0, true); // True = Resets the stat
|
Plugin.setStat(theClient.getAccountId(), "Global.Build Draw Abuse", 0);
|
||||||
caller.sendMessage(F.main("MasterBuilder Unban", "The user " + target + " has been unbanned from Master Builders"));
|
caller.sendMessage(F.main("MasterBuilder Unban", "The user " + target + " has been unbanned from Master Builders"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -35,19 +35,7 @@ public class TimeCommand extends CommandBase<StatsManager>
|
|||||||
|
|
||||||
if (target == null)
|
if (target == null)
|
||||||
{
|
{
|
||||||
Plugin.getPlugin().getServer().getScheduler().runTaskAsynchronously(Plugin.getPlugin(), new Runnable()
|
Plugin.getOfflinePlayerStats(args[0], stats ->
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
final PlayerStats stats = Plugin.getOfflinePlayerStats(args[0]);
|
|
||||||
|
|
||||||
Plugin.getPlugin().getServer().getScheduler().runTask(Plugin.getPlugin(), new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (stats == null)
|
if (stats == null)
|
||||||
{
|
{
|
||||||
@ -58,14 +46,6 @@ public class TimeCommand extends CommandBase<StatsManager>
|
|||||||
long time = stats.getStat("Global.TimeInGame");
|
long time = stats.getStat("Global.TimeInGame");
|
||||||
UtilPlayer.message(caller, F.main("Time", F.name(args[0]) + " has spent " + F.elem(UtilTime.convertString(time * 1000L, 1, UtilTime.TimeUnit.FIT)) + " in game"));
|
UtilPlayer.message(caller, F.main("Time", F.name(args[0]) + " has spent " + F.elem(UtilTime.convertString(time * 1000L, 1, UtilTime.TimeUnit.FIT)) + " in game"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -12,4 +12,10 @@ public class UtilScheduler
|
|||||||
Plugin plugin = UtilServer.getPlugin();
|
Plugin plugin = UtilServer.getPlugin();
|
||||||
plugin.getServer().getScheduler().runTaskTimer(plugin, action, 0, (int) Math.ceil(speed.getMilliseconds() / 50.0));
|
plugin.getServer().getScheduler().runTaskTimer(plugin, action, 0, (int) Math.ceil(speed.getMilliseconds() / 50.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void runAsyncEvery(UpdateType speed, Runnable action)
|
||||||
|
{
|
||||||
|
Plugin plugin = UtilServer.getPlugin();
|
||||||
|
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, action, 0, (int) Math.ceil(speed.getMilliseconds() / 50.0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,19 +38,7 @@ public class PlayTimeCommand extends CommandBase<StatsManager>
|
|||||||
|
|
||||||
if (target == null)
|
if (target == null)
|
||||||
{
|
{
|
||||||
Plugin.getPlugin().getServer().getScheduler().runTaskAsynchronously(Plugin.getPlugin(), new Runnable()
|
Plugin.getOfflinePlayerStats(args[0], stats ->
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
final PlayerStats stats = Plugin.getOfflinePlayerStats(args[0]);
|
|
||||||
|
|
||||||
Plugin.getPlugin().getServer().getScheduler().runTask(Plugin.getPlugin(), new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (stats == null)
|
if (stats == null)
|
||||||
{
|
{
|
||||||
@ -61,14 +49,6 @@ public class PlayTimeCommand extends CommandBase<StatsManager>
|
|||||||
long time = stats.getStat(ClansPlayerStats.PLAY_TIME.id());
|
long time = stats.getStat(ClansPlayerStats.PLAY_TIME.id());
|
||||||
UtilPlayer.message(caller, F.main("Clans", F.name(args[0]) + " has spent " + F.elem(UtilTime.convertString(time * 1000L, 1, UtilTime.TimeUnit.FIT)) + " playing Clans."));
|
UtilPlayer.message(caller, F.main("Clans", F.name(args[0]) + " has spent " + F.elem(UtilTime.convertString(time * 1000L, 1, UtilTime.TimeUnit.FIT)) + " playing Clans."));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (SQLException e)
|
|
||||||
{
|
|
||||||
UtilPlayer.message(caller, F.main("Clans", F.name(target.getName()) + " does not have any play time in Clans."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -10,6 +10,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.GameMode;
|
import org.bukkit.GameMode;
|
||||||
@ -342,12 +343,13 @@ public class CustomerSupport extends MiniPlugin implements ResultSetCallable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PlayerStats playerStats = statsManager.getOfflinePlayerStats(playerName);
|
PlayerStats playerStats = statsManager.getOfflinePlayerStats(playerName).get();
|
||||||
if (playerStats != null)
|
if (playerStats != null)
|
||||||
{
|
{
|
||||||
hauntedChestsOpened = (int) playerStats.getStat("Global.Treasure.Haunted");
|
hauntedChestsOpened = (int) playerStats.getStat("Global.Treasure.Haunted");
|
||||||
}
|
}
|
||||||
} catch (SQLException e)
|
}
|
||||||
|
catch (InterruptedException | ExecutionException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user