mirror of
https://github.com/EssentialsX/Essentials.git
synced 2024-12-22 00:58:50 +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/userdata/testplayer1.yml
|
||||
/Essentials/usermap.csv
|
||||
/Essentials/usermap.bin
|
||||
/Essentials/uuids.bin
|
||||
|
||||
# Build files
|
||||
.gradle/
|
||||
|
@ -24,4 +24,4 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
@ -28,8 +28,8 @@ public class BalanceTopImpl implements BalanceTop {
|
||||
private void calculateBalanceTopMap() {
|
||||
final List<Entry> entries = new LinkedList<>();
|
||||
BigDecimal newTotal = BigDecimal.ZERO;
|
||||
for (UUID u : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
for (UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
if (user != null) {
|
||||
if (!ess.getSettings().isNpcsInBalanceRanking() && user.isNPC()) {
|
||||
// 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.SimpleTextInput;
|
||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
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 PermissionsHandler permissionsHandler;
|
||||
private transient AlternativeCommandsHandler alternativeCommandsHandler;
|
||||
private transient UserMap userMap;
|
||||
@Deprecated
|
||||
private transient UserMap legacyUserMap;
|
||||
private transient ModernUserMap userMap;
|
||||
private transient BalanceTopImpl balanceTop;
|
||||
private transient ExecuteTimer execTimer;
|
||||
private transient MailService mail;
|
||||
@ -222,7 +225,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
LOGGER.log(Level.INFO, dataFolder.toString());
|
||||
settings = new Settings(this);
|
||||
mail = new MailServiceImpl(this);
|
||||
userMap = new UserMap(this);
|
||||
userMap = new ModernUserMap(this);
|
||||
balanceTop = new BalanceTopImpl(this);
|
||||
permissionsHandler = new PermissionsHandler(this, false);
|
||||
Economy.setEss(this);
|
||||
@ -301,11 +304,14 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
confList.add(settings);
|
||||
execTimer.mark("Settings");
|
||||
|
||||
upgrade.preModules();
|
||||
execTimer.mark("Upgrade2");
|
||||
|
||||
mail = new MailServiceImpl(this);
|
||||
execTimer.mark("Init(Mail)");
|
||||
|
||||
userMap = new UserMap(this);
|
||||
confList.add(userMap);
|
||||
userMap = new ModernUserMap(this);
|
||||
legacyUserMap = new UserMap(userMap);
|
||||
execTimer.mark("Init(Usermap)");
|
||||
|
||||
balanceTop = new BalanceTopImpl(this);
|
||||
@ -317,7 +323,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
execTimer.mark("Kits");
|
||||
|
||||
upgrade.afterSettings();
|
||||
execTimer.mark("Upgrade2");
|
||||
execTimer.mark("Upgrade3");
|
||||
|
||||
warps = new Warps(this.getDataFolder());
|
||||
confList.add(warps);
|
||||
@ -588,7 +594,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
|
||||
Economy.setEss(null);
|
||||
Trade.closeLog();
|
||||
getUserMap().getUUIDMap().shutdown();
|
||||
getUsers().shutdown();
|
||||
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
@ -1067,7 +1073,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
if (getSettings().isDebug()) {
|
||||
LOGGER.log(Level.INFO, "Constructing new userfile from base player " + base.getName());
|
||||
}
|
||||
user = new User(base, this);
|
||||
user = userMap.loadUncachedUser(base);
|
||||
} else {
|
||||
user.update(base);
|
||||
}
|
||||
@ -1203,7 +1209,13 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public UserMap getUserMap() {
|
||||
return legacyUserMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModernUserMap getUsers() {
|
||||
return userMap;
|
||||
}
|
||||
|
||||
|
@ -308,6 +308,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
|
||||
|
||||
ess.getBackup().onPlayerJoin();
|
||||
final User dUser = ess.getUser(player);
|
||||
dUser.update(player);
|
||||
|
||||
dUser.startTransaction();
|
||||
if (dUser.isNPC()) {
|
||||
@ -367,7 +368,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
|
||||
} else if (ess.getSettings().isCustomJoinMessage()) {
|
||||
final String msg = (newUsername ? ess.getSettings().getCustomNewUsernameMessage() : ess.getSettings().getCustomJoinMessage())
|
||||
.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("{UPTIME}", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime()))
|
||||
.replace("{PREFIX}", FormatUtil.replaceFormat(ess.getPermissionsHandler().getPrefix(player)))
|
||||
@ -527,6 +528,7 @@ public class EssentialsPlayerListener implements Listener, FakeAccessor {
|
||||
public void onPlayerLogin(final PlayerLoginEvent event) {
|
||||
if (event.getResult() == Result.KICK_FULL) {
|
||||
final User kfuser = ess.getUser(event.getPlayer());
|
||||
kfuser.update(event.getPlayer());
|
||||
if (kfuser.isAuthorized("essentials.joinfullserver")) {
|
||||
event.allow();
|
||||
return;
|
||||
|
@ -4,9 +4,9 @@ import com.earth2me.essentials.config.ConfigurateUtil;
|
||||
import com.earth2me.essentials.config.EssentialsConfiguration;
|
||||
import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
||||
import com.earth2me.essentials.craftbukkit.BanLookup;
|
||||
import com.earth2me.essentials.userstorage.ModernUUIDCache;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.ess3.api.IEssentials;
|
||||
@ -31,10 +31,10 @@ import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@ -47,13 +47,12 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
|
||||
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 Pattern PATTERN_CONFIG_UUID = Pattern.compile(PATTERN_CONFIG_UUID_REGEX);
|
||||
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;
|
||||
|
||||
if (showProgress == 0) {
|
||||
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
|
||||
ess.getLogger().info("Converted " + countFiles + "/" + userdir.list().length);
|
||||
}
|
||||
|
||||
@ -103,7 +101,8 @@ public class EssentialsUpgrade {
|
||||
final EssentialsUserConfiguration config;
|
||||
UUID uuid = null;
|
||||
try {
|
||||
uuid = UUID.fromString(name);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
UUID.fromString(name);
|
||||
} catch (final IllegalArgumentException ex) {
|
||||
final File file = new File(userdir, string);
|
||||
final EssentialsConfiguration conf = new EssentialsConfiguration(file);
|
||||
@ -115,6 +114,7 @@ public class EssentialsUpgrade {
|
||||
|
||||
final String uuidString = conf.getString(uuidConf, null);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
uuid = UUID.fromString(uuidString);
|
||||
@ -122,7 +122,7 @@ public class EssentialsUpgrade {
|
||||
break;
|
||||
} catch (final Exception ex2) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -130,23 +130,24 @@ public class EssentialsUpgrade {
|
||||
uuid = player.getUniqueId();
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (uuid != null) {
|
||||
countBukkit++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
if (uuid != null) {
|
||||
conf.blockingSave();
|
||||
config = new EssentialsUserConfiguration(name, uuid, new File(userdir, uuid + ".yml"));
|
||||
config.convertLegacyFile();
|
||||
ess.getUserMap().trackUUID(uuid, name, false);
|
||||
ess.getUsers().loadUncachedUser(uuid);
|
||||
continue;
|
||||
}
|
||||
countFails++;
|
||||
}
|
||||
}
|
||||
ess.getUserMap().getUUIDMap().forceWriteUUIDMap();
|
||||
|
||||
ess.getLogger().info("Converted " + countFiles + "/" + countFiles + ". Conversion complete.");
|
||||
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);
|
||||
}
|
||||
|
||||
private void repairUserMap() {
|
||||
if (doneFile.getBoolean("userMapRepaired", false)) {
|
||||
public void generateUidCache() {
|
||||
if (doneFile.getBoolean("newUidCacheBuilt", false)) {
|
||||
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");
|
||||
if (!userdataFolder.isDirectory()) {
|
||||
|
||||
if (!userdataFolder.isDirectory() || usermapFile.exists() || uidsFile.exists()) {
|
||||
ess.getLogger().warning("Missing userdata folder, aborting");
|
||||
doneFile.setProperty("newUidCacheBuilt", true);
|
||||
doneFile.save();
|
||||
return;
|
||||
}
|
||||
final File[] files = userdataFolder.listFiles(YML_FILTER);
|
||||
|
||||
final DecimalFormat format = new DecimalFormat("#0.00");
|
||||
final Map<String, UUID> names = Maps.newHashMap();
|
||||
|
||||
for (int index = 0; index < files.length; index++) {
|
||||
final File file = files[index];
|
||||
try {
|
||||
UUID uuid = null;
|
||||
final String filename = file.getName();
|
||||
final String configData = new String(java.nio.file.Files.readAllBytes(file.toPath()), Charsets.UTF_8);
|
||||
|
||||
if (filename.length() > 36) {
|
||||
try {
|
||||
// ".yml" ending has 4 chars...
|
||||
uuid = UUID.fromString(filename.substring(0, filename.length() - 4));
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
final Matcher nameMatcher = PATTERN_CONFIG_NAME.matcher(configData);
|
||||
if (nameMatcher.find()) {
|
||||
final String username = nameMatcher.group(1);
|
||||
if (username != null && username.length() > 0) {
|
||||
names.put(StringUtil.safeString(username), uuid);
|
||||
}
|
||||
}
|
||||
|
||||
if (index % 1000 == 0) {
|
||||
ess.getLogger().info("Reading: " + format.format((100d * (double) index) / files.length)
|
||||
+ "%");
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
ess.getLogger().log(Level.SEVERE, "Error while reading file: ", e);
|
||||
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);
|
||||
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));
|
||||
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 (name != null) {
|
||||
if (nameToUuidMap.containsKey(name)) {
|
||||
final UUID oldUuid = nameToUuidMap.get(name);
|
||||
if (oldUuid.version() < uuid.version() || (oldUuid.version() == uuid.version() && uuids.get(oldUuid) < time)) {
|
||||
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.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
uuids.put(uuid, config.getLong("timestamps.logout", 0L));
|
||||
nameToUuidMap.put(name, uuid);
|
||||
}
|
||||
} catch (IllegalArgumentException | IndexOutOfBoundsException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nameToUuidMap.isEmpty()) {
|
||||
ModernUUIDCache.writeNameUuidMap(usermapFile, nameToUuidMap);
|
||||
}
|
||||
|
||||
if (!uuids.isEmpty()) {
|
||||
ModernUUIDCache.writeUuidCache(uidsFile, uuids.keySet());
|
||||
}
|
||||
|
||||
doneFile.setProperty("newUidCacheBuilt", true);
|
||||
doneFile.save();
|
||||
} catch (final IOException e) {
|
||||
ess.getLogger().log(Level.SEVERE, "Error while generating initial uuids/names cache", e);
|
||||
}
|
||||
|
||||
ess.getUserMap().getNames().putAll(names);
|
||||
ess.getUserMap().reloadConfig();
|
||||
|
||||
doneFile.setProperty("userMapRepaired", true);
|
||||
doneFile.save();
|
||||
ess.getLogger().info("Completed usermap repair.");
|
||||
}
|
||||
|
||||
public void beforeSettings() {
|
||||
@ -991,6 +994,10 @@ public class EssentialsUpgrade {
|
||||
moveMotdRulesToFile("rules");
|
||||
}
|
||||
|
||||
public void preModules() {
|
||||
generateUidCache();
|
||||
}
|
||||
|
||||
public void afterSettings() {
|
||||
sanitizeAllUserFilenames();
|
||||
updateUsersPowerToolsFormat();
|
||||
@ -1001,7 +1008,6 @@ public class EssentialsUpgrade {
|
||||
uuidFileChange();
|
||||
banFormatChange();
|
||||
warnMetrics();
|
||||
repairUserMap();
|
||||
convertIgnoreList();
|
||||
convertStupidCamelCaseUserdataKeys();
|
||||
convertMailList();
|
||||
|
@ -7,6 +7,7 @@ import com.earth2me.essentials.commands.IEssentialsCommand;
|
||||
import com.earth2me.essentials.commands.PlayerNotFoundException;
|
||||
import com.earth2me.essentials.perm.PermissionsHandler;
|
||||
import com.earth2me.essentials.updatecheck.UpdateChecker;
|
||||
import com.earth2me.essentials.userstorage.IUserMap;
|
||||
import net.ess3.nms.refl.providers.ReflOnlineModeProvider;
|
||||
import net.ess3.provider.ContainerProvider;
|
||||
import net.ess3.provider.FormattedCommandAliasProvider;
|
||||
@ -119,6 +120,9 @@ public interface IEssentials extends Plugin {
|
||||
|
||||
IItemDb getItemDb();
|
||||
|
||||
IUserMap getUsers();
|
||||
|
||||
@Deprecated
|
||||
UserMap getUserMap();
|
||||
|
||||
BalanceTop getBalanceTop();
|
||||
|
@ -330,4 +330,8 @@ public interface IUser {
|
||||
this.time = time;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> getPastUsernames();
|
||||
|
||||
void addPastUsername(String username);
|
||||
}
|
||||
|
@ -1,155 +1,19 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
|
||||
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;
|
||||
|
||||
@Deprecated
|
||||
public class UUIDMap {
|
||||
private static final ScheduledExecutorService writeScheduler = Executors.newScheduledThreadPool(1);
|
||||
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 UUIDMap() {
|
||||
}
|
||||
|
||||
public void writeUUIDMap() {
|
||||
pendingWrite = true;
|
||||
//no-op
|
||||
}
|
||||
|
||||
public void forceWriteUUIDMap() {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().log(Level.INFO, "Forcing usermap write to disk");
|
||||
}
|
||||
pendingWrite = true;
|
||||
writeTaskRunnable.run();
|
||||
//no-op
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
writeScheduler.submit(writeTaskRunnable);
|
||||
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);
|
||||
}
|
||||
}
|
||||
//no-op
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public class User extends UserData implements Comparable<User>, IMessageRecipien
|
||||
this.messageRecipient = new SimpleMessageRecipient(ess, this);
|
||||
}
|
||||
|
||||
void update(final Player base) {
|
||||
public void update(final Player 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.LazyLocation;
|
||||
import com.earth2me.essentials.config.holders.UserConfigHolder;
|
||||
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
@ -43,19 +44,14 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
super(base);
|
||||
this.ess = ess;
|
||||
final File folder = new File(ess.getDataFolder(), "userdata");
|
||||
if (!folder.exists()) {
|
||||
folder.mkdirs();
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
throw new RuntimeException("Unable to create userdata folder!");
|
||||
}
|
||||
|
||||
String filename;
|
||||
try {
|
||||
filename = base.getUniqueId().toString();
|
||||
} 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"));
|
||||
config = new EssentialsUserConfiguration(base.getName(), base.getUniqueId(), new File(folder, base.getUniqueId() + ".yml"));
|
||||
config.setSaveHook(() -> {
|
||||
config.setRootHolder(UserConfigHolder.class, holder);
|
||||
});
|
||||
reloadConfig();
|
||||
|
||||
if (config.getUsername() == null) {
|
||||
@ -65,12 +61,15 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
|
||||
public final void reset() {
|
||||
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) {
|
||||
ess.getUserMap().removeUser(config.getUsername());
|
||||
final ModernUserMap users = (ModernUserMap) ess.getUsers();
|
||||
users.invalidate(config.getUuid());
|
||||
if (isNPC()) {
|
||||
final String uuid = UUID.nameUUIDFromBytes(("NPC:" + StringUtil.safeString(config.getUsername())).getBytes(Charsets.UTF_8)).toString();
|
||||
ess.getUserMap().removeUserUUID(uuid);
|
||||
final String name = ess.getSettings().isSafeUsermap() ? StringUtil.safeString(config.getUsername()) : config.getUsername();
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
@ -589,9 +580,13 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
}
|
||||
|
||||
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);
|
||||
config.save();
|
||||
ess.getUserMap().trackUUID(getConfigUUID(), lastAccountName, true);
|
||||
}
|
||||
|
||||
public boolean arePowerToolsEnabled() {
|
||||
@ -722,6 +717,17 @@ public abstract class UserData extends PlayerExtension implements IConf {
|
||||
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() {
|
||||
if (holder.shouting() == null) {
|
||||
holder.shouting(ess.getSettings().isShoutDefault());
|
||||
|
@ -1,383 +1,70 @@
|
||||
package com.earth2me.essentials;
|
||||
|
||||
import com.earth2me.essentials.api.UserDoesNotExistException;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
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 com.earth2me.essentials.userstorage.ModernUserMap;
|
||||
import org.bukkit.Bukkit;
|
||||
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.UUID;
|
||||
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 {
|
||||
private static boolean legacy = false;
|
||||
private static Method getLegacy;
|
||||
private final transient IEssentials ess;
|
||||
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}$");
|
||||
@Deprecated
|
||||
public class UserMap {
|
||||
private final transient ModernUserMap userMap;
|
||||
private final transient UUIDMap uuidMap;
|
||||
|
||||
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 IEssentials ess) {
|
||||
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 UserMap(final ModernUserMap userMap) {
|
||||
this.userMap = userMap;
|
||||
this.uuidMap = new UUIDMap();
|
||||
}
|
||||
|
||||
public User getUser(final String name) {
|
||||
final String sanitizedName = StringUtil.safeString(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;
|
||||
}
|
||||
return userMap.getUser(name);
|
||||
}
|
||||
|
||||
public User getUser(final UUID uuid) {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
return userMap.getUser(uuid);
|
||||
}
|
||||
|
||||
public void trackUUID(final UUID uuid, final String name, final boolean replace) {
|
||||
if (uuid != null) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// no-op
|
||||
}
|
||||
|
||||
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 {
|
||||
final UUID uuid = 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!");
|
||||
return userMap.load(UUID.fromString(stringUUID));
|
||||
}
|
||||
|
||||
public User load(final org.bukkit.OfflinePlayer player) throws UserDoesNotExistException {
|
||||
if (player == null) {
|
||||
throw new IllegalArgumentException("Player cannot be null!");
|
||||
}
|
||||
|
||||
final Player userPlayer;
|
||||
if (player instanceof Player) {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
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());
|
||||
userPlayer = (Player) player;
|
||||
} else {
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("OfflinePlayer usermap load saving user data for " + player);
|
||||
}
|
||||
|
||||
// this code makes me sad
|
||||
user.startTransaction();
|
||||
try {
|
||||
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();
|
||||
final com.earth2me.essentials.OfflinePlayer essPlayer = new com.earth2me.essentials.OfflinePlayer(player.getUniqueId(), Bukkit.getServer());
|
||||
essPlayer.setName(player.getName());
|
||||
userPlayer = essPlayer;
|
||||
}
|
||||
|
||||
trackUUID(player.getUniqueId(), user.getName(), false);
|
||||
final User user = userMap.getUser(userPlayer);
|
||||
if (user == null) {
|
||||
throw new UserDoesNotExistException("User not found");
|
||||
}
|
||||
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() {
|
||||
return Collections.unmodifiableSet(keys);
|
||||
return userMap.getAllUserUUIDs();
|
||||
}
|
||||
|
||||
public int getUniqueUsers() {
|
||||
return keys.size();
|
||||
return userMap.getUserCount();
|
||||
}
|
||||
|
||||
protected ConcurrentSkipListMap<String, UUID> getNames() {
|
||||
return names;
|
||||
}
|
||||
|
||||
protected ConcurrentSkipListMap<UUID, ArrayList<String>> getHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
public List<String> getUserHistory(final UUID uuid) {
|
||||
return history.get(uuid);
|
||||
return new ConcurrentSkipListMap<>(userMap.getNameCache());
|
||||
}
|
||||
|
||||
public UUIDMap getUUIDMap() {
|
||||
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.User;
|
||||
import com.earth2me.essentials.config.EssentialsUserConfiguration;
|
||||
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||
import com.earth2me.essentials.utils.NumberUtil;
|
||||
import com.earth2me.essentials.utils.StringUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
@ -41,9 +42,9 @@ public class Economy {
|
||||
ess = aEss;
|
||||
}
|
||||
|
||||
private static void createNPCFile(String name) {
|
||||
private static void createNPCFile(final String unsanitizedName) {
|
||||
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.mkdirs()) {
|
||||
throw new RuntimeException("Error while creating userdata directory!");
|
||||
@ -59,9 +60,12 @@ public class Economy {
|
||||
npcConfig.load();
|
||||
npcConfig.setProperty("npc", true);
|
||||
npcConfig.setProperty("last-account-name", name);
|
||||
npcConfig.setProperty("npc-name", unsanitizedName);
|
||||
npcConfig.setProperty("money", ess.getSettings().getStartingBalance());
|
||||
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) {
|
||||
|
@ -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 (ess.getUserMap().getUniqueUsers() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUserMap().getUniqueUsers()));
|
||||
if (ess.getUsers().getUserCount() > MINUSERS) {
|
||||
sender.sendMessage(tl("orderBalances", ess.getUsers().getUserCount()));
|
||||
}
|
||||
|
||||
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.EssentialsUpgrade;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.UserMap;
|
||||
import com.earth2me.essentials.economy.EconomyLayer;
|
||||
import com.earth2me.essentials.economy.EconomyLayers;
|
||||
import com.earth2me.essentials.userstorage.ModernUserMap;
|
||||
import com.earth2me.essentials.utils.CommandMapUtil;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
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.PasteUtil;
|
||||
import com.earth2me.essentials.utils.VersionUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonArray;
|
||||
@ -33,6 +32,7 @@ import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -45,12 +45,15 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.earth2me.essentials.I18n.tl;
|
||||
@ -147,11 +150,8 @@ public class Commandessentials extends EssentialsCommand {
|
||||
case "homes":
|
||||
runHomes(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "uuidconvert":
|
||||
runUUIDConvert(server, sender, commandLabel, args);
|
||||
break;
|
||||
case "uuidtest":
|
||||
runUUIDTest(server, sender, commandLabel, args);
|
||||
case "usermap":
|
||||
runUserMap(sender, args);
|
||||
break;
|
||||
|
||||
// "#EasterEgg"
|
||||
@ -473,12 +473,11 @@ public class Commandessentials extends EssentialsCommand {
|
||||
final long daysArg = Long.parseLong(args[1]);
|
||||
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 UserMap userMap = ess.getUserMap();
|
||||
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
final long currTime = System.currentTimeMillis();
|
||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
@ -523,13 +522,12 @@ public class Commandessentials extends EssentialsCommand {
|
||||
throw new Exception(HOMES_USAGE);
|
||||
}
|
||||
|
||||
final UserMap userMap = ess.getUserMap();
|
||||
switch (args[1]) {
|
||||
case "fix":
|
||||
sender.sendMessage(tl("fixingHomes"));
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
@ -553,8 +551,8 @@ public class Commandessentials extends EssentialsCommand {
|
||||
}
|
||||
sender.sendMessage(filterByWorld ? tl("deletingHomesWorld", args[2]) : tl("deletingHomes"));
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
@ -577,52 +575,77 @@ public class Commandessentials extends EssentialsCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Forces a rerun of userdata UUID conversion.
|
||||
private void runUUIDConvert(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
sender.sendMessage("Starting Essentials UUID userdata conversion; this may lag the server.");
|
||||
|
||||
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.
|
||||
private void runUUIDTest(final Server server, final CommandSource sender, final String commandLabel, final String[] args) throws Exception {
|
||||
if (args.length < 2) {
|
||||
throw new Exception("/<command> uuidtest <name>");
|
||||
// Gets information about cached users
|
||||
private void runUserMap(final CommandSource sender, final String[] args) {
|
||||
if (!sender.isAuthorized("essentials.usermap", ess)) {
|
||||
return;
|
||||
}
|
||||
final String name = args[1];
|
||||
sender.sendMessage("Looking up UUID for " + name);
|
||||
|
||||
UUID onlineUUID = null;
|
||||
final ModernUserMap userMap = (ModernUserMap) ess.getUsers();
|
||||
sender.sendMessage(tl("usermapSize", userMap.getCachedCount(), userMap.getUserCount(), ess.getSettings().getMaxUserCacheCount()));
|
||||
if (args.length > 1) {
|
||||
if (args[1].equals("full")) {
|
||||
for (final Map.Entry<String, UUID> entry : userMap.getNameCache().entrySet()) {
|
||||
sender.sendMessage(tl("usermapEntry", entry.getKey(), entry.getValue().toString()));
|
||||
}
|
||||
} else if (args[1].equals("purge")) {
|
||||
final boolean seppuku = args.length > 2 && args[2].equals("iknowwhatimdoing");
|
||||
|
||||
for (final Player player : ess.getOnlinePlayers()) {
|
||||
if (player.getName().equalsIgnoreCase(name)) {
|
||||
onlineUUID = player.getUniqueId();
|
||||
break;
|
||||
sender.sendMessage(tl("usermapPurge", String.valueOf(seppuku)));
|
||||
|
||||
final Set<UUID> uuids = new HashSet<>(ess.getUsers().getAllUserUUIDs());
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
final File userdataFolder = new File(ess.getDataFolder(), "userdata");
|
||||
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;
|
||||
}
|
||||
|
||||
if (seppuku && !backupFolder.mkdir()) {
|
||||
ess.getLogger().warning("Unable to create backup folder, aborting usermap purge.");
|
||||
return;
|
||||
}
|
||||
|
||||
int total = 0;
|
||||
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 essUUID = ess.getUserMap().getUser(name).getConfigUUID();
|
||||
|
||||
final org.bukkit.OfflinePlayer player = ess.getServer().getOfflinePlayer(name);
|
||||
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) {
|
||||
sender.sendMessage("Essentials config: " + essUUID.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.
|
||||
@ -750,7 +773,6 @@ public class Commandessentials extends EssentialsCommand {
|
||||
options.add("cleanup");
|
||||
options.add("homes");
|
||||
//options.add("uuidconvert");
|
||||
//options.add("uuidtest");
|
||||
//options.add("nya");
|
||||
//options.add("moo");
|
||||
return options;
|
||||
@ -763,7 +785,6 @@ public class Commandessentials extends EssentialsCommand {
|
||||
}
|
||||
break;
|
||||
case "reset":
|
||||
case "uuidtest":
|
||||
if (args.length == 2) {
|
||||
return getPlayers(server, sender);
|
||||
}
|
||||
@ -782,11 +803,6 @@ public class Commandessentials extends EssentialsCommand {
|
||||
return server.getWorlds().stream().map(World::getName).collect(Collectors.toList());
|
||||
}
|
||||
break;
|
||||
case "uuidconvert":
|
||||
if (args.length == 2) {
|
||||
return Lists.newArrayList("ignoreUFCache");
|
||||
}
|
||||
break;
|
||||
case "dump":
|
||||
final List<String> list = Lists.newArrayList("config", "kits", "log", "discord", "worth", "tpr", "spawns", "commands", "all");
|
||||
for (String arg : args) {
|
||||
|
@ -261,8 +261,8 @@ public class Commandmail extends EssentialsCommand {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (UUID userid : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(userid);
|
||||
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
if (user != null) {
|
||||
user.sendMail(messageRecipient, message, dateDiff);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.earth2me.essentials.commands;
|
||||
|
||||
import com.earth2me.essentials.CommandSource;
|
||||
import com.earth2me.essentials.User;
|
||||
import com.earth2me.essentials.UserMap;
|
||||
import com.earth2me.essentials.craftbukkit.BanLookup;
|
||||
import com.earth2me.essentials.utils.DateUtil;
|
||||
import com.earth2me.essentials.utils.FormatUtil;
|
||||
@ -63,7 +62,7 @@ public class Commandseen extends EssentialsCommand {
|
||||
ess.getScheduler().runTaskAsynchronously(ess, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final User userFromBukkit = ess.getUserMap().getUserFromBukkit(args[0]);
|
||||
final User userFromBukkit = ess.getUsers().getUser(args[0]);
|
||||
try {
|
||||
if (userFromBukkit != null) {
|
||||
showUserSeen(userFromBukkit);
|
||||
@ -103,8 +102,8 @@ public class Commandseen extends EssentialsCommand {
|
||||
user.setDisplayNick();
|
||||
sender.sendMessage(tl("seenOnline", user.getDisplayName(), DateUtil.formatDateDiff(user.getLastLogin())));
|
||||
|
||||
final List<String> history = ess.getUserMap().getUserHistory(user.getBase().getUniqueId());
|
||||
if (history != null && history.size() > 1) {
|
||||
final List<String> history = user.getPastUsernames();
|
||||
if (history != null && !history.isEmpty()) {
|
||||
sender.sendMessage(tl("seenAccounts", StringUtil.joinListSkip(", ", user.getName(), history)));
|
||||
}
|
||||
|
||||
@ -139,7 +138,7 @@ public class Commandseen extends EssentialsCommand {
|
||||
user.setDisplayNick();
|
||||
if (user.getLastLogout() > 0) {
|
||||
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) {
|
||||
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) {
|
||||
final UserMap userMap = ess.getUserMap();
|
||||
|
||||
sender.sendMessage(tl("runningPlayerMatch", display));
|
||||
|
||||
ess.runTaskAsynchronously(() -> {
|
||||
final List<String> matches = new ArrayList<>();
|
||||
for (final UUID u : userMap.getAllUniqueUsers()) {
|
||||
final User user = ess.getUserMap().getUser(u);
|
||||
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -32,9 +32,9 @@ public abstract class EssentialsLoopCommand extends EssentialsCommand {
|
||||
final User matchedUser = ess.getUser(uuid);
|
||||
userConsumer.accept(matchedUser);
|
||||
} else if (matchWildcards && searchTerm.contentEquals("**")) {
|
||||
for (final UUID sUser : ess.getUserMap().getAllUniqueUsers()) {
|
||||
final User matchedUser = ess.getUser(sUser);
|
||||
userConsumer.accept(matchedUser);
|
||||
for (final UUID u : ess.getUsers().getAllUserUUIDs()) {
|
||||
final User user = ess.getUsers().loadUncachedUser(u);
|
||||
userConsumer.accept(user);
|
||||
}
|
||||
} else if (matchWildcards && searchTerm.contentEquals("*")) {
|
||||
final boolean skipHidden = sender.isPlayer() && !ess.getUser(sender.getPlayer()).canInteractVanished();
|
||||
|
@ -103,6 +103,15 @@ public class EssentialsConfiguration {
|
||||
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() {
|
||||
return configFile;
|
||||
}
|
||||
@ -366,10 +375,10 @@ public class EssentialsConfiguration {
|
||||
} catch (final ParsingException e) {
|
||||
final File broken = new File(configFile.getAbsolutePath() + ".broken." + System.currentTimeMillis());
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
Essentials.getWrappedLogger().log(Level.SEVERE, e.getMessage(), e);
|
||||
} finally {
|
||||
@ -420,6 +429,10 @@ public class EssentialsConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTransaction() {
|
||||
return transaction.get();
|
||||
}
|
||||
|
||||
public void setSaveHook(Runnable saveHook) {
|
||||
this.saveHook = saveHook;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ public class EssentialsUserConfiguration extends EssentialsConfiguration {
|
||||
|
||||
private File getAltFile() {
|
||||
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
|
||||
|
@ -262,6 +262,16 @@ public class UserConfigHolder {
|
||||
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;
|
||||
|
||||
public boolean powerToolsEnabled() {
|
||||
@ -332,6 +342,20 @@ public class UserConfigHolder {
|
||||
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();
|
||||
|
||||
public Timestamps timestamps() {
|
||||
|
@ -11,6 +11,7 @@ import net.ess3.api.MaxMoneyException;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import net.milkbowl.vault.economy.EconomyResponse;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
@ -308,21 +309,27 @@ public class VaultEconomyProvider implements Economy {
|
||||
npcConfig.setProperty("last-account-name", player.getName());
|
||||
npcConfig.setProperty("money", ess.getSettings().getStartingBalance());
|
||||
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;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
ess.getLogger().info("Vault requested a player account creation for a v4 UUID: " + player);
|
||||
}
|
||||
ess.getUserMap().load(player);
|
||||
return true;
|
||||
} catch (UserDoesNotExistException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
if (ess.getSettings().isDebug()) {
|
||||
ess.getLogger().info("Vault requested a player account creation for a v4 UUID: " + player);
|
||||
}
|
||||
|
||||
final Player userPlayer;
|
||||
if (player instanceof Player) {
|
||||
userPlayer = (Player) player;
|
||||
} 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
|
||||
|
@ -70,11 +70,11 @@ public class LuckPermsHandler extends ModernVaultHandler {
|
||||
// If the player doesn't exist in the UserMap, just skip
|
||||
// Ess will cause performance problems for permissions checks if it attempts to
|
||||
// perform i/o to load the user data otherwise.
|
||||
if (!ess.getUserMap().userExists(target.getUniqueId())) {
|
||||
if (!ess.getUsers().getAllUserUUIDs().contains(target.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final User user = ess.getUser(target);
|
||||
final User user = ess.getUsers().loadUncachedUser(target.getUniqueId());
|
||||
for (Calculator calculator : this.calculators) {
|
||||
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);
|
||||
break;
|
||||
case UNIQUE:
|
||||
replacer = NumberFormat.getInstance().format(ess.getUserMap().getUniqueUsers());
|
||||
replacer = NumberFormat.getInstance().format(ess.getUsers().getUserCount());
|
||||
break;
|
||||
case WORLDS:
|
||||
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
|
||||
public static String safeString(final String string) {
|
||||
if (string == null) {
|
||||
return null;
|
||||
}
|
||||
return STRICTINVALIDCHARS.matcher(string.toLowerCase(Locale.ENGLISH)).replaceAll("_");
|
||||
}
|
||||
|
||||
|
@ -1415,6 +1415,9 @@ userIsAwaySelf=\u00a77You are now AFK.
|
||||
userIsAwaySelfWithMessage=\u00a77You are now AFK.
|
||||
userIsNotAwaySelf=\u00a77You are no longer AFK.
|
||||
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.
|
||||
usingTempFolderForTesting=Using temp folder for testing\:
|
||||
vanish=\u00a76Vanish for {0}\u00a76\: {1}
|
||||
|
@ -137,7 +137,7 @@ public class BukkitListener implements Listener {
|
||||
MessageUtil.sanitizeDiscordMarkdown(player.getDisplayName()),
|
||||
MessageUtil.sanitizeDiscordMarkdown(message),
|
||||
jda.getPlugin().getEss().getOnlinePlayers().size() - (join ? 0 : 1),
|
||||
jda.getPlugin().getEss().getUserMap().getUniqueUsers()),
|
||||
jda.getPlugin().getEss().getUsers().getUserCount()),
|
||||
player);
|
||||
}
|
||||
|
||||
|
@ -1341,7 +1341,7 @@ public class OfflinePlayer implements Player {
|
||||
return name;
|
||||
}
|
||||
|
||||
protected void setName(final String name) {
|
||||
public void setName(final String name) {
|
||||
this.name = base.getName();
|
||||
if (this.name == null) {
|
||||
this.name = name;
|
||||
|
Loading…
Reference in New Issue
Block a user