mirror of
https://github.com/EssentialsX/Essentials.git
synced 2025-01-02 22:47:41 +01:00
Rewrite User storage and UUID cache (#4581)
Co-authored-by: triagonal <10545540+triagonal@users.noreply.github.com> Co-authored-by: MD <1917406+mdcfe@users.noreply.github.com>
This commit is contained in:
parent
d75d3cd001
commit
0ca58ce4ba
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,6 +15,8 @@
|
|||||||
/Essentials/kits.yml
|
/Essentials/kits.yml
|
||||||
/Essentials/userdata/testplayer1.yml
|
/Essentials/userdata/testplayer1.yml
|
||||||
/Essentials/usermap.csv
|
/Essentials/usermap.csv
|
||||||
|
/Essentials/usermap.bin
|
||||||
|
/Essentials/uuids.bin
|
||||||
|
|
||||||
# Build files
|
# Build files
|
||||||
.gradle/
|
.gradle/
|
||||||
|
@ -28,8 +28,8 @@ public class BalanceTopImpl implements BalanceTop {
|
|||||||
private void calculateBalanceTopMap() {
|
private void calculateBalanceTopMap() {
|
||||||
final List<Entry> entries = new LinkedList<>();
|
final List<Entry> entries = new LinkedList<>();
|
||||||
BigDecimal newTotal = BigDecimal.ZERO;
|
BigDecimal newTotal = BigDecimal.ZERO;
|
||||||
for (UUID u : ess.getUserMap().getAllUniqueUsers()) {
|
for (UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User user = ess.getUserMap().getUser(u);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
|
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
|
||||||
// Don't list NPCs in output
|
// Don't list NPCs in output
|
||||||
|
@ -39,6 +39,7 @@ import com.earth2me.essentials.textreader.IText;
|
|||||||
import com.earth2me.essentials.textreader.KeywordReplacer;
|
import com.earth2me.essentials.textreader.KeywordReplacer;
|
||||||
import com.earth2me.essentials.textreader.SimpleTextInput;
|
import com.earth2me.essentials.textreader.SimpleTextInput;
|
||||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||||
|
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||||
import com.earth2me.essentials.utils.FormatUtil;
|
import com.earth2me.essentials.utils.FormatUtil;
|
||||||
import com.earth2me.essentials.utils.VersionUtil;
|
import com.earth2me.essentials.utils.VersionUtil;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
@ -155,7 +156,9 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
private transient CustomItemResolver customItemResolver;
|
private transient CustomItemResolver customItemResolver;
|
||||||
private transient PermissionsHandler permissionsHandler;
|
private transient PermissionsHandler permissionsHandler;
|
||||||
private transient AlternativeCommandsHandler alternativeCommandsHandler;
|
private transient AlternativeCommandsHandler alternativeCommandsHandler;
|
||||||
private transient UserMap userMap;
|
@Deprecated
|
||||||
|
private transient UserMap legacyUserMap;
|
||||||
|
private transient ModernUserMap userMap;
|
||||||
private transient BalanceTopImpl balanceTop;
|
private transient BalanceTopImpl balanceTop;
|
||||||
private transient ExecuteTimer execTimer;
|
private transient ExecuteTimer execTimer;
|
||||||
private transient MailService mail;
|
private transient MailService mail;
|
||||||
@ -222,7 +225,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
LOGGER.log(Level.INFO, dataFolder.toString());
|
LOGGER.log(Level.INFO, dataFolder.toString());
|
||||||
settings = new Settings(this);
|
settings = new Settings(this);
|
||||||
mail = new MailServiceImpl(this);
|
mail = new MailServiceImpl(this);
|
||||||
userMap = new UserMap(this);
|
userMap = new ModernUserMap(this);
|
||||||
balanceTop = new BalanceTopImpl(this);
|
balanceTop = new BalanceTopImpl(this);
|
||||||
permissionsHandler = new PermissionsHandler(this, false);
|
permissionsHandler = new PermissionsHandler(this, false);
|
||||||
Economy.setEss(this);
|
Economy.setEss(this);
|
||||||
@ -301,11 +304,14 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
confList.add(settings);
|
confList.add(settings);
|
||||||
execTimer.mark("Settings");
|
execTimer.mark("Settings");
|
||||||
|
|
||||||
|
upgrade.preModules();
|
||||||
|
execTimer.mark("Upgrade2");
|
||||||
|
|
||||||
mail = new MailServiceImpl(this);
|
mail = new MailServiceImpl(this);
|
||||||
execTimer.mark("Init(Mail)");
|
execTimer.mark("Init(Mail)");
|
||||||
|
|
||||||
userMap = new UserMap(this);
|
userMap = new ModernUserMap(this);
|
||||||
confList.add(userMap);
|
legacyUserMap = new UserMap(userMap);
|
||||||
execTimer.mark("Init(Usermap)");
|
execTimer.mark("Init(Usermap)");
|
||||||
|
|
||||||
balanceTop = new BalanceTopImpl(this);
|
balanceTop = new BalanceTopImpl(this);
|
||||||
@ -317,7 +323,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
execTimer.mark("Kits");
|
execTimer.mark("Kits");
|
||||||
|
|
||||||
upgrade.afterSettings();
|
upgrade.afterSettings();
|
||||||
execTimer.mark("Upgrade2");
|
execTimer.mark("Upgrade3");
|
||||||
|
|
||||||
warps = new Warps(this.getDataFolder());
|
warps = new Warps(this.getDataFolder());
|
||||||
confList.add(warps);
|
confList.add(warps);
|
||||||
@ -588,7 +594,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
|
|
||||||
Economy.setEss(null);
|
Economy.setEss(null);
|
||||||
Trade.closeLog();
|
Trade.closeLog();
|
||||||
getUserMap().getUUIDMap().shutdown();
|
getUsers().shutdown();
|
||||||
|
|
||||||
HandlerList.unregisterAll(this);
|
HandlerList.unregisterAll(this);
|
||||||
}
|
}
|
||||||
@ -1067,7 +1073,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
if (getSettings().isDebug()) {
|
if (getSettings().isDebug()) {
|
||||||
LOGGER.log(Level.INFO, "Constructing new userfile from base player " + base.getName());
|
LOGGER.log(Level.INFO, "Constructing new userfile from base player " + base.getName());
|
||||||
}
|
}
|
||||||
user = new User(base, this);
|
user = userMap.loadUncachedUser(base);
|
||||||
} else {
|
} else {
|
||||||
user.update(base);
|
user.update(base);
|
||||||
}
|
}
|
||||||
@ -1203,7 +1209,13 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
public UserMap getUserMap() {
|
public UserMap getUserMap() {
|
||||||
|
return legacyUserMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModernUserMap getUsers() {
|
||||||
return userMap;
|
return userMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +308,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
|
|||||||
|
|
||||||
ess.getBackup().onPlayerJoin();
|
ess.getBackup().onPlayerJoin();
|
||||||
final User dUser = ess.getUser(player);
|
final User dUser = ess.getUser(player);
|
||||||
|
dUser.update(player);
|
||||||
|
|
||||||
dUser.startTransaction();
|
dUser.startTransaction();
|
||||||
if (dUser.isNPC()) {
|
if (dUser.isNPC()) {
|
||||||
@ -367,7 +368,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
|
|||||||
} else if (ess.getSettings().isCustomJoinMessage()) {
|
} else if (ess.getSettings().isCustomJoinMessage()) {
|
||||||
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
|
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
|
||||||
.replace("{PLAYER}", player.getDisplayName()).replace("{USERNAME}", player.getName())
|
.replace("{PLAYER}", player.getDisplayName()).replace("{USERNAME}", player.getName())
|
||||||
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUserMap().getUniqueUsers()))
|
.replace("{UNIQUE}", NumberFormat.getInstance().format(ess.getUsers().getUserCount()))
|
||||||
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
|
.replace("{ONLINE}", NumberFormat.getInstance().format(ess.getOnlinePlayers().size()))
|
||||||
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()))
|
.replace("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()))
|
||||||
.replace("{PREFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(player)))
|
.replace("{PREFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(player)))
|
||||||
@ -527,6 +528,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
|
|||||||
public void onPlayerLogin(final PlayerLoginEvent event) {
|
public void onPlayerLogin(final PlayerLoginEvent event) {
|
||||||
if (event.getResult() == Result.KICK_FULL) {
|
if (event.getResult() == Result.KICK_FULL) {
|
||||||
final User kfuser = ess.getUser(event.getPlayer());
|
final User kfuser = ess.getUser(event.getPlayer());
|
||||||
|
kfuser.update(event.getPlayer());
|
||||||
if (kfuser.isAuthorized("essentials.joinfullserver")) {
|
if (kfuser.isAuthorized("essentials.joinfullserver")) {
|
||||||
event.allow();
|
event.allow();
|
||||||
return;
|
return;
|
||||||
|
@ -4,9 +4,9 @@ import com.earth2me.essentials.config.ConfigurateUtil;
|
|||||||
import com.earth2me.essentials.config.EssentialsConfiguration;
|
import com.earth2me.essentials.config.EssentialsConfiguration;
|
||||||
import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
||||||
import com.earth2me.essentials.craftbukkit.BanLookup;
|
import com.earth2me.essentials.craftbukkit.BanLookup;
|
||||||
|
import com.earth2me.essentials.userstorage.ModernUUIDCache;
|
||||||
import com.earth2me.essentials.utils.StringUtil;
|
import com.earth2me.essentials.utils.StringUtil;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import net.ess3.api.IEssentials;
|
import net.ess3.api.IEssentials;
|
||||||
@ -31,10 +31,10 @@ import java.math.BigDecimal;
|
|||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -47,13 +47,12 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
import static com.earth2me.essentials.I18n.tl;
|
||||||
|
|
||||||
public class EssentialsUpgrade {
|
public class EssentialsUpgrade {
|
||||||
private static final FileFilter YML_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".yml");
|
public static final FileFilter YML_FILTER = pathname -> pathname.isFile() && pathname.getName().endsWith(".yml");
|
||||||
private static final String PATTERN_CONFIG_UUID_REGEX = "(?mi)^uuid:\\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\s*$";
|
private static final String PATTERN_CONFIG_UUID_REGEX = "(?mi)^uuid:\\s*([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\s*$";
|
||||||
private static final Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
|
private static final Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
|
||||||
private static final String PATTERN_CONFIG_NAME_REGEX = "(?mi)^lastAccountName:\\s*[\"\']?(\\w+)[\"\']?\\s*$";
|
private static final String PATTERN_CONFIG_NAME_REGEX = "(?mi)^lastAccountName:\\s*[\"\']?(\\w+)[\"\']?\\s*$";
|
||||||
@ -93,7 +92,6 @@ public class EssentialsUpgrade {
|
|||||||
final int showProgress = countFiles % 250;
|
final int showProgress = countFiles % 250;
|
||||||
|
|
||||||
if (showProgress == 0) {
|
if (showProgress == 0) {
|
||||||
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
|
|
||||||
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
|
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +101,8 @@ public class EssentialsUpgrade {
|
|||||||
final EssentialsUserConfiguration config;
|
final EssentialsUserConfiguration config;
|
||||||
UUID uuid = null;
|
UUID uuid = null;
|
||||||
try {
|
try {
|
||||||
uuid = UUID.fromString(name);
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
UUID.fromString(name);
|
||||||
} catch (final IllegalArgumentException ex) {
|
} catch (final IllegalArgumentException ex) {
|
||||||
final File file = new File(userdir, string);
|
final File file = new File(userdir, string);
|
||||||
final EssentialsConfiguration conf = new EssentialsConfiguration(file);
|
final EssentialsConfiguration conf = new EssentialsConfiguration(file);
|
||||||
@ -115,6 +114,7 @@ public class EssentialsUpgrade {
|
|||||||
|
|
||||||
final String uuidString = conf.getString(uuidConf, null);
|
final String uuidString = conf.getString(uuidConf, null);
|
||||||
|
|
||||||
|
//noinspection ConstantConditions
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
try {
|
try {
|
||||||
uuid = UUID.fromString(uuidString);
|
uuid = UUID.fromString(uuidString);
|
||||||
@ -122,7 +122,7 @@ public class EssentialsUpgrade {
|
|||||||
break;
|
break;
|
||||||
} catch (final Exception ex2) {
|
} catch (final Exception ex2) {
|
||||||
if (conf.getBoolean("npc", false)) {
|
if (conf.getBoolean("npc", false)) {
|
||||||
uuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
|
uuid = UUID.nameUUIDFromBytes(("NPC:" + (ess.getSettings().isSafeUsermap() ? StringUtil.safeString(name) : name)).getBytes(Charsets.UTF_8));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,23 +130,24 @@ public class EssentialsUpgrade {
|
|||||||
uuid = player.getUniqueId();
|
uuid = player.getUniqueId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection ConstantConditions
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
countBukkit++;
|
countBukkit++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//noinspection ConstantConditions
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
conf.blockingSave();
|
conf.blockingSave();
|
||||||
config = new EssentialsUserConfiguration(name, uuid, new File(userdir, uuid + ".yml"));
|
config = new EssentialsUserConfiguration(name, uuid, new File(userdir, uuid + ".yml"));
|
||||||
config.convertLegacyFile();
|
config.convertLegacyFile();
|
||||||
ess.getUserMap().trackUUID(uuid, name, false);
|
ess.getUsers().loadUncachedUser(uuid);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
countFails++;
|
countFails++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
|
|
||||||
|
|
||||||
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
|
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
|
||||||
ess.getLogger().info("Converted via cache: " + countEssCache + " :: Converted via lookup: " + countBukkit + " :: Failed to convert: " + countFails);
|
ess.getLogger().info("Converted via cache: " + countEssCache + " :: Converted via lookup: " + countBukkit + " :: Failed to convert: " + countFails);
|
||||||
@ -913,74 +914,76 @@ public class EssentialsUpgrade {
|
|||||||
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, banTimeout == 0 ? null : new Date(banTimeout), Console.NAME);
|
Bukkit.getBanList(BanList.Type.NAME).addBan(playerName, banReason, banTimeout == 0 ? null : new Date(banTimeout), Console.NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void repairUserMap() {
|
public void generateUidCache() {
|
||||||
if (doneFile.getBoolean("userMapRepaired", false)) {
|
if (doneFile.getBoolean("newUidCacheBuilt", false)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ess.getLogger().info("Starting usermap repair");
|
|
||||||
|
|
||||||
|
final File usermapFile = new File(ess.getDataFolder(), "usermap.bin");
|
||||||
|
final File uidsFile = new File(ess.getDataFolder(), "uuids.bin");
|
||||||
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||||
if (!userdataFolder.isDirectory()) {
|
|
||||||
|
if (!userdataFolder.isDirectory() || usermapFile.exists() || uidsFile.exists()) {
|
||||||
ess.getLogger().warning("Missing userdata folder, aborting");
|
ess.getLogger().warning("Missing userdata folder, aborting");
|
||||||
|
doneFile.setProperty("newUidCacheBuilt", true);
|
||||||
|
doneFile.save();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!usermapFile.createNewFile() || !uidsFile.createNewFile()) {
|
||||||
|
ess.getLogger().warning("Couldn't create usermap.bin or uuids.bin, aborting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<UUID, Long> uuids = new HashMap<>();
|
||||||
|
final Map<String, UUID> nameToUuidMap = new HashMap<>();
|
||||||
|
|
||||||
final File[] files = userdataFolder.listFiles(YML_FILTER);
|
final File[] files = userdataFolder.listFiles(YML_FILTER);
|
||||||
|
if (files != null) {
|
||||||
final DecimalFormat format = new DecimalFormat("#0.00");
|
for (final File file : files) {
|
||||||
final Map<String, UUID> names = Maps.newHashMap();
|
|
||||||
|
|
||||||
for (int index = 0; index < files.length; index++) {
|
|
||||||
final File file = files[index];
|
|
||||||
try {
|
try {
|
||||||
UUID uuid = null;
|
final String fileName = file.getName();
|
||||||
final String filename = file.getName();
|
final UUID uuid = UUID.fromString(fileName.substring(0, fileName.length() - 4));
|
||||||
final String configData = new String(java.nio.file.Files.readAllBytes(file.toPath()), Charsets.UTF_8);
|
final EssentialsConfiguration config = new EssentialsConfiguration(file);
|
||||||
|
config.load();
|
||||||
|
String name = config.getString("last-account-name", null);
|
||||||
|
name = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(name) : name;
|
||||||
|
final long time = config.getLong("timestamps.logout", 0L);
|
||||||
|
|
||||||
if (filename.length() > 36) {
|
if (name != null) {
|
||||||
try {
|
if (nameToUuidMap.containsKey(name)) {
|
||||||
// ".yml" ending has 4 chars...
|
final UUID oldUuid = nameToUuidMap.get(name);
|
||||||
uuid = UUID.fromString(filename.substring(0, filename.length() - 4));
|
if (oldUuid.version() < uuid.version() || (oldUuid.version() == uuid.version() && uuids.get(oldUuid) < time)) {
|
||||||
} catch (final IllegalArgumentException ignored) {
|
ess.getLogger().warning("New UUID found for " + name + ": " + uuid + " (old: " + oldUuid + "). Replacing.");
|
||||||
}
|
uuids.remove(oldUuid);
|
||||||
}
|
} else {
|
||||||
|
ess.getLogger().warning("Found UUID for " + name + ": " + uuid + " (old: " + oldUuid + "). Skipping.");
|
||||||
final Matcher uuidMatcher = PATTERN_CONFIG_UUID.matcher(configData);
|
|
||||||
if (uuidMatcher.find()) {
|
|
||||||
try {
|
|
||||||
uuid = UUID.fromString(uuidMatcher.group(1));
|
|
||||||
} catch (final IllegalArgumentException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uuid == null) {
|
|
||||||
// Don't import
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Matcher nameMatcher = PATTERN_CONFIG_NAME.matcher(configData);
|
uuids.put(uuid, config.getLong("timestamps.logout", 0L));
|
||||||
if (nameMatcher.find()) {
|
nameToUuidMap.put(name, uuid);
|
||||||
final String username = nameMatcher.group(1);
|
}
|
||||||
if (username != null && username.length() > 0) {
|
} catch (IllegalArgumentException | IndexOutOfBoundsException ignored) {
|
||||||
names.put(StringUtil.safeString(username), uuid);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index % 1000 == 0) {
|
if (!nameToUuidMap.isEmpty()) {
|
||||||
ess.getLogger().info("Reading: " + format.format((100d * (double) index) / files.length)
|
ModernUUIDCache.writeNameUuidMap(usermapFile, nameToUuidMap);
|
||||||
+ "%");
|
|
||||||
}
|
|
||||||
} catch (final IOException e) {
|
|
||||||
ess.getLogger().log(Level.SEVERE, "Error while reading file: ", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ess.getUserMap().getNames().putAll(names);
|
if (!uuids.isEmpty()) {
|
||||||
ess.getUserMap().reloadConfig();
|
ModernUUIDCache.writeUuidCache(uidsFile, uuids.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
doneFile.setProperty("userMapRepaired", true);
|
doneFile.setProperty("newUidCacheBuilt", true);
|
||||||
doneFile.save();
|
doneFile.save();
|
||||||
ess.getLogger().info("Completed usermap repair.");
|
} catch (final IOException e) {
|
||||||
|
ess.getLogger().log(Level.SEVERE, "Error while generating initial uuids/names cache", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beforeSettings() {
|
public void beforeSettings() {
|
||||||
@ -991,6 +994,10 @@ public class EssentialsUpgrade {
|
|||||||
moveMotdRulesToFile("rules");
|
moveMotdRulesToFile("rules");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void preModules() {
|
||||||
|
generateUidCache();
|
||||||
|
}
|
||||||
|
|
||||||
public void afterSettings() {
|
public void afterSettings() {
|
||||||
sanitizeAllUserFilenames();
|
sanitizeAllUserFilenames();
|
||||||
updateUsersPowerToolsFormat();
|
updateUsersPowerToolsFormat();
|
||||||
@ -1001,7 +1008,6 @@ public class EssentialsUpgrade {
|
|||||||
uuidFileChange();
|
uuidFileChange();
|
||||||
banFormatChange();
|
banFormatChange();
|
||||||
warnMetrics();
|
warnMetrics();
|
||||||
repairUserMap();
|
|
||||||
convertIgnoreList();
|
convertIgnoreList();
|
||||||
convertStupidCamelCaseUserdataKeys();
|
convertStupidCamelCaseUserdataKeys();
|
||||||
convertMailList();
|
convertMailList();
|
||||||
|
@ -7,6 +7,7 @@ import com.earth2me.essentials.commands.IEssentialsCommand;
|
|||||||
import com.earth2me.essentials.commands.PlayerNotFoundException;
|
import com.earth2me.essentials.commands.PlayerNotFoundException;
|
||||||
import com.earth2me.essentials.perm.PermissionsHandler;
|
import com.earth2me.essentials.perm.PermissionsHandler;
|
||||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||||
|
import com.earth2me.essentials.userstorage.IUserMap;
|
||||||
import net.ess3.nms.refl.providers.ReflOnlineModeProvider;
|
import net.ess3.nms.refl.providers.ReflOnlineModeProvider;
|
||||||
import net.ess3.provider.ContainerProvider;
|
import net.ess3.provider.ContainerProvider;
|
||||||
import net.ess3.provider.FormattedCommandAliasProvider;
|
import net.ess3.provider.FormattedCommandAliasProvider;
|
||||||
@ -119,6 +120,9 @@ public interface IEssentials extends Plugin {
|
|||||||
|
|
||||||
IItemDb getItemDb();
|
IItemDb getItemDb();
|
||||||
|
|
||||||
|
IUserMap getUsers();
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
UserMap getUserMap();
|
UserMap getUserMap();
|
||||||
|
|
||||||
BalanceTop getBalanceTop();
|
BalanceTop getBalanceTop();
|
||||||
|
@ -330,4 +330,8 @@ public interface IUser {
|
|||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getPastUsernames();
|
||||||
|
|
||||||
|
void addPastUsername(String username);
|
||||||
}
|
}
|
||||||
|
@ -1,155 +1,19 @@
|
|||||||
package com.earth2me.essentials;
|
package com.earth2me.essentials;
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
@Deprecated
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class UUIDMap {
|
public class UUIDMap {
|
||||||
private static final ScheduledExecutorService writeScheduler = Executors.newScheduledThreadPool(1);
|
public UUIDMap() {
|
||||||
private static boolean pendingWrite;
|
|
||||||
private static boolean loading = false;
|
|
||||||
private final transient net.ess3.api.IEssentials ess;
|
|
||||||
private final File userList;
|
|
||||||
private final transient Pattern splitPattern = Pattern.compile(",");
|
|
||||||
private final Runnable writeTaskRunnable;
|
|
||||||
|
|
||||||
public UUIDMap(final net.ess3.api.IEssentials ess) {
|
|
||||||
this.ess = ess;
|
|
||||||
userList = new File(ess.getDataFolder(), "usermap.csv");
|
|
||||||
pendingWrite = false;
|
|
||||||
writeTaskRunnable = () -> {
|
|
||||||
if (pendingWrite) {
|
|
||||||
try {
|
|
||||||
new WriteRunner(ess.getDataFolder(), userList, ess.getUserMap().getNames()).run();
|
|
||||||
} catch (final Throwable t) { // bad code to prevent task from being suppressed
|
|
||||||
t.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
writeScheduler.scheduleWithFixedDelay(writeTaskRunnable, 5, 5, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadAllUsers(final ConcurrentSkipListMap<String, UUID> names, final ConcurrentSkipListMap<UUID, ArrayList<String>> history) {
|
|
||||||
try {
|
|
||||||
if (!userList.exists()) {
|
|
||||||
userList.createNewFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().log(Level.INFO, "Reading usermap from disk");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
names.clear();
|
|
||||||
history.clear();
|
|
||||||
loading = true;
|
|
||||||
|
|
||||||
try (final BufferedReader reader = new BufferedReader(new FileReader(userList))) {
|
|
||||||
while (true) {
|
|
||||||
final String line = reader.readLine();
|
|
||||||
if (line == null) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
final String[] values = splitPattern.split(line);
|
|
||||||
if (values.length == 2) {
|
|
||||||
final String name = values[0];
|
|
||||||
final UUID uuid = UUID.fromString(values[1]);
|
|
||||||
names.put(name, uuid);
|
|
||||||
if (!history.containsKey(uuid)) {
|
|
||||||
final ArrayList<String> list = new ArrayList<>();
|
|
||||||
list.add(name);
|
|
||||||
history.put(uuid, list);
|
|
||||||
} else {
|
|
||||||
final ArrayList<String> list = history.get(uuid);
|
|
||||||
if (!list.contains(name)) {
|
|
||||||
list.add(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
loading = false;
|
|
||||||
} catch (final IOException ex) {
|
|
||||||
Essentials.getWrappedLogger().log(Level.SEVERE, ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeUUIDMap() {
|
public void writeUUIDMap() {
|
||||||
pendingWrite = true;
|
//no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
public void forceWriteUUIDMap() {
|
public void forceWriteUUIDMap() {
|
||||||
if (ess.getSettings().isDebug()) {
|
//no-op
|
||||||
ess.getLogger().log(Level.INFO, "Forcing usermap write to disk");
|
|
||||||
}
|
|
||||||
pendingWrite = true;
|
|
||||||
writeTaskRunnable.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
writeScheduler.submit(writeTaskRunnable);
|
//no-op
|
||||||
writeScheduler.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class WriteRunner implements Runnable {
|
|
||||||
private final File location;
|
|
||||||
private final File endFile;
|
|
||||||
private final Map<String, UUID> names;
|
|
||||||
|
|
||||||
private WriteRunner(final File location, final File endFile, final Map<String, UUID> names) {
|
|
||||||
this.location = location;
|
|
||||||
this.endFile = endFile;
|
|
||||||
this.names = new HashMap<>(names);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
pendingWrite = false;
|
|
||||||
if (loading || names.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File configFile = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
configFile = File.createTempFile("usermap", ".tmp.csv", location);
|
|
||||||
|
|
||||||
final BufferedWriter bWriter = new BufferedWriter(new FileWriter(configFile));
|
|
||||||
for (final Map.Entry<String, UUID> entry : names.entrySet()) {
|
|
||||||
bWriter.write(entry.getKey() + "," + entry.getValue().toString());
|
|
||||||
bWriter.newLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
bWriter.close();
|
|
||||||
Files.move(configFile, endFile);
|
|
||||||
} catch (final IOException ex) {
|
|
||||||
try {
|
|
||||||
if (configFile != null && configFile.exists()) {
|
|
||||||
Files.move(configFile, new File(endFile.getParentFile(), "usermap.bak.csv"));
|
|
||||||
}
|
|
||||||
} catch (final Exception ex2) {
|
|
||||||
Essentials.getWrappedLogger().log(Level.SEVERE, ex2.getMessage(), ex2);
|
|
||||||
}
|
|
||||||
Essentials.getWrappedLogger().log(Level.WARNING, ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
|||||||
this.messageRecipient = new SimpleMessageRecipient(ess, this);
|
this.messageRecipient = new SimpleMessageRecipient(ess, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(final Player base) {
|
public void update(final Player base) {
|
||||||
setBase(base);
|
setBase(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
|||||||
import com.earth2me.essentials.config.entities.CommandCooldown;
|
import com.earth2me.essentials.config.entities.CommandCooldown;
|
||||||
import com.earth2me.essentials.config.entities.LazyLocation;
|
import com.earth2me.essentials.config.entities.LazyLocation;
|
||||||
import com.earth2me.essentials.config.holders.UserConfigHolder;
|
import com.earth2me.essentials.config.holders.UserConfigHolder;
|
||||||
|
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||||
import com.earth2me.essentials.utils.NumberUtil;
|
import com.earth2me.essentials.utils.NumberUtil;
|
||||||
import com.earth2me.essentials.utils.StringUtil;
|
import com.earth2me.essentials.utils.StringUtil;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
@ -43,19 +44,14 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
|||||||
super(base);
|
super(base);
|
||||||
this.ess = ess;
|
this.ess = ess;
|
||||||
final File folder = new File(ess.getDataFolder(), "userdata");
|
final File folder = new File(ess.getDataFolder(), "userdata");
|
||||||
if (!folder.exists()) {
|
if (!folder.exists() && !folder.mkdirs()) {
|
||||||
folder.mkdirs();
|
throw new RuntimeException("Unable to create userdata folder!");
|
||||||
}
|
}
|
||||||
|
|
||||||
String filename;
|
config = new EssentialsUserConfiguration(base.getName(), base.getUniqueId(), new File(folder, base.getUniqueId() + ".yml"));
|
||||||
try {
|
config.setSaveHook(() -> {
|
||||||
filename = base.getUniqueId().toString();
|
config.setRootHolder(UserConfigHolder.class, holder);
|
||||||
} catch (final Throwable ex) {
|
});
|
||||||
ess.getLogger().warning("Falling back to old username system for " + base.getName());
|
|
||||||
filename = base.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
config = new EssentialsUserConfiguration(base.getName(), base.getUniqueId(), new File(folder, filename + ".yml"));
|
|
||||||
reloadConfig();
|
reloadConfig();
|
||||||
|
|
||||||
if (config.getUsername() == null) {
|
if (config.getUsername() == null) {
|
||||||
@ -65,12 +61,15 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
|||||||
|
|
||||||
public final void reset() {
|
public final void reset() {
|
||||||
config.blockingSave();
|
config.blockingSave();
|
||||||
config.getFile().delete();
|
if (!config.getFile().delete()) {
|
||||||
|
ess.getLogger().warning("Unable to delete data file for " + config.getFile().getName());
|
||||||
|
}
|
||||||
if (config.getUsername() != null) {
|
if (config.getUsername() != null) {
|
||||||
ess.getUserMap().removeUser(config.getUsername());
|
final ModernUserMap users = (ModernUserMap) ess.getUsers();
|
||||||
|
users.invalidate(config.getUuid());
|
||||||
if (isNPC()) {
|
if (isNPC()) {
|
||||||
final String uuid = UUID.nameUUIDFromBytes(("NPC:" + StringUtil.safeString(config.getUsername())).getBytes(Charsets.UTF_8)).toString();
|
final String name = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(config.getUsername()) : config.getUsername();
|
||||||
ess.getUserMap().removeUserUUID(uuid);
|
users.invalidate(UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,14 +87,6 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
|||||||
ess.getLogger().log(Level.SEVERE, "Error while reading user config: " + config.getFile().getName(), e);
|
ess.getLogger().log(Level.SEVERE, "Error while reading user config: " + config.getFile().getName(), e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
config.setSaveHook(() -> {
|
|
||||||
try {
|
|
||||||
config.getRootNode().set(UserConfigHolder.class, holder);
|
|
||||||
} catch (SerializationException e) {
|
|
||||||
ess.getLogger().log(Level.SEVERE, "Error while saving user config: " + config.getFile().getName(), e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
money = _getMoney();
|
money = _getMoney();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,9 +580,13 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setLastAccountName(final String lastAccountName) {
|
public void setLastAccountName(final String lastAccountName) {
|
||||||
|
if (getLastAccountName() != null && !getLastAccountName().equals(lastAccountName)) {
|
||||||
|
final List<String> usernames = holder.pastUsernames();
|
||||||
|
usernames.add(0, getLastAccountName());
|
||||||
|
holder.pastUsernames(usernames);
|
||||||
|
}
|
||||||
holder.lastAccountName(lastAccountName);
|
holder.lastAccountName(lastAccountName);
|
||||||
config.save();
|
config.save();
|
||||||
ess.getUserMap().trackUUID(getConfigUUID(), lastAccountName, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean arePowerToolsEnabled() {
|
public boolean arePowerToolsEnabled() {
|
||||||
@ -722,6 +717,17 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
|||||||
config.save();
|
config.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getPastUsernames() {
|
||||||
|
return holder.pastUsernames();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPastUsername(String username) {
|
||||||
|
final List<String> usernames = holder.pastUsernames();
|
||||||
|
usernames.add(0, username);
|
||||||
|
holder.pastUsernames(usernames);
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isShouting() {
|
public boolean isShouting() {
|
||||||
if (holder.shouting() == null) {
|
if (holder.shouting() == null) {
|
||||||
holder.shouting(ess.getSettings().isShoutDefault());
|
holder.shouting(ess.getSettings().isShoutDefault());
|
||||||
|
@ -1,383 +1,70 @@
|
|||||||
package com.earth2me.essentials;
|
package com.earth2me.essentials;
|
||||||
|
|
||||||
import com.earth2me.essentials.api.UserDoesNotExistException;
|
import com.earth2me.essentials.api.UserDoesNotExistException;
|
||||||
import com.earth2me.essentials.utils.StringUtil;
|
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||||
import com.google.common.base.Charsets;
|
import org.bukkit.Bukkit;
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.google.common.cache.CacheLoader;
|
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
|
||||||
import net.ess3.api.IEssentials;
|
|
||||||
import net.ess3.api.MaxMoneyException;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
import java.util.concurrent.ConcurrentSkipListSet;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class UserMap extends CacheLoader<String, User> implements IConf {
|
@Deprecated
|
||||||
private static boolean legacy = false;
|
public class UserMap {
|
||||||
private static Method getLegacy;
|
private final transient ModernUserMap userMap;
|
||||||
private final transient IEssentials ess;
|
private final transient UUIDMap uuidMap;
|
||||||
private final transient ConcurrentSkipListSet<UUID> keys = new ConcurrentSkipListSet<>();
|
|
||||||
private final transient ConcurrentSkipListMap<String, UUID> names = new ConcurrentSkipListMap<>();
|
|
||||||
private final transient ConcurrentSkipListMap<UUID, ArrayList<String>> history = new ConcurrentSkipListMap<>();
|
|
||||||
private final UUIDMap uuidMap;
|
|
||||||
private final transient Cache<String, User> users;
|
|
||||||
private final Pattern validUserPattern = Pattern.compile("^[a-zA-Z0-9_]{2,16}$");
|
|
||||||
|
|
||||||
private static final String WARN_UUID_NOT_REPLACE = "Found UUID {0} for player {1}, but player already has a UUID ({2}). Not replacing UUID in usermap.";
|
public UserMap(final ModernUserMap userMap) {
|
||||||
|
this.userMap = userMap;
|
||||||
public UserMap(final IEssentials ess) {
|
this.uuidMap = new UUIDMap();
|
||||||
super();
|
|
||||||
this.ess = ess;
|
|
||||||
uuidMap = new UUIDMap(ess);
|
|
||||||
//RemovalListener<UUID, User> remListener = new UserMapRemovalListener();
|
|
||||||
//users = CacheBuilder.newBuilder().maximumSize(ess.getSettings().getMaxUserCacheCount()).softValues().removalListener(remListener).build(this);
|
|
||||||
final CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder();
|
|
||||||
final int maxCount = ess.getSettings().getMaxUserCacheCount();
|
|
||||||
try {
|
|
||||||
cacheBuilder.maximumSize(maxCount);
|
|
||||||
} catch (final NoSuchMethodError nsme) {
|
|
||||||
legacy = true;
|
|
||||||
legacyMaximumSize(cacheBuilder, maxCount);
|
|
||||||
}
|
|
||||||
cacheBuilder.softValues();
|
|
||||||
if (!legacy) {
|
|
||||||
users = cacheBuilder.build(this);
|
|
||||||
} else {
|
|
||||||
users = legacyBuild(cacheBuilder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAllUsersAsync(final IEssentials ess) {
|
|
||||||
ess.runTaskAsynchronously(() -> {
|
|
||||||
synchronized (users) {
|
|
||||||
final File userdir = new File(ess.getDataFolder(), "userdata");
|
|
||||||
if (!userdir.exists()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
keys.clear();
|
|
||||||
users.invalidateAll();
|
|
||||||
for (final String string : userdir.list()) {
|
|
||||||
if (!string.endsWith(".yml")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String name = string.substring(0, string.length() - 4);
|
|
||||||
try {
|
|
||||||
keys.add(UUID.fromString(name));
|
|
||||||
} catch (final IllegalArgumentException ex) {
|
|
||||||
//Ignore these users till they rejoin.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uuidMap.loadAllUsers(names, history);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean userExists(final UUID uuid) {
|
|
||||||
return keys.contains(uuid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getUser(final String name) {
|
public User getUser(final String name) {
|
||||||
final String sanitizedName = StringUtil.safeString(name);
|
return userMap.getUser(name);
|
||||||
try {
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().warning("Looking up username " + name + " (" + sanitizedName + ") ...");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (names.containsKey(sanitizedName)) {
|
|
||||||
final UUID uuid = names.get(sanitizedName);
|
|
||||||
return getUser(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().warning(name + "(" + sanitizedName + ") has no known usermap entry");
|
|
||||||
}
|
|
||||||
|
|
||||||
final File userFile = getUserFileFromString(sanitizedName);
|
|
||||||
if (userFile.exists()) {
|
|
||||||
ess.getLogger().info("Importing user " + name + " to usermap.");
|
|
||||||
final User user = new User(new OfflinePlayer(sanitizedName, ess.getServer()), ess);
|
|
||||||
trackUUID(user.getBase().getUniqueId(), user.getName(), true);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
} catch (final UncheckedExecutionException ex) {
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().log(Level.WARNING, ex, () -> String.format("Exception while getting user for %s (%s)", name, sanitizedName));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getUser(final UUID uuid) {
|
public User getUser(final UUID uuid) {
|
||||||
try {
|
return userMap.getUser(uuid);
|
||||||
if (!legacy) {
|
|
||||||
return ((LoadingCache<String, User>) users).get(uuid.toString());
|
|
||||||
} else {
|
|
||||||
return legacyCacheGet(uuid);
|
|
||||||
}
|
|
||||||
} catch (final ExecutionException | UncheckedExecutionException ex) {
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().log(Level.WARNING, ex, () -> "Exception while getting user for " + uuid);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void trackUUID(final UUID uuid, final String name, final boolean replace) {
|
public void trackUUID(final UUID uuid, final String name, final boolean replace) {
|
||||||
if (uuid != null) {
|
// no-op
|
||||||
keys.add(uuid);
|
|
||||||
if (name != null && name.length() > 0) {
|
|
||||||
final String keyName = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(name) : name;
|
|
||||||
if (!names.containsKey(keyName)) {
|
|
||||||
names.put(keyName, uuid);
|
|
||||||
uuidMap.writeUUIDMap();
|
|
||||||
} else if (!isUUIDMatch(uuid, keyName)) {
|
|
||||||
if (replace) {
|
|
||||||
ess.getLogger().info("Found new UUID for " + name + ". Replacing " + names.get(keyName).toString() + " with " + uuid.toString());
|
|
||||||
names.put(keyName, uuid);
|
|
||||||
uuidMap.writeUUIDMap();
|
|
||||||
} else {
|
|
||||||
ess.getLogger().log(Level.INFO, MessageFormat.format(WARN_UUID_NOT_REPLACE, uuid.toString(), name, names.get(keyName).toString()), new RuntimeException());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUUIDMatch(final UUID uuid, final String name) {
|
|
||||||
return names.containsKey(name) && names.get(name).equals(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User load(final String stringUUID) throws Exception {
|
public User load(final String stringUUID) throws Exception {
|
||||||
final UUID uuid = UUID.fromString(stringUUID);
|
return userMap.load(UUID.fromString(stringUUID));
|
||||||
Player player = ess.getServer().getPlayer(uuid);
|
|
||||||
if (player != null) {
|
|
||||||
final User user = new User(player, ess);
|
|
||||||
trackUUID(uuid, user.getName(), true);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
final File userFile = getUserFileFromID(uuid);
|
|
||||||
|
|
||||||
if (userFile.exists()) {
|
|
||||||
player = new OfflinePlayer(uuid, ess.getServer());
|
|
||||||
final User user = new User(player, ess);
|
|
||||||
((OfflinePlayer) player).setName(user.getLastAccountName());
|
|
||||||
trackUUID(uuid, user.getName(), false);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception("User not found!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public User load(final org.bukkit.OfflinePlayer player) throws UserDoesNotExistException {
|
public User load(final org.bukkit.OfflinePlayer player) throws UserDoesNotExistException {
|
||||||
if (player == null) {
|
final Player userPlayer;
|
||||||
throw new IllegalArgumentException("Player cannot be null!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player instanceof Player) {
|
if (player instanceof Player) {
|
||||||
if (ess.getSettings().isDebug()) {
|
userPlayer = (Player) player;
|
||||||
ess.getLogger().info("Loading online OfflinePlayer into user map...");
|
|
||||||
}
|
|
||||||
final User user = new User((Player) player, ess);
|
|
||||||
trackUUID(player.getUniqueId(), player.getName(), true);
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
final File userFile = getUserFileFromID(player.getUniqueId());
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().info("Loading OfflinePlayer into user map. Has data: " + userFile.exists() + " for " + player);
|
|
||||||
}
|
|
||||||
|
|
||||||
final OfflinePlayer essPlayer = new OfflinePlayer(player.getUniqueId(), ess.getServer());
|
|
||||||
final User user = new User(essPlayer, ess);
|
|
||||||
if (userFile.exists()) {
|
|
||||||
essPlayer.setName(user.getLastAccountName());
|
|
||||||
} else {
|
} else {
|
||||||
if (ess.getSettings().isDebug()) {
|
final com.earth2me.essentials.OfflinePlayer essPlayer = new com.earth2me.essentials.OfflinePlayer(player.getUniqueId(), Bukkit.getServer());
|
||||||
ess.getLogger().info("OfflinePlayer usermap load saving user data for " + player);
|
essPlayer.setName(player.getName());
|
||||||
|
userPlayer = essPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this code makes me sad
|
final User user = userMap.getUser(userPlayer);
|
||||||
user.startTransaction();
|
if (user == null) {
|
||||||
try {
|
throw new UserDoesNotExistException("User not found");
|
||||||
user.setMoney(ess.getSettings().getStartingBalance());
|
|
||||||
} catch (MaxMoneyException e) {
|
|
||||||
// Shouldn't happen as it would be an illegal configuration state
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
user.setLastAccountName(user.getName());
|
|
||||||
user.stopTransaction();
|
|
||||||
}
|
|
||||||
|
|
||||||
trackUUID(player.getUniqueId(), user.getName(), false);
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reloadConfig() {
|
|
||||||
getUUIDMap().forceWriteUUIDMap();
|
|
||||||
loadAllUsersAsync(ess);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidateAll() {
|
|
||||||
users.invalidateAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeUser(final String name) {
|
|
||||||
if (names == null) {
|
|
||||||
ess.getLogger().warning("Name collection is null, cannot remove user.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final UUID uuid = names.get(name);
|
|
||||||
if (uuid != null) {
|
|
||||||
keys.remove(uuid);
|
|
||||||
users.invalidate(uuid.toString());
|
|
||||||
}
|
|
||||||
names.remove(name);
|
|
||||||
names.remove(StringUtil.safeString(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeUserUUID(final String uuid) {
|
|
||||||
users.invalidate(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<UUID> getAllUniqueUsers() {
|
public Set<UUID> getAllUniqueUsers() {
|
||||||
return Collections.unmodifiableSet(keys);
|
return userMap.getAllUserUUIDs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getUniqueUsers() {
|
public int getUniqueUsers() {
|
||||||
return keys.size();
|
return userMap.getUserCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ConcurrentSkipListMap<String, UUID> getNames() {
|
protected ConcurrentSkipListMap<String, UUID> getNames() {
|
||||||
return names;
|
return new ConcurrentSkipListMap<>(userMap.getNameCache());
|
||||||
}
|
|
||||||
|
|
||||||
protected ConcurrentSkipListMap<UUID, ArrayList<String>> getHistory() {
|
|
||||||
return history;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getUserHistory(final UUID uuid) {
|
|
||||||
return history.get(uuid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUIDMap getUUIDMap() {
|
public UUIDMap getUUIDMap() {
|
||||||
return uuidMap;
|
return uuidMap;
|
||||||
}
|
}
|
||||||
// class UserMapRemovalListener implements RemovalListener
|
|
||||||
// {
|
|
||||||
// @Override
|
|
||||||
// public void onRemoval(final RemovalNotification notification)
|
|
||||||
// {
|
|
||||||
// Object value = notification.getValue();
|
|
||||||
// if (value != null)
|
|
||||||
// {
|
|
||||||
// ((User)value).cleanup();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
private File getUserFileFromID(final UUID uuid) {
|
|
||||||
final File userFolder = new File(ess.getDataFolder(), "userdata");
|
|
||||||
return new File(userFolder, uuid.toString() + ".yml");
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getUserFileFromString(final String name) {
|
|
||||||
final File userFolder = new File(ess.getDataFolder(), "userdata");
|
|
||||||
return new File(userFolder, StringUtil.sanitizeFileName(name) + ".yml");
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public User getUserFromBukkit(String name) {
|
|
||||||
name = StringUtil.safeString(name);
|
|
||||||
if (ess.getSettings().isDebug()) {
|
|
||||||
ess.getLogger().warning("Using potentially blocking Bukkit UUID lookup for: " + name);
|
|
||||||
}
|
|
||||||
// Don't attempt to look up entirely invalid usernames
|
|
||||||
if (name == null || !validUserPattern.matcher(name).matches()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final org.bukkit.OfflinePlayer offlinePlayer = ess.getServer().getOfflinePlayer(name);
|
|
||||||
if (offlinePlayer == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final UUID uuid;
|
|
||||||
try {
|
|
||||||
uuid = offlinePlayer.getUniqueId();
|
|
||||||
} catch (final UnsupportedOperationException | NullPointerException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// This is how Bukkit generates fake UUIDs
|
|
||||||
if (UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8)).equals(uuid)) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
names.put(name, uuid);
|
|
||||||
return getUser(uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private User legacyCacheGet(final UUID uuid) {
|
|
||||||
if (getLegacy == null) {
|
|
||||||
final Class<?> usersClass = users.getClass();
|
|
||||||
for (final Method m : usersClass.getDeclaredMethods()) {
|
|
||||||
if (m.getName().equals("get")) {
|
|
||||||
getLegacy = m;
|
|
||||||
getLegacy.setAccessible(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return (User) getLegacy.invoke(users, uuid.toString());
|
|
||||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void legacyMaximumSize(final CacheBuilder builder, final int maxCount) {
|
|
||||||
try {
|
|
||||||
final Method maxSizeLegacy = builder.getClass().getDeclaredMethod("maximumSize", Integer.TYPE);
|
|
||||||
maxSizeLegacy.setAccessible(true);
|
|
||||||
maxSizeLegacy.invoke(builder, maxCount);
|
|
||||||
} catch (final NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private Cache<String, User> legacyBuild(final CacheBuilder builder) {
|
|
||||||
Method build = null;
|
|
||||||
for (final Method method : builder.getClass().getDeclaredMethods()) {
|
|
||||||
if (method.getName().equals("build")) {
|
|
||||||
build = method;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Cache<String, User> legacyUsers;
|
|
||||||
try {
|
|
||||||
assert build != null;
|
|
||||||
build.setAccessible(true);
|
|
||||||
legacyUsers = (Cache<String, User>) build.invoke(builder, this);
|
|
||||||
} catch (final IllegalAccessException | InvocationTargetException e) {
|
|
||||||
legacyUsers = null;
|
|
||||||
}
|
|
||||||
return legacyUsers;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.earth2me.essentials.api;
|
|||||||
import com.earth2me.essentials.Trade;
|
import com.earth2me.essentials.Trade;
|
||||||
import com.earth2me.essentials.User;
|
import com.earth2me.essentials.User;
|
||||||
import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
||||||
|
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||||
import com.earth2me.essentials.utils.NumberUtil;
|
import com.earth2me.essentials.utils.NumberUtil;
|
||||||
import com.earth2me.essentials.utils.StringUtil;
|
import com.earth2me.essentials.utils.StringUtil;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
@ -41,9 +42,9 @@ public class Economy {
|
|||||||
ess = aEss;
|
ess = aEss;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createNPCFile(String name) {
|
private static void createNPCFile(final String unsanitizedName) {
|
||||||
final File folder = new File(ess.getDataFolder(), "userdata");
|
final File folder = new File(ess.getDataFolder(), "userdata");
|
||||||
name = StringUtil.safeString(name);
|
final String name = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(unsanitizedName) : unsanitizedName;
|
||||||
if (!folder.exists()) {
|
if (!folder.exists()) {
|
||||||
if (!folder.mkdirs()) {
|
if (!folder.mkdirs()) {
|
||||||
throw new RuntimeException("Error while creating userdata directory!");
|
throw new RuntimeException("Error while creating userdata directory!");
|
||||||
@ -59,9 +60,12 @@ public class Economy {
|
|||||||
npcConfig.load();
|
npcConfig.load();
|
||||||
npcConfig.setProperty("npc", true);
|
npcConfig.setProperty("npc", true);
|
||||||
npcConfig.setProperty("last-account-name", name);
|
npcConfig.setProperty("last-account-name", name);
|
||||||
|
npcConfig.setProperty("npc-name", unsanitizedName);
|
||||||
npcConfig.setProperty("money", ess.getSettings().getStartingBalance());
|
npcConfig.setProperty("money", ess.getSettings().getStartingBalance());
|
||||||
npcConfig.blockingSave();
|
npcConfig.blockingSave();
|
||||||
ess.getUserMap().trackUUID(npcUUID, name, false);
|
// This will load the NPC into the UserMap + UUID cache
|
||||||
|
((ModernUserMap) ess.getUsers()).addCachedNpcName(npcUUID, name);
|
||||||
|
ess.getUsers().getUser(npcUUID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void deleteNPC(final String name) {
|
private static void deleteNPC(final String name) {
|
||||||
|
@ -64,8 +64,8 @@ public class Commandbalancetop extends EssentialsCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 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) {
|
if (ess.getUsers().getUserCount() > MINUSERS) {
|
||||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
sender.sendMessage(tl("orderBalances", ess.getUsers().getUserCount()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ess.runTaskAsynchronously(new Viewer(sender, page, force));
|
ess.runTaskAsynchronously(new Viewer(sender, page, force));
|
||||||
|
@ -3,9 +3,9 @@ package com.earth2me.essentials.commands;
|
|||||||
import com.earth2me.essentials.CommandSource;
|
import com.earth2me.essentials.CommandSource;
|
||||||
import com.earth2me.essentials.EssentialsUpgrade;
|
import com.earth2me.essentials.EssentialsUpgrade;
|
||||||
import com.earth2me.essentials.User;
|
import com.earth2me.essentials.User;
|
||||||
import com.earth2me.essentials.UserMap;
|
|
||||||
import com.earth2me.essentials.economy.EconomyLayer;
|
import com.earth2me.essentials.economy.EconomyLayer;
|
||||||
import com.earth2me.essentials.economy.EconomyLayers;
|
import com.earth2me.essentials.economy.EconomyLayers;
|
||||||
|
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||||
import com.earth2me.essentials.utils.CommandMapUtil;
|
import com.earth2me.essentials.utils.CommandMapUtil;
|
||||||
import com.earth2me.essentials.utils.DateUtil;
|
import com.earth2me.essentials.utils.DateUtil;
|
||||||
import com.earth2me.essentials.utils.EnumUtil;
|
import com.earth2me.essentials.utils.EnumUtil;
|
||||||
@ -13,7 +13,6 @@ import com.earth2me.essentials.utils.FloatUtil;
|
|||||||
import com.earth2me.essentials.utils.NumberUtil;
|
import com.earth2me.essentials.utils.NumberUtil;
|
||||||
import com.earth2me.essentials.utils.PasteUtil;
|
import com.earth2me.essentials.utils.PasteUtil;
|
||||||
import com.earth2me.essentials.utils.VersionUtil;
|
import com.earth2me.essentials.utils.VersionUtil;
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
@ -33,6 +32,7 @@ import org.bukkit.plugin.PluginDescriptionFile;
|
|||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.scheduler.BukkitRunnable;
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
@ -45,12 +45,15 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
import static com.earth2me.essentials.I18n.tl;
|
||||||
@ -147,11 +150,8 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
case "homes":
|
case "homes":
|
||||||
runHomes(server, sender, commandLabel, args);
|
runHomes(server, sender, commandLabel, args);
|
||||||
break;
|
break;
|
||||||
case "uuidconvert":
|
case "usermap":
|
||||||
runUUIDConvert(server, sender, commandLabel, args);
|
runUserMap(sender, args);
|
||||||
break;
|
|
||||||
case "uuidtest":
|
|
||||||
runUUIDTest(server, sender, commandLabel, args);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// "#EasterEgg"
|
// "#EasterEgg"
|
||||||
@ -473,12 +473,11 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
final long daysArg = Long.parseLong(args[1]);
|
final long daysArg = Long.parseLong(args[1]);
|
||||||
final double moneyArg = args.length >= 3 ? FloatUtil.parseDouble(args[2].replaceAll("[^0-9\\.]", "")) : 0;
|
final double moneyArg = args.length >= 3 ? FloatUtil.parseDouble(args[2].replaceAll("[^0-9\\.]", "")) : 0;
|
||||||
final int homesArg = args.length >= 4 && NumberUtil.isInt(args[3]) ? Integer.parseInt(args[3]) : 0;
|
final int homesArg = args.length >= 4 && NumberUtil.isInt(args[3]) ? Integer.parseInt(args[3]) : 0;
|
||||||
final UserMap userMap = ess.getUserMap();
|
|
||||||
|
|
||||||
ess.runTaskAsynchronously(() -> {
|
ess.runTaskAsynchronously(() -> {
|
||||||
final long currTime = System.currentTimeMillis();
|
final long currTime = System.currentTimeMillis();
|
||||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User user = ess.getUserMap().getUser(u);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -523,13 +522,12 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
throw new Exception(HOMES_USAGE);
|
throw new Exception(HOMES_USAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
final UserMap userMap = ess.getUserMap();
|
|
||||||
switch (args[1]) {
|
switch (args[1]) {
|
||||||
case "fix":
|
case "fix":
|
||||||
sender.sendMessage(tl("fixingHomes"));
|
sender.sendMessage(tl("fixingHomes"));
|
||||||
ess.runTaskAsynchronously(() -> {
|
ess.runTaskAsynchronously(() -> {
|
||||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User user = ess.getUserMap().getUser(u);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -553,8 +551,8 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
}
|
}
|
||||||
sender.sendMessage(filterByWorld ? tl("deletingHomesWorld", args[2]) : tl("deletingHomes"));
|
sender.sendMessage(filterByWorld ? tl("deletingHomesWorld", args[2]) : tl("deletingHomes"));
|
||||||
ess.runTaskAsynchronously(() -> {
|
ess.runTaskAsynchronously(() -> {
|
||||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User user = ess.getUserMap().getUser(u);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -577,52 +575,77 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forces a rerun of userdata UUID conversion.
|
// Gets information about cached users
|
||||||
private void runUUIDConvert(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
private void runUserMap(final CommandSource sender, final String[] args) {
|
||||||
sender.sendMessage("Starting Essentials UUID userdata conversion; this may lag the server.");
|
if (!sender.isAuthorized("essentials.usermap", ess)) {
|
||||||
|
return;
|
||||||
final Boolean ignoreUFCache = args.length > 2 && args[1].toLowerCase(Locale.ENGLISH).contains("ignore");
|
|
||||||
EssentialsUpgrade.uuidFileConvert(ess, ignoreUFCache);
|
|
||||||
|
|
||||||
sender.sendMessage("UUID conversion complete. Check your server log for more information.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Looks up various UUIDs for a user.
|
final ModernUserMap userMap = (ModernUserMap) ess.getUsers();
|
||||||
private void runUUIDTest(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
sender.sendMessage(tl("usermapSize", userMap.getCachedCount(), userMap.getUserCount(), ess.getSettings().getMaxUserCacheCount()));
|
||||||
if (args.length < 2) {
|
if (args.length > 1) {
|
||||||
throw new Exception("/<command> uuidtest <name>");
|
if (args[1].equals("full")) {
|
||||||
|
for (final Map.Entry<String, UUID> entry : userMap.getNameCache().entrySet()) {
|
||||||
|
sender.sendMessage(tl("usermapEntry", entry.getKey(), entry.getValue().toString()));
|
||||||
}
|
}
|
||||||
final String name = args[1];
|
} else if (args[1].equals("purge")) {
|
||||||
sender.sendMessage("Looking up UUID for " + name);
|
final boolean seppuku = args.length > 2 && args[2].equals("iknowwhatimdoing");
|
||||||
|
|
||||||
UUID onlineUUID = null;
|
sender.sendMessage(tl("usermapPurge", String.valueOf(seppuku)));
|
||||||
|
|
||||||
for (final Player player : ess.getOnlinePlayers()) {
|
final Set<UUID> uuids = new HashSet<>(ess.getUsers().getAllUserUUIDs());
|
||||||
if (player.getName().equalsIgnoreCase(name)) {
|
ess.runTaskAsynchronously(() -> {
|
||||||
onlineUUID = player.getUniqueId();
|
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||||
break;
|
final File backupFolder = new File(ess.getDataFolder(), "userdata-npc-backup-boogaloo-" + System.currentTimeMillis());
|
||||||
}
|
|
||||||
|
if (!userdataFolder.isDirectory()) {
|
||||||
|
ess.getLogger().warning("Missing userdata folder, aborting usermap purge.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final UUID essUUID = ess.getUserMap().getUser(name).getConfigUUID();
|
if (seppuku && !backupFolder.mkdir()) {
|
||||||
|
ess.getLogger().warning("Unable to create backup folder, aborting usermap purge.");
|
||||||
final org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
|
return;
|
||||||
final UUID bukkituuid = player.getUniqueId();
|
|
||||||
sender.sendMessage("Bukkit Lookup: " + bukkituuid.toString());
|
|
||||||
|
|
||||||
if (onlineUUID != null && onlineUUID != bukkituuid) {
|
|
||||||
sender.sendMessage("Online player: " + onlineUUID.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (essUUID != null && essUUID != bukkituuid) {
|
int total = 0;
|
||||||
sender.sendMessage("Essentials config: " + essUUID.toString());
|
final File[] files = userdataFolder.listFiles(EssentialsUpgrade.YML_FILTER);
|
||||||
|
if (files != null) {
|
||||||
|
for (final File file : files) {
|
||||||
|
try {
|
||||||
|
final String fileName = file.getName();
|
||||||
|
final UUID uuid = UUID.fromString(fileName.substring(0, fileName.length() - 4));
|
||||||
|
if (!uuids.contains(uuid)) {
|
||||||
|
total++;
|
||||||
|
ess.getLogger().warning("Found orphaned userdata file: " + file.getName());
|
||||||
|
if (seppuku) {
|
||||||
|
try {
|
||||||
|
com.google.common.io.Files.move(file, new File(backupFolder, file.getName()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
ess.getLogger().log(Level.WARNING, "Unable to move orphaned userdata file: " + file.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ess.getLogger().info("Found " + total + " orphaned userdata files.");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
final UUID uuid = UUID.fromString(args[1]);
|
||||||
|
for (final Map.Entry<String, UUID> entry : userMap.getNameCache().entrySet()) {
|
||||||
|
if (entry.getValue().equals(uuid)) {
|
||||||
|
sender.sendMessage(tl("usermapEntry", entry.getKey(), args[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
final String sanitizedName = userMap.getSanitizedName(args[1]);
|
||||||
|
sender.sendMessage(tl("usermapEntry", sanitizedName, userMap.getNameCache().get(sanitizedName).toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final UUID npcuuid = UUID.nameUUIDFromBytes(("NPC:" + name).getBytes(Charsets.UTF_8));
|
|
||||||
sender.sendMessage("NPC UUID: " + npcuuid.toString());
|
|
||||||
|
|
||||||
final UUID offlineuuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(Charsets.UTF_8));
|
|
||||||
sender.sendMessage("Offline Mode UUID: " + offlineuuid.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Displays versions of EssentialsX and related plugins.
|
// Displays versions of EssentialsX and related plugins.
|
||||||
@ -750,7 +773,6 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
options.add("cleanup");
|
options.add("cleanup");
|
||||||
options.add("homes");
|
options.add("homes");
|
||||||
//options.add("uuidconvert");
|
//options.add("uuidconvert");
|
||||||
//options.add("uuidtest");
|
|
||||||
//options.add("nya");
|
//options.add("nya");
|
||||||
//options.add("moo");
|
//options.add("moo");
|
||||||
return options;
|
return options;
|
||||||
@ -763,7 +785,6 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "reset":
|
case "reset":
|
||||||
case "uuidtest":
|
|
||||||
if (args.length == 2) {
|
if (args.length == 2) {
|
||||||
return getPlayers(server, sender);
|
return getPlayers(server, sender);
|
||||||
}
|
}
|
||||||
@ -782,11 +803,6 @@ public class Commandessentials extends EssentialsCommand {
|
|||||||
return server.getWorlds().stream().map(World::getName).collect(Collectors.toList());
|
return server.getWorlds().stream().map(World::getName).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "uuidconvert":
|
|
||||||
if (args.length == 2) {
|
|
||||||
return Lists.newArrayList("ignoreUFCache");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "dump":
|
case "dump":
|
||||||
final List<String> list = Lists.newArrayList("config", "kits", "log", "discord", "worth", "tpr", "spawns", "commands", "all");
|
final List<String> list = Lists.newArrayList("config", "kits", "log", "discord", "worth", "tpr", "spawns", "commands", "all");
|
||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
|
@ -261,8 +261,8 @@ public class Commandmail extends EssentialsCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (UUID userid : ess.getUserMap().getAllUniqueUsers()) {
|
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User user = ess.getUserMap().getUser(userid);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
user.sendMail(messageRecipient, message, dateDiff);
|
user.sendMail(messageRecipient, message, dateDiff);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package com.earth2me.essentials.commands;
|
|||||||
|
|
||||||
import com.earth2me.essentials.CommandSource;
|
import com.earth2me.essentials.CommandSource;
|
||||||
import com.earth2me.essentials.User;
|
import com.earth2me.essentials.User;
|
||||||
import com.earth2me.essentials.UserMap;
|
|
||||||
import com.earth2me.essentials.craftbukkit.BanLookup;
|
import com.earth2me.essentials.craftbukkit.BanLookup;
|
||||||
import com.earth2me.essentials.utils.DateUtil;
|
import com.earth2me.essentials.utils.DateUtil;
|
||||||
import com.earth2me.essentials.utils.FormatUtil;
|
import com.earth2me.essentials.utils.FormatUtil;
|
||||||
@ -63,7 +62,7 @@ public class Commandseen extends EssentialsCommand {
|
|||||||
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
|
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
final User userFromBukkit = ess.getUserMap().getUserFromBukkit(args[0]);
|
final User userFromBukkit = ess.getUsers().getUser(args[0]);
|
||||||
try {
|
try {
|
||||||
if (userFromBukkit != null) {
|
if (userFromBukkit != null) {
|
||||||
showUserSeen(userFromBukkit);
|
showUserSeen(userFromBukkit);
|
||||||
@ -103,8 +102,8 @@ public class Commandseen extends EssentialsCommand {
|
|||||||
user.setDisplayNick();
|
user.setDisplayNick();
|
||||||
sender.sendMessage(tl("seenOnline", user.getDisplayName(), DateUtil.formatDateDiff(user.getLastLogin())));
|
sender.sendMessage(tl("seenOnline", user.getDisplayName(), DateUtil.formatDateDiff(user.getLastLogin())));
|
||||||
|
|
||||||
final List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
|
final List<String> history = user.getPastUsernames();
|
||||||
if (history != null && history.size() > 1) {
|
if (history != null && !history.isEmpty()) {
|
||||||
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +138,7 @@ public class Commandseen extends EssentialsCommand {
|
|||||||
user.setDisplayNick();
|
user.setDisplayNick();
|
||||||
if (user.getLastLogout() > 0) {
|
if (user.getLastLogout() > 0) {
|
||||||
sender.sendMessage(tl("seenOffline", user.getName(), DateUtil.formatDateDiff(user.getLastLogout())));
|
sender.sendMessage(tl("seenOffline", user.getName(), DateUtil.formatDateDiff(user.getLastLogout())));
|
||||||
final List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
|
final List<String> history = user.getPastUsernames();
|
||||||
if (history != null && history.size() > 1) {
|
if (history != null && history.size() > 1) {
|
||||||
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
||||||
}
|
}
|
||||||
@ -192,14 +191,12 @@ public class Commandseen extends EssentialsCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void seenIP(final CommandSource sender, final String ipAddress, final String display) {
|
private void seenIP(final CommandSource sender, final String ipAddress, final String display) {
|
||||||
final UserMap userMap = ess.getUserMap();
|
|
||||||
|
|
||||||
sender.sendMessage(tl("runningPlayerMatch", display));
|
sender.sendMessage(tl("runningPlayerMatch", display));
|
||||||
|
|
||||||
ess.runTaskAsynchronously(() -> {
|
ess.runTaskAsynchronously(() -> {
|
||||||
final List<String> matches = new ArrayList<>();
|
final List<String> matches = new ArrayList<>();
|
||||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User user = ess.getUserMap().getUser(u);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,9 @@ public abstract class EssentialsLoopCommand extends EssentialsCommand {
|
|||||||
final User matchedUser = ess.getUser(uuid);
|
final User matchedUser = ess.getUser(uuid);
|
||||||
userConsumer.accept(matchedUser);
|
userConsumer.accept(matchedUser);
|
||||||
} else if (matchWildcards && searchTerm.contentEquals("**")) {
|
} else if (matchWildcards && searchTerm.contentEquals("**")) {
|
||||||
for (final UUID sUser : ess.getUserMap().getAllUniqueUsers()) {
|
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||||
final User matchedUser = ess.getUser(sUser);
|
final User user = ess.getUsers().loadUncachedUser(u);
|
||||||
userConsumer.accept(matchedUser);
|
userConsumer.accept(user);
|
||||||
}
|
}
|
||||||
} else if (matchWildcards && searchTerm.contentEquals("*")) {
|
} else if (matchWildcards && searchTerm.contentEquals("*")) {
|
||||||
final boolean skipHidden = sender.isPlayer() && !ess.getUser(sender.getPlayer()).canInteractVanished();
|
final boolean skipHidden = sender.isPlayer() && !ess.getUser(sender.getPlayer()).canInteractVanished();
|
||||||
|
@ -103,6 +103,15 @@ public class EssentialsConfiguration {
|
|||||||
return configurationNode;
|
return configurationNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRootHolder(final Class<?> holderClass, final Object holder) {
|
||||||
|
try {
|
||||||
|
getRootNode().set(holderClass, holder);
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
Essentials.getWrappedLogger().log(Level.SEVERE, "Error while saving user config: " + configFile.getName(), e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public File getFile() {
|
public File getFile() {
|
||||||
return configFile;
|
return configFile;
|
||||||
}
|
}
|
||||||
@ -366,10 +375,10 @@ public class EssentialsConfiguration {
|
|||||||
} catch (final ParsingException e) {
|
} catch (final ParsingException e) {
|
||||||
final File broken = new File(configFile.getAbsolutePath() + ".broken." + System.currentTimeMillis());
|
final File broken = new File(configFile.getAbsolutePath() + ".broken." + System.currentTimeMillis());
|
||||||
if (configFile.renameTo(broken)) {
|
if (configFile.renameTo(broken)) {
|
||||||
Essentials.getWrappedLogger().log(Level.SEVERE, "The file " + configFile.toString() + " is broken, it has been renamed to " + broken.toString(), e.getCause());
|
Essentials.getWrappedLogger().log(Level.SEVERE, "The file " + configFile + " is broken, it has been renamed to " + broken, e.getCause());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Essentials.getWrappedLogger().log(Level.SEVERE, "The file " + configFile.toString() + " is broken. A backup file has failed to be created", e.getCause());
|
Essentials.getWrappedLogger().log(Level.SEVERE, "The file " + configFile + " is broken. A backup file has failed to be created", e.getCause());
|
||||||
} catch (final ConfigurateException e) {
|
} catch (final ConfigurateException e) {
|
||||||
Essentials.getWrappedLogger().log(Level.SEVERE, e.getMessage(), e);
|
Essentials.getWrappedLogger().log(Level.SEVERE, e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
@ -420,6 +429,10 @@ public class EssentialsConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTransaction() {
|
||||||
|
return transaction.get();
|
||||||
|
}
|
||||||
|
|
||||||
public void setSaveHook(Runnable saveHook) {
|
public void setSaveHook(Runnable saveHook) {
|
||||||
this.saveHook = saveHook;
|
this.saveHook = saveHook;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class EssentialsUserConfiguration extends EssentialsConfiguration {
|
|||||||
|
|
||||||
private File getAltFile() {
|
private File getAltFile() {
|
||||||
final UUID fn = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username.toLowerCase(Locale.ENGLISH)).getBytes(Charsets.UTF_8));
|
final UUID fn = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username.toLowerCase(Locale.ENGLISH)).getBytes(Charsets.UTF_8));
|
||||||
return new File(configFile.getParentFile(), fn.toString() + ".yml");
|
return new File(configFile.getParentFile(), fn + ".yml");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -262,6 +262,16 @@ public class UserConfigHolder {
|
|||||||
this.lastAccountName = value;
|
this.lastAccountName = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @MonotonicNonNull String npcName;
|
||||||
|
|
||||||
|
public String npcName() {
|
||||||
|
return this.npcName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void npcName(final String value) {
|
||||||
|
this.npcName = value;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean powertoolsenabled = true;
|
private boolean powertoolsenabled = true;
|
||||||
|
|
||||||
public boolean powerToolsEnabled() {
|
public boolean powerToolsEnabled() {
|
||||||
@ -332,6 +342,20 @@ public class UserConfigHolder {
|
|||||||
this.shouting = value;
|
this.shouting = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DeleteOnEmpty
|
||||||
|
private @MonotonicNonNull List<String> pastUsernames;
|
||||||
|
|
||||||
|
public List<String> pastUsernames() {
|
||||||
|
if (this.pastUsernames == null) {
|
||||||
|
this.pastUsernames = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return this.pastUsernames;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pastUsernames(List<String> value) {
|
||||||
|
this.pastUsernames = value;
|
||||||
|
}
|
||||||
|
|
||||||
private @NonNull Timestamps timestamps = new Timestamps();
|
private @NonNull Timestamps timestamps = new Timestamps();
|
||||||
|
|
||||||
public Timestamps timestamps() {
|
public Timestamps timestamps() {
|
||||||
|
@ -11,6 +11,7 @@ import net.ess3.api.MaxMoneyException;
|
|||||||
import net.milkbowl.vault.economy.Economy;
|
import net.milkbowl.vault.economy.Economy;
|
||||||
import net.milkbowl.vault.economy.EconomyResponse;
|
import net.milkbowl.vault.economy.EconomyResponse;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -308,21 +309,27 @@ public class VaultEconomyProvider implements Economy {
|
|||||||
npcConfig.setProperty("last-account-name", player.getName());
|
npcConfig.setProperty("last-account-name", player.getName());
|
||||||
npcConfig.setProperty("money", ess.getSettings().getStartingBalance());
|
npcConfig.setProperty("money", ess.getSettings().getStartingBalance());
|
||||||
npcConfig.blockingSave();
|
npcConfig.blockingSave();
|
||||||
ess.getUserMap().trackUUID(player.getUniqueId(), player.getName(), false);
|
// This will load the NPC into the UserMap + UUID cache
|
||||||
|
ess.getUsers().addCachedNpcName(player.getUniqueId(), player.getName());
|
||||||
|
ess.getUsers().getUser(player.getUniqueId());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading a v4 UUID that we somehow didn't track, mark it as a normal player and hope for the best, vault sucks :/
|
// Loading a v4 UUID that we somehow didn't track, mark it as a normal player and hope for the best, vault sucks :/
|
||||||
try {
|
|
||||||
if (ess.getSettings().isDebug()) {
|
if (ess.getSettings().isDebug()) {
|
||||||
ess.getLogger().info("Vault requested a player account creation for a v4 UUID: " + player);
|
ess.getLogger().info("Vault requested a player account creation for a v4 UUID: " + player);
|
||||||
}
|
}
|
||||||
ess.getUserMap().load(player);
|
|
||||||
return true;
|
final Player userPlayer;
|
||||||
} catch (UserDoesNotExistException e) {
|
if (player instanceof Player) {
|
||||||
e.printStackTrace();
|
userPlayer = (Player) player;
|
||||||
return false;
|
} else {
|
||||||
|
final com.earth2me.essentials.OfflinePlayer essPlayer = new com.earth2me.essentials.OfflinePlayer(player.getUniqueId(), ess.getServer());
|
||||||
|
essPlayer.setName(player.getName());
|
||||||
|
userPlayer = essPlayer;
|
||||||
}
|
}
|
||||||
|
ess.getUsers().getUser(userPlayer);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,11 +70,11 @@ public class LuckPermsHandler extends ModernVaultHandler {
|
|||||||
// If the player doesn't exist in the UserMap, just skip
|
// If the player doesn't exist in the UserMap, just skip
|
||||||
// Ess will cause performance problems for permissions checks if it attempts to
|
// Ess will cause performance problems for permissions checks if it attempts to
|
||||||
// perform i/o to load the user data otherwise.
|
// perform i/o to load the user data otherwise.
|
||||||
if (!ess.getUserMap().userExists(target.getUniqueId())) {
|
if (!ess.getUsers().getAllUserUUIDs().contains(target.getUniqueId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final User user = ess.getUser(target);
|
final User user = ess.getUsers().loadUncachedUser(target.getUniqueId());
|
||||||
for (Calculator calculator : this.calculators) {
|
for (Calculator calculator : this.calculators) {
|
||||||
calculator.function.apply(user).forEach(value -> consumer.accept(calculator.id, value));
|
calculator.function.apply(user).forEach(value -> consumer.accept(calculator.id, value));
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ public class KeywordReplacer implements IText {
|
|||||||
replacer = Integer.toString(ess.getOnlinePlayers().size() - playerHidden);
|
replacer = Integer.toString(ess.getOnlinePlayers().size() - playerHidden);
|
||||||
break;
|
break;
|
||||||
case UNIQUE:
|
case UNIQUE:
|
||||||
replacer = NumberFormat.getInstance().format(ess.getUserMap().getUniqueUsers());
|
replacer = NumberFormat.getInstance().format(ess.getUsers().getUserCount());
|
||||||
break;
|
break;
|
||||||
case WORLDS:
|
case WORLDS:
|
||||||
final StringBuilder worldsBuilder = new StringBuilder();
|
final StringBuilder worldsBuilder = new StringBuilder();
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.earth2me.essentials.userstorage;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.User;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface IUserMap {
|
||||||
|
/**
|
||||||
|
* Gets all the UUIDs of every User which has joined.
|
||||||
|
* @return the UUIDs of all Users.
|
||||||
|
*/
|
||||||
|
Set<UUID> getAllUserUUIDs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current amount of users loaded into memory.
|
||||||
|
* @return the amount of users loaded into memory.
|
||||||
|
*/
|
||||||
|
long getCachedCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the amount of users stored by Essentials.
|
||||||
|
* @return the amount of users stored by Essentials.
|
||||||
|
*/
|
||||||
|
int getUserCount();
|
||||||
|
|
||||||
|
User getUser(final UUID uuid);
|
||||||
|
|
||||||
|
User getUser(final Player base);
|
||||||
|
|
||||||
|
User getUser(final String name);
|
||||||
|
|
||||||
|
User loadUncachedUser(final Player base);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a User by the given UUID in the cache, if present, otherwise loads the user without placing them in the cache.
|
||||||
|
* Ideally to be used when running operations on all stored users.
|
||||||
|
*
|
||||||
|
* @param uuid the UUID of the user to get.
|
||||||
|
* @return the User with the given UUID, or null if not found.
|
||||||
|
*/
|
||||||
|
User loadUncachedUser(final UUID uuid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name to UUID cache.
|
||||||
|
* @return the name to UUID cache.
|
||||||
|
*/
|
||||||
|
Map<String, UUID> getNameCache();
|
||||||
|
}
|
@ -0,0 +1,250 @@
|
|||||||
|
package com.earth2me.essentials.userstorage;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.utils.StringUtil;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
import net.ess3.api.IEssentials;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class ModernUUIDCache {
|
||||||
|
private final IEssentials ess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We use a name to uuid map for offline caching due to the following scenario:
|
||||||
|
* * JRoy and mdcfeYT420 play on a server
|
||||||
|
* * mdcfeYT420 changes his name to mdcfe
|
||||||
|
* * mdcfe doesn't log in the server for 31 days
|
||||||
|
* * JRoy changes his name to mdcfeYT420
|
||||||
|
* * mdcfeYT420 (previously JRoy) logs in the server
|
||||||
|
* In a UUID->Name based map, different uuids now point to the same name
|
||||||
|
* preventing any command which allows for offline players from resolving a
|
||||||
|
* given uuid from a given name.
|
||||||
|
* <p>
|
||||||
|
* This map is backed by a file-based cache. If this cache is missing, a new
|
||||||
|
* one is populated by iterating over all files in the userdata folder and
|
||||||
|
* caching the {@code last-account-name} value.
|
||||||
|
*/
|
||||||
|
private final ConcurrentHashMap<String, UUID> nameToUuidMap = new ConcurrentHashMap<>();
|
||||||
|
private final CopyOnWriteArraySet<UUID> uuidCache = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
private final ScheduledExecutorService writeExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
private final AtomicBoolean pendingNameWrite = new AtomicBoolean(false);
|
||||||
|
private final AtomicBoolean pendingUuidWrite = new AtomicBoolean(false);
|
||||||
|
private final File nameToUuidFile;
|
||||||
|
private final File uuidCacheFile;
|
||||||
|
|
||||||
|
public ModernUUIDCache(final IEssentials ess) {
|
||||||
|
this.ess = ess;
|
||||||
|
this.nameToUuidFile = new File(ess.getDataFolder(), "usermap.bin");
|
||||||
|
this.uuidCacheFile = new File(ess.getDataFolder(), "uuids.bin");
|
||||||
|
loadCache();
|
||||||
|
writeExecutor.scheduleWithFixedDelay(() -> {
|
||||||
|
if (pendingNameWrite.compareAndSet(true, false)) {
|
||||||
|
saveNameToUuidCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingUuidWrite.compareAndSet(true, false)) {
|
||||||
|
saveUuidCache();
|
||||||
|
}
|
||||||
|
}, 5, 5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UUID getCachedUUID(final String name) {
|
||||||
|
return nameToUuidMap.get(getSanitizedName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Set<UUID> getCachedUUIDs() {
|
||||||
|
return Collections.unmodifiableSet(uuidCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<String, UUID> getNameCache() {
|
||||||
|
return Collections.unmodifiableMap(nameToUuidMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getCacheSize() {
|
||||||
|
return uuidCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getSanitizedName(final String name) {
|
||||||
|
return ess.getSettings().isSafeUsermap() ? StringUtil.safeString(name) : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateCache(final UUID uuid, final String name) {
|
||||||
|
if (uuidCache.add(uuid)) {
|
||||||
|
pendingUuidWrite.set(true);
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
final String sanitizedName = getSanitizedName(name);
|
||||||
|
final UUID replacedUuid = nameToUuidMap.put(sanitizedName, uuid);
|
||||||
|
if (!uuid.equals(replacedUuid)) {
|
||||||
|
if (ess.getSettings().isDebug()) {
|
||||||
|
ess.getLogger().log(Level.WARNING, "Replaced UUID during cache update for " + sanitizedName + ": " + replacedUuid + " -> " + uuid);
|
||||||
|
}
|
||||||
|
pendingNameWrite.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeCache(final UUID uuid) {
|
||||||
|
if (uuid == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uuidCache.remove(uuid)) {
|
||||||
|
pendingUuidWrite.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<String> toRemove = new HashSet<>();
|
||||||
|
for (final Map.Entry<String, UUID> entry : nameToUuidMap.entrySet()) {
|
||||||
|
if (uuid.equals(entry.getValue())) {
|
||||||
|
toRemove.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final String name : toRemove) {
|
||||||
|
nameToUuidMap.remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toRemove.isEmpty()) {
|
||||||
|
pendingNameWrite.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCache() {
|
||||||
|
final boolean debug = ess.getSettings().isDebug();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!nameToUuidFile.exists()) {
|
||||||
|
if (!nameToUuidFile.createNewFile()) {
|
||||||
|
throw new RuntimeException("Error while creating usermap.bin");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
ess.getLogger().log(Level.INFO, "Loading Name->UUID cache from disk...");
|
||||||
|
}
|
||||||
|
|
||||||
|
nameToUuidMap.clear();
|
||||||
|
|
||||||
|
try (final DataInputStream dis = new DataInputStream(new FileInputStream(nameToUuidFile))) {
|
||||||
|
while (dis.available() > 0) {
|
||||||
|
final String username = dis.readUTF();
|
||||||
|
final UUID uuid = new UUID(dis.readLong(), dis.readLong());
|
||||||
|
final UUID previous = nameToUuidMap.put(username, uuid);
|
||||||
|
if (previous != null && debug) {
|
||||||
|
ess.getLogger().log(Level.WARNING, "Replaced UUID during cache load for " + username + ": " + previous + " -> " + uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
ess.getLogger().log(Level.SEVERE, "Error while loading Name->UUID cache", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!uuidCacheFile.exists()) {
|
||||||
|
if (!uuidCacheFile.createNewFile()) {
|
||||||
|
throw new RuntimeException("Error while creating uuids.bin");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
ess.getLogger().log(Level.INFO, "Loading UUID cache from disk...");
|
||||||
|
}
|
||||||
|
|
||||||
|
uuidCache.clear();
|
||||||
|
|
||||||
|
try (final DataInputStream dis = new DataInputStream(new FileInputStream(uuidCacheFile))) {
|
||||||
|
while (dis.available() > 0) {
|
||||||
|
final UUID uuid = new UUID(dis.readLong(), dis.readLong());
|
||||||
|
if (uuidCache.contains(uuid) && debug) {
|
||||||
|
ess.getLogger().log(Level.WARNING, "UUID " + uuid + " duplicated in cache");
|
||||||
|
}
|
||||||
|
uuidCache.add(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
ess.getLogger().log(Level.SEVERE, "Error while loading UUID cache", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveUuidCache() {
|
||||||
|
if (ess.getSettings().isDebug()) {
|
||||||
|
ess.getLogger().log(Level.INFO, "Saving UUID cache to disk...");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final File tmpMap = File.createTempFile("uuids", ".tmp.bin", ess.getDataFolder());
|
||||||
|
|
||||||
|
writeUuidCache(tmpMap, uuidCache);
|
||||||
|
//noinspection UnstableApiUsage
|
||||||
|
Files.move(tmpMap, uuidCacheFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
ess.getLogger().log(Level.SEVERE, "Error while saving UUID cache", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveNameToUuidCache() {
|
||||||
|
if (ess.getSettings().isDebug()) {
|
||||||
|
ess.getLogger().log(Level.INFO, "Saving Name->UUID cache to disk...");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final File tmpMap = File.createTempFile("usermap", ".tmp.bin", ess.getDataFolder());
|
||||||
|
|
||||||
|
writeNameUuidMap(tmpMap, nameToUuidMap);
|
||||||
|
//noinspection UnstableApiUsage
|
||||||
|
Files.move(tmpMap, nameToUuidFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
ess.getLogger().log(Level.SEVERE, "Error while saving Name->UUID cache", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void blockingSave() {
|
||||||
|
saveUuidCache();
|
||||||
|
saveNameToUuidCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeUuidCache(final File file, Set<UUID> uuids) throws IOException {
|
||||||
|
try (final DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
|
||||||
|
for (final UUID uuid: uuids) {
|
||||||
|
dos.writeLong(uuid.getMostSignificantBits());
|
||||||
|
dos.writeLong(uuid.getLeastSignificantBits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeNameUuidMap(final File file, final Map<String, UUID> nameToUuidMap) throws IOException {
|
||||||
|
try (final DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) {
|
||||||
|
for (final Map.Entry<String, UUID> entry : nameToUuidMap.entrySet()) {
|
||||||
|
dos.writeUTF(entry.getKey());
|
||||||
|
final UUID uuid = entry.getValue();
|
||||||
|
dos.writeLong(uuid.getMostSignificantBits());
|
||||||
|
dos.writeLong(uuid.getLeastSignificantBits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
writeExecutor.shutdownNow();
|
||||||
|
blockingSave();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
package com.earth2me.essentials.userstorage;
|
||||||
|
|
||||||
|
import com.earth2me.essentials.OfflinePlayer;
|
||||||
|
import com.earth2me.essentials.User;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import net.ess3.api.IEssentials;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
public class ModernUserMap extends CacheLoader<UUID, User> implements IUserMap {
|
||||||
|
private final transient IEssentials ess;
|
||||||
|
private final transient ModernUUIDCache uuidCache;
|
||||||
|
private final transient LoadingCache<UUID, User> userCache;
|
||||||
|
|
||||||
|
public ModernUserMap(final IEssentials ess) {
|
||||||
|
this.ess = ess;
|
||||||
|
this.uuidCache = new ModernUUIDCache(ess);
|
||||||
|
this.userCache = CacheBuilder.newBuilder()
|
||||||
|
.maximumSize(ess.getSettings().getMaxUserCacheCount())
|
||||||
|
.softValues()
|
||||||
|
.build(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<UUID> getAllUserUUIDs() {
|
||||||
|
return uuidCache.getCachedUUIDs();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCachedCount() {
|
||||||
|
return userCache.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getUserCount() {
|
||||||
|
return uuidCache.getCacheSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUser(final UUID uuid) {
|
||||||
|
if (uuid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return userCache.get(uuid);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
if (ess.getSettings().isDebug()) {
|
||||||
|
ess.getLogger().log(Level.WARNING, "Exception while getting user for " + uuid, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUser(final Player base) {
|
||||||
|
final User user = loadUncachedUser(base);
|
||||||
|
userCache.put(user.getUUID(), user);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUser(final String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final User user = getUser(uuidCache.getCachedUUID(name));
|
||||||
|
if (user != null && user.getBase() instanceof OfflinePlayer) {
|
||||||
|
if (user.getLastAccountName() != null) {
|
||||||
|
((OfflinePlayer) user.getBase()).setName(user.getLastAccountName());
|
||||||
|
} else {
|
||||||
|
((OfflinePlayer) user.getBase()).setName(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCachedNpcName(final UUID uuid, final String name) {
|
||||||
|
if (uuid == null || name == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uuidCache.updateCache(uuid, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
|
@Override
|
||||||
|
public User load(final UUID uuid) throws Exception {
|
||||||
|
final User user = loadUncachedUser(uuid);
|
||||||
|
if (user != null) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("User not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User loadUncachedUser(final Player base) {
|
||||||
|
if (base == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
User user = getUser(base.getUniqueId());
|
||||||
|
if (user == null) {
|
||||||
|
ess.getLogger().log(Level.INFO, "Essentials created a User for " + base.getName() + " (" + base.getUniqueId() + ") for non Bukkit type: " + base.getClass().getName());
|
||||||
|
user = new User(base, ess);
|
||||||
|
} else if (!base.equals(user.getBase())) {
|
||||||
|
ess.getLogger().log(Level.INFO, "Essentials updated the underlying Player object for " + user.getUUID());
|
||||||
|
user.update(base);
|
||||||
|
}
|
||||||
|
uuidCache.updateCache(user.getUUID(), user.getName());
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User loadUncachedUser(final UUID uuid) {
|
||||||
|
User user = userCache.getIfPresent(uuid);
|
||||||
|
if (user != null) {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player player = ess.getServer().getPlayer(uuid);
|
||||||
|
if (player != null) {
|
||||||
|
// This is a real player, cache their UUID.
|
||||||
|
user = new User(player, ess);
|
||||||
|
uuidCache.updateCache(uuid, player.getName());
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
final File userFile = getUserFile(uuid);
|
||||||
|
if (userFile.exists()) {
|
||||||
|
player = new OfflinePlayer(uuid, ess.getServer());
|
||||||
|
user = new User(player, ess);
|
||||||
|
((OfflinePlayer) player).setName(user.getLastAccountName());
|
||||||
|
uuidCache.updateCache(uuid, null);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, UUID> getNameCache() {
|
||||||
|
return uuidCache.getNameCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSanitizedName(final String name) {
|
||||||
|
return uuidCache.getSanitizedName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void blockingSave() {
|
||||||
|
uuidCache.blockingSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate(final UUID uuid) {
|
||||||
|
userCache.invalidate(uuid);
|
||||||
|
uuidCache.removeCache(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getUserFile(final UUID uuid) {
|
||||||
|
return new File(new File(ess.getDataFolder(), "userdata"), uuid.toString() + ".yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
uuidCache.shutdown();
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,9 @@ public final class StringUtil {
|
|||||||
|
|
||||||
//Used to clean strings/names before saving as filenames/permissions
|
//Used to clean strings/names before saving as filenames/permissions
|
||||||
public static String safeString(final String string) {
|
public static String safeString(final String string) {
|
||||||
|
if (string == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return STRICTINVALIDCHARS.matcher(string.toLowerCase(Locale.ENGLISH)).replaceAll("_");
|
return STRICTINVALIDCHARS.matcher(string.toLowerCase(Locale.ENGLISH)).replaceAll("_");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1415,6 +1415,9 @@ userIsAwaySelf=\u00a77You are now AFK.
|
|||||||
userIsAwaySelfWithMessage=\u00a77You are now AFK.
|
userIsAwaySelfWithMessage=\u00a77You are now AFK.
|
||||||
userIsNotAwaySelf=\u00a77You are no longer AFK.
|
userIsNotAwaySelf=\u00a77You are no longer AFK.
|
||||||
userJailed=\u00a76You have been jailed\!
|
userJailed=\u00a76You have been jailed\!
|
||||||
|
usermapEntry=\u00a7c{0} \u00a76is mapped to \u00a7c{1}\u00a76.
|
||||||
|
usermapPurge=\u00a76Checking for files in userdata that are not mapped, results will be logged to console. Destructive Mode: {0}
|
||||||
|
usermapSize=\u00a76Current cached users in user map is \u00a7c{0}\u00a76/\u00a7c{1}\u00a76/\u00a7c{2}\u00a76.
|
||||||
userUnknown=\u00a74Warning\: The user ''\u00a7c{0}\u00a74'' has never joined this server.
|
userUnknown=\u00a74Warning\: The user ''\u00a7c{0}\u00a74'' has never joined this server.
|
||||||
usingTempFolderForTesting=Using temp folder for testing\:
|
usingTempFolderForTesting=Using temp folder for testing\:
|
||||||
vanish=\u00a76Vanish for {0}\u00a76\: {1}
|
vanish=\u00a76Vanish for {0}\u00a76\: {1}
|
||||||
|
@ -137,7 +137,7 @@ public class BukkitListener implements Listener {
|
|||||||
MessageUtil.sanitizeDiscordMarkdown(player.getDisplayName()),
|
MessageUtil.sanitizeDiscordMarkdown(player.getDisplayName()),
|
||||||
MessageUtil.sanitizeDiscordMarkdown(message),
|
MessageUtil.sanitizeDiscordMarkdown(message),
|
||||||
jda.getPlugin().getEss().getOnlinePlayers().size() - (join ? 0 : 1),
|
jda.getPlugin().getEss().getOnlinePlayers().size() - (join ? 0 : 1),
|
||||||
jda.getPlugin().getEss().getUserMap().getUniqueUsers()),
|
jda.getPlugin().getEss().getUsers().getUserCount()),
|
||||||
player);
|
player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1341,7 +1341,7 @@ public class OfflinePlayer implements Player {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setName(final String name) {
|
public void setName(final String name) {
|
||||||
this.name = base.getName();
|
this.name = base.getName();
|
||||||
if (this.name == null) {
|
if (this.name == null) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
Loading…
Reference in New Issue
Block a user