Merge pull request #564 from AuthMe-Team/master

DB Hotfix
This commit is contained in:
Gabriele C 2016-02-27 01:14:57 +01:00
commit 0e6d0c6d25
68 changed files with 1321 additions and 871 deletions

View File

@ -50,7 +50,7 @@ McStats: http://mcstats.org/plugin/AuthMe
#####Running Requirements: #####Running Requirements:
>- Java 1.7 (should work also with Java 1.8) >- Java 1.7 (should work also with Java 1.8)
>- Spigot or CraftBukkit (1.7.10 or 1.8.X) >- Spigot or CraftBukkit (1.7.10, 1.8.X or 1.9-pre1)
>- ProtocolLib (optional, required by the protectInventory feature) >- ProtocolLib (optional, required by the protectInventory feature)
<hr> <hr>

33
pom.xml
View File

@ -61,7 +61,7 @@
<javaVersion>1.7</javaVersion> <javaVersion>1.7</javaVersion>
<!-- Change Bukkit Version HERE! --> <!-- Change Bukkit Version HERE! -->
<bukkitVersion>1.8.8-R0.1-SNAPSHOT</bukkitVersion> <bukkitVersion>1.9-pre1-SNAPSHOT</bukkitVersion>
</properties> </properties>
<profiles> <profiles>
@ -305,10 +305,10 @@
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url> <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots</url>
</repository> </repository>
<!-- EssentialsX Repo --> <!-- Essentials Repo -->
<repository> <repository>
<id>ess-repo</id> <id>ess-repo</id>
<url>http://ci.drtshock.net/plugin/repository/everything</url> <url>http://repo.ess3.net/content/groups/essentials</url>
</repository> </repository>
<!-- CombatTagPlus Repo --> <!-- CombatTagPlus Repo -->
@ -376,6 +376,19 @@
<scope>compile</scope> <scope>compile</scope>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- JDBC drivers for datasource integration tests -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.8.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
<scope>test</scope>
</dependency>
<!-- Log4J Logger (required by the console filter) --> <!-- Log4J Logger (required by the console filter) -->
<dependency> <dependency>
@ -651,16 +664,20 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- EssentialsX plugin, http://www.spigotmc.org/resources/essentialsx.9089/ --> <!-- Essentials plugin -->
<dependency> <dependency>
<groupId>net.ess3</groupId> <groupId>net.ess3</groupId>
<artifactId>EssentialsX</artifactId> <artifactId>Essentials</artifactId>
<version>2.0.1-SNAPSHOT</version> <version>2.13-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.spigotmc</groupId> <groupId>org.bukkit</groupId>
<artifactId>spigot-api</artifactId> <artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
<optional>true</optional> <optional>true</optional>

View File

@ -64,7 +64,6 @@ import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -76,6 +75,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Date; import java.util.Date;
@ -104,15 +104,14 @@ public class AuthMe extends JavaPlugin {
// Private Instances // Private Instances
private static AuthMe plugin; private static AuthMe plugin;
private static Server server; private static Server server;
private Management management; /*
private CommandHandler commandHandler = null; * Maps and stuff
private PermissionsManager permsMan = null; * TODO: Clean up and Move into a manager
private NewSetting newSettings; */
private Messages messages; public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>();
private JsonCache playerBackup; public final ConcurrentHashMap<String, Integer> captcha = new ConcurrentHashMap<>();
private PasswordSecurity passwordSecurity; public final ConcurrentHashMap<String, String> cap = new ConcurrentHashMap<>();
private DataSource database; public final ConcurrentHashMap<String, String> realIp = new ConcurrentHashMap<>();
/* /*
* Public Instances * Public Instances
* TODO #432: Encapsulation * TODO #432: Encapsulation
@ -122,7 +121,6 @@ public class AuthMe extends JavaPlugin {
public DataManager dataManager; public DataManager dataManager;
public OtherAccounts otherAccounts; public OtherAccounts otherAccounts;
public Location essentialsSpawn; public Location essentialsSpawn;
/* /*
* Plugin Hooks * Plugin Hooks
* TODO: Move into modules * TODO: Move into modules
@ -133,15 +131,14 @@ public class AuthMe extends JavaPlugin {
public AuthMeInventoryPacketAdapter inventoryProtector; public AuthMeInventoryPacketAdapter inventoryProtector;
public AuthMeTabCompletePacketAdapter tabComplete; public AuthMeTabCompletePacketAdapter tabComplete;
public AuthMeTablistPacketAdapter tablistHider; public AuthMeTablistPacketAdapter tablistHider;
private Management management;
/* private CommandHandler commandHandler = null;
* Maps and stuff private PermissionsManager permsMan = null;
* TODO: Clean up and Move into a manager private NewSetting newSettings;
*/ private Messages messages;
public final ConcurrentHashMap<String, BukkitTask> sessions = new ConcurrentHashMap<>(); private JsonCache playerBackup;
public final ConcurrentHashMap<String, Integer> captcha = new ConcurrentHashMap<>(); private PasswordSecurity passwordSecurity;
public final ConcurrentHashMap<String, String> cap = new ConcurrentHashMap<>(); private DataSource database;
public final ConcurrentHashMap<String, String> realIp = new ConcurrentHashMap<>();
/** /**
* Get the plugin's instance. * Get the plugin's instance.
@ -494,11 +491,41 @@ public class AuthMe extends JavaPlugin {
if (newSettings != null) { if (newSettings != null) {
new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.STOP); new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.STOP);
} }
new Thread(new Runnable() {
@Override
public void run() {
List<Integer> pendingTasks = new ArrayList<>();
for (BukkitTask pendingTask : getServer().getScheduler().getPendingTasks()) {
if (pendingTask.getOwner().equals(plugin) && !pendingTask.isSync()) {
pendingTasks.add(pendingTask.getTaskId());
}
}
ConsoleLogger.info("Waiting for " + pendingTasks.size() + " tasks to finish");
int progress = 0;
for (int taskId : pendingTasks) {
int maxTries = 5;
while (getServer().getScheduler().isCurrentlyRunning(taskId)) {
if (maxTries <= 0) {
ConsoleLogger.info("Async task " + taskId + " times out after to many tries");
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
maxTries--;
}
progress++;
ConsoleLogger.info("Progress: " + progress + " / " + pendingTasks.size());
}
if (database != null) {
database.close();
}
}
}, "AuthMe-DataSource#close").start();
// Close the database // Close the database
if (database != null) {
database.close();
}
// Disabled correctly // Disabled correctly
ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!"); ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " disabled!");
@ -518,6 +545,7 @@ public class AuthMe extends JavaPlugin {
* Sets up the data source. * Sets up the data source.
* *
* @param settings The settings instance * @param settings The settings instance
*
* @see AuthMe#database * @see AuthMe#database
*/ */
public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException { public void setupDatabase(NewSetting settings) throws ClassNotFoundException, SQLException {
@ -653,6 +681,7 @@ public class AuthMe extends JavaPlugin {
ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it...");
Settings.protectInventoryBeforeLogInEnabled = false; Settings.protectInventoryBeforeLogInEnabled = false;
newSettings.setProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN, false); newSettings.setProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN, false);
newSettings.save();
} }
return; return;
} }
@ -664,13 +693,19 @@ public class AuthMe extends JavaPlugin {
inventoryProtector.unregister(); inventoryProtector.unregister();
inventoryProtector = null; inventoryProtector = null;
} }
if (tabComplete == null && newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN)) { if (newSettings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN) && tabComplete == null) {
tabComplete = new AuthMeTabCompletePacketAdapter(this); tabComplete = new AuthMeTabCompletePacketAdapter(this);
tabComplete.register(); tabComplete.register();
} else if (tabComplete != null) {
tabComplete.unregister();
tabComplete = null;
} }
if (tablistHider == null && newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN)) { if (newSettings.getProperty(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN) && tablistHider == null) {
tablistHider = new AuthMeTablistPacketAdapter(this); tablistHider = new AuthMeTablistPacketAdapter(this);
tablistHider.register(); tablistHider.register();
} else if (tablistHider != null) {
tablistHider.unregister();
tablistHider = null;
} }
} }
@ -737,61 +772,9 @@ public class AuthMe extends JavaPlugin {
} }
// Return the spawn location of a player // Return the spawn location of a player
@Deprecated
public Location getSpawnLocation(Player player) { public Location getSpawnLocation(Player player) {
World world = player.getWorld(); return Spawn.getInstance().getSpawnLocation(player);
String[] spawnPriority = Settings.spawnPriority.split(",");
Location spawnLoc = world.getSpawnLocation();
for (int i = spawnPriority.length - 1; i >= 0; i--) {
String s = spawnPriority[i];
if (s.equalsIgnoreCase("default") && getDefaultSpawn(world) != null)
spawnLoc = getDefaultSpawn(world);
if (s.equalsIgnoreCase("multiverse") && getMultiverseSpawn(world) != null)
spawnLoc = getMultiverseSpawn(world);
if (s.equalsIgnoreCase("essentials") && getEssentialsSpawn() != null)
spawnLoc = getEssentialsSpawn();
if (s.equalsIgnoreCase("authme") && getAuthMeSpawn(player) != null)
spawnLoc = getAuthMeSpawn(player);
}
if (spawnLoc == null) {
spawnLoc = world.getSpawnLocation();
}
return spawnLoc;
}
// Return the default spawn point of a world
private Location getDefaultSpawn(World world) {
return world.getSpawnLocation();
}
// Return the multiverse spawn point of a world
private Location getMultiverseSpawn(World world) {
if (multiverse != null && Settings.multiverse) {
try {
return multiverse.getMVWorldManager().getMVWorld(world).getSpawnLocation();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
// Return the essentials spawn point
private Location getEssentialsSpawn() {
if (essentialsSpawn != null) {
return essentialsSpawn;
}
return null;
}
// Return the AuthMe spawn point
private Location getAuthMeSpawn(Player player) {
if ((!database.isAuthAvailable(player.getName().toLowerCase()) || !player.hasPlayedBefore())
&& (Spawn.getInstance().getFirstSpawn() != null)) {
return Spawn.getInstance().getFirstSpawn();
} else if (Spawn.getInstance().getSpawn() != null) {
return Spawn.getInstance().getSpawn();
}
return player.getWorld().getSpawnLocation();
} }
private void scheduleRecallEmailTask() { private void scheduleRecallEmailTask() {

View File

@ -1,12 +1,11 @@
package fr.xephi.authme.cache.auth; package fr.xephi.authme.cache.auth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.bukkit.Location;
import static com.google.common.base.Objects.firstNonNull; import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import org.bukkit.Location;
import fr.xephi.authme.security.crypts.HashedPassword;
/** /**
*/ */
@ -85,20 +84,6 @@ public class PlayerAuth {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName); this(nickname, new HashedPassword(hash), -1, ip, lastLogin, 0, 0, 0, "world", email, realName);
} }
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param salt String
* @param ip String
* @param lastLogin long
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, String realName) {
this(nickname, new HashedPassword(hash, salt), -1, ip, lastLogin, 0, 0, 0, "world", "your@email.com", realName);
}
/** /**
* Constructor for PlayerAuth. * Constructor for PlayerAuth.
* *
@ -118,44 +103,6 @@ public class PlayerAuth {
this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName); this(nickname, new HashedPassword(hash), -1, ip, lastLogin, x, y, z, world, email, realName);
} }
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param salt String
* @param ip String
* @param lastLogin long
* @param x double
* @param y double
* @param z double
* @param world String
* @param email String
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, String ip, long lastLogin, double x, double y,
double z, String world, String email, String realName) {
this(nickname, new HashedPassword(hash, salt), -1, ip, lastLogin,
x, y, z, world, email, realName);
}
/**
* Constructor for PlayerAuth.
*
* @param nickname String
* @param hash String
* @param salt String
* @param groupId int
* @param ip String
* @param lastLogin long
* @param realName String
*/
public PlayerAuth(String nickname, String hash, String salt, int groupId, String ip,
long lastLogin, String realName) {
this(nickname, new HashedPassword(hash, salt), groupId, ip, lastLogin,
0, 0, 0, "world", "your@email.com", realName);
}
/** /**
* Constructor for PlayerAuth. * Constructor for PlayerAuth.
* *
@ -171,8 +118,8 @@ public class PlayerAuth {
* @param email String * @param email String
* @param realName String * @param realName String
*/ */
public PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin, private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) { double x, double y, double z, String world, String email, String realName) {
this.nickname = nickname.toLowerCase(); this.nickname = nickname.toLowerCase();
this.password = password; this.password = password;
this.ip = ip; this.ip = ip;

View File

@ -1,38 +0,0 @@
package fr.xephi.authme.cache.backup;
/**
*/
public class DataFileCache {
private final String group;
private final boolean operator;
/**
* Constructor for DataFileCache.
*
* @param group String
* @param operator boolean
*/
public DataFileCache(String group, boolean operator) {
this.group = group;
this.operator = operator;
}
/**
* Method getGroup.
*
* @return String
*/
public String getGroup() {
return group;
}
/**
* Method getOperator.
*
* @return boolean
*/
public boolean getOperator() {
return operator;
}
}

View File

@ -2,7 +2,14 @@ package fr.xephi.authme.cache.backup;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.gson.*; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -11,8 +18,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
/**
*/
public class JsonCache { public class JsonCache {
private final Gson gson; private final Gson gson;
@ -24,31 +29,19 @@ public class JsonCache {
ConsoleLogger.showError("Failed to create cache directory."); ConsoleLogger.showError("Failed to create cache directory.");
} }
gson = new GsonBuilder() gson = new GsonBuilder()
.registerTypeAdapter(DataFileCache.class, new PlayerDataSerializer()) .registerTypeAdapter(PlayerData.class, new PlayerDataSerializer())
.registerTypeAdapter(DataFileCache.class, new PlayerDataDeserializer()) .registerTypeAdapter(PlayerData.class, new PlayerDataDeserializer())
.setPrettyPrinting() .setPrettyPrinting()
.create(); .create();
} }
/** public void createCache(Player player, PlayerData playerData) {
* Method createCache.
*
* @param player Player
* @param playerData DataFileCache
*/
public void createCache(Player player, DataFileCache playerData) {
if (player == null) { if (player == null) {
return; return;
} }
String path; String name = player.getName().toLowerCase();
try { File file = new File(cacheDir, name + File.separator + "cache.json");
path = player.getUniqueId().toString();
} catch (Exception | Error e) {
path = player.getName().toLowerCase();
}
File file = new File(cacheDir, path + File.separator + "cache.json");
if (file.exists()) { if (file.exists()) {
return; return;
} }
@ -61,52 +54,29 @@ public class JsonCache {
Files.touch(file); Files.touch(file);
Files.write(data, file, Charsets.UTF_8); Files.write(data, file, Charsets.UTF_8);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); ConsoleLogger.writeStackTrace(e);
} }
} }
/** public PlayerData readCache(Player player) {
* Method readCache. String name = player.getName().toLowerCase();
* File file = new File(cacheDir, name + File.separator + "cache.json");
* @param player Player
*
* @return DataFileCache
*/
public DataFileCache readCache(Player player) {
String path;
try {
path = player.getUniqueId().toString();
} catch (Exception | Error e) {
path = player.getName().toLowerCase();
}
File file = new File(cacheDir, path + File.separator + "cache.json");
if (!file.exists()) { if (!file.exists()) {
return null; return null;
} }
try { try {
String str = Files.toString(file, Charsets.UTF_8); String str = Files.toString(file, Charsets.UTF_8);
return gson.fromJson(str, DataFileCache.class); return gson.fromJson(str, PlayerData.class);
} catch (Exception e) { } catch (IOException e) {
e.printStackTrace(); ConsoleLogger.writeStackTrace(e);
return null; return null;
} }
} }
/**
* Method removeCache.
*
* @param player Player
*/
public void removeCache(Player player) { public void removeCache(Player player) {
String path; String name = player.getName().toLowerCase();
try { File file = new File(cacheDir, name);
path = player.getUniqueId().toString();
} catch (Exception | Error e) {
path = player.getName().toLowerCase();
}
File file = new File(cacheDir, path);
if (file.exists()) { if (file.exists()) {
purgeDirectory(file); purgeDirectory(file);
if (!file.delete()) { if (!file.delete()) {
@ -115,75 +85,47 @@ public class JsonCache {
} }
} }
/**
* Method doesCacheExist.
*
* @param player Player
*
* @return boolean
*/
public boolean doesCacheExist(Player player) { public boolean doesCacheExist(Player player) {
String path; String name = player.getName().toLowerCase();
try { File file = new File(cacheDir, name + File.separator + "cache.json");
path = player.getUniqueId().toString();
} catch (Exception | Error e) {
path = player.getName().toLowerCase();
}
File file = new File(cacheDir, path + File.separator + "cache.json");
return file.exists(); return file.exists();
} }
/** private class PlayerDataDeserializer implements JsonDeserializer<PlayerData> {
*/
private static class PlayerDataDeserializer implements JsonDeserializer<DataFileCache> {
/**
* Method deserialize.
*
* @param jsonElement JsonElement
* @param type Type
* @param jsonDeserializationContext JsonDeserializationContext
*
* @return DataFileCache * @throws JsonParseException * @see com.google.gson.JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)
*/
@Override @Override
public DataFileCache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { public PlayerData deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext context) {
JsonObject jsonObject = jsonElement.getAsJsonObject(); JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject == null) { if (jsonObject == null) {
return null; return null;
} }
JsonElement e;
String group = null; String group = null;
boolean operator = false; boolean operator = false;
boolean fly = false;
JsonElement e;
if ((e = jsonObject.get("group")) != null) { if ((e = jsonObject.get("group")) != null) {
group = e.getAsString(); group = e.getAsString();
} }
if ((e = jsonObject.get("operator")) != null) { if ((e = jsonObject.get("operator")) != null) {
operator = e.getAsBoolean(); operator = e.getAsBoolean();
} }
if ((e = jsonObject.get("fly")) != null) {
fly = e.getAsBoolean();
}
return new DataFileCache(group, operator); return new PlayerData(group, operator, fly);
} }
} }
/** private class PlayerDataSerializer implements JsonSerializer<PlayerData> {
*/
private class PlayerDataSerializer implements JsonSerializer<DataFileCache> {
/**
* Method serialize.
*
* @param dataFileCache DataFileCache
* @param type Type
* @param jsonSerializationContext JsonSerializationContext
*
* @return JsonElement
*/
@Override @Override
public JsonElement serialize(DataFileCache dataFileCache, Type type, JsonSerializationContext jsonSerializationContext) { public JsonElement serialize(PlayerData playerData, Type type,
JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("group", dataFileCache.getGroup()); jsonObject.addProperty("group", playerData.getGroup());
jsonObject.addProperty("operator", dataFileCache.getOperator()); jsonObject.addProperty("operator", playerData.getOperator());
jsonObject.addProperty("fly", playerData.isFlyEnabled());
return jsonObject; return jsonObject;
} }
} }

View File

@ -0,0 +1,26 @@
package fr.xephi.authme.cache.backup;
public class PlayerData {
private final String group;
private final boolean operator;
private final boolean flyEnabled;
public PlayerData(String group, boolean operator, boolean flyEnabled) {
this.group = group;
this.operator = operator;
this.flyEnabled = flyEnabled;
}
public String getGroup() {
return group;
}
public boolean getOperator() {
return operator;
}
public boolean isFlyEnabled() {
return flyEnabled;
}
}

View File

@ -1,9 +1,8 @@
package fr.xephi.authme.cache.limbo; package fr.xephi.authme.cache.limbo;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.backup.DataFileCache;
import fr.xephi.authme.cache.backup.JsonCache; import fr.xephi.authme.cache.backup.JsonCache;
import fr.xephi.authme.cache.backup.PlayerData;
import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PermissionsManager;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -19,7 +18,7 @@ public class LimboCache {
private volatile static LimboCache singleton; private volatile static LimboCache singleton;
public final ConcurrentHashMap<String, LimboPlayer> cache; public final ConcurrentHashMap<String, LimboPlayer> cache;
public final AuthMe plugin; public final AuthMe plugin;
private final JsonCache playerData; private final JsonCache jsonCache;
/** /**
* Constructor for LimboCache. * Constructor for LimboCache.
@ -29,7 +28,7 @@ public class LimboCache {
private LimboCache(AuthMe plugin) { private LimboCache(AuthMe plugin) {
this.plugin = plugin; this.plugin = plugin;
this.cache = new ConcurrentHashMap<>(); this.cache = new ConcurrentHashMap<>();
this.playerData = new JsonCache(); this.jsonCache = new JsonCache();
} }
/** /**
@ -52,44 +51,28 @@ public class LimboCache {
public void addLimboPlayer(Player player) { public void addLimboPlayer(Player player) {
String name = player.getName().toLowerCase(); String name = player.getName().toLowerCase();
Location loc = player.getLocation(); Location loc = player.getLocation();
boolean operator = false; boolean operator = player.isOp();
boolean flyEnabled = player.getAllowFlight();
String playerGroup = ""; String playerGroup = "";
PermissionsManager permsMan = plugin.getPermissionsManager();
if (permsMan.hasGroupSupport()) {
playerGroup = permsMan.getPrimaryGroup(player);
}
// Get the permissions manager, and make sure it's valid if (jsonCache.doesCacheExist(player)) {
PermissionsManager permsMan = this.plugin.getPermissionsManager(); PlayerData cache = jsonCache.readCache(player);
if (permsMan == null)
ConsoleLogger.showError("Unable to access permissions manager!");
assert permsMan != null;
if (playerData.doesCacheExist(player)) {
DataFileCache cache = playerData.readCache(player);
if (cache != null) { if (cache != null) {
playerGroup = cache.getGroup(); playerGroup = cache.getGroup();
operator = cache.getOperator(); operator = cache.getOperator();
flyEnabled = cache.isFlyEnabled();
} }
} else {
operator = player.isOp();
// Check whether groups are supported
if (permsMan.hasGroupSupport())
playerGroup = permsMan.getPrimaryGroup(player);
} }
if (player.isDead()) { if (player.isDead()) {
loc = plugin.getSpawnLocation(player); loc = plugin.getSpawnLocation(player);
} }
cache.put(name, new LimboPlayer(name, loc, operator, playerGroup)); cache.put(name, new LimboPlayer(name, loc, operator, playerGroup, flyEnabled));
}
/**
* Method addLimboPlayer.
*
* @param player Player
* @param group String
*/
public void addLimboPlayer(Player player, String group) {
cache.put(player.getName().toLowerCase(), new LimboPlayer(player.getName().toLowerCase(), group));
} }
/** /**
@ -99,7 +82,11 @@ public class LimboCache {
*/ */
public void deleteLimboPlayer(String name) { public void deleteLimboPlayer(String name) {
checkNotNull(name); checkNotNull(name);
cache.remove(name.toLowerCase()); name = name.toLowerCase();
if (cache.containsKey(name)) {
cache.get(name).clearTask();
cache.remove(name);
}
} }
/** /**

View File

@ -8,37 +8,20 @@ import org.bukkit.scheduler.BukkitTask;
public class LimboPlayer { public class LimboPlayer {
private final String name; private final String name;
private final boolean fly;
private Location loc = null; private Location loc = null;
private BukkitTask timeoutTaskId = null; private BukkitTask timeoutTaskId = null;
private BukkitTask messageTaskId = null; private BukkitTask messageTaskId = null;
private boolean operator = false; private boolean operator = false;
private String group = ""; private String group;
/** public LimboPlayer(String name, Location loc, boolean operator,
* Constructor for LimboPlayer. String group, boolean fly) {
*
* @param name String
* @param loc Location
* @param operator boolean
* @param group String
*/
public LimboPlayer(String name, Location loc,
boolean operator, String group) {
this.name = name; this.name = name;
this.loc = loc; this.loc = loc;
this.operator = operator; this.operator = operator;
this.group = group; this.group = group;
} this.fly = fly;
/**
* Constructor for LimboPlayer.
*
* @param name String
* @param group String
*/
public LimboPlayer(String name, String group) {
this.name = name;
this.group = group;
} }
/** /**
@ -77,11 +60,10 @@ public class LimboPlayer {
return group; return group;
} }
/** public boolean isFly() {
* Method getTimeoutTaskId. return fly;
* }
* @return BukkitTask
*/
public BukkitTask getTimeoutTaskId() { public BukkitTask getTimeoutTaskId() {
return timeoutTaskId; return timeoutTaskId;
} }
@ -121,7 +103,6 @@ public class LimboPlayer {
/** /**
* Method clearTask. * Method clearTask.
*
*/ */
public void clearTask() { public void clearTask() {
if (messageTaskId != null) { if (messageTaskId != null) {

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.CommandService;
import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.command.ExecutableCommand;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.settings.Spawn;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List; import java.util.List;
@ -20,6 +21,7 @@ public class ReloadCommand implements ExecutableCommand {
try { try {
commandService.getSettings().reload(); commandService.getSettings().reload();
commandService.reloadMessages(commandService.getSettings().getMessagesFile()); commandService.reloadMessages(commandService.getSettings().getMessagesFile());
Spawn.reload();
// TODO #432: We should not reload only certain plugin entities but actually reinitialize all elements, // TODO #432: We should not reload only certain plugin entities but actually reinitialize all elements,
// i.e. here in the future we might not have setupDatabase() but Authme.onEnable(), maybe after // i.e. here in the future we might not have setupDatabase() but Authme.onEnable(), maybe after
// a call to some destructor method // a call to some destructor method

View File

@ -1,14 +1,5 @@
package fr.xephi.authme.command.executable.authme; package fr.xephi.authme.command.executable.authme;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
@ -20,6 +11,14 @@ import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.MessageTask;
import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.task.TimeoutTask;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import java.util.List;
/** /**
* Admin command to unregister a player. * Admin command to unregister a player.
@ -55,19 +54,20 @@ public class UnregisterAdminCommand implements ExecutableCommand {
if (target != null && target.isOnline()) { if (target != null && target.isOnline()) {
Utils.teleportToSpawn(target); Utils.teleportToSpawn(target);
LimboCache.getInstance().addLimboPlayer(target); LimboCache.getInstance().addLimboPlayer(target);
int delay = Settings.getRegistrationTimeout * 20; int timeOut = Settings.getRegistrationTimeout * 20;
int interval = Settings.getWarnMessageInterval; int interval = Settings.getWarnMessageInterval;
BukkitScheduler scheduler = sender.getServer().getScheduler(); BukkitScheduler scheduler = sender.getServer().getScheduler();
if (delay != 0) { if (timeOut != 0) {
BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), delay); BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, playerNameLowerCase, target), timeOut);
LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setTimeoutTaskId(id); LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setTimeoutTaskId(id);
} }
LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTaskId( LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTaskId(
scheduler.runTaskAsynchronously(plugin, scheduler.runTask(
new MessageTask(plugin, playerNameLowerCase, commandService.retrieveMessage(MessageKey.REGISTER_MESSAGE), interval))); plugin, new MessageTask(plugin, playerNameLowerCase, MessageKey.REGISTER_MESSAGE, interval)
)
);
if (Settings.applyBlindEffect) { if (Settings.applyBlindEffect) {
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2));
Settings.getRegistrationTimeout * 20, 2));
} }
commandService.send(target, MessageKey.UNREGISTERED_SUCCESS); commandService.send(target, MessageKey.UNREGISTERED_SUCCESS);
} }

View File

@ -4,14 +4,19 @@ import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.cache.RemovalNotification; import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@ -20,6 +25,7 @@ public class CacheDataSource implements DataSource {
private final DataSource source; private final DataSource source;
private final LoadingCache<String, Optional<PlayerAuth>> cachedAuths; private final LoadingCache<String, Optional<PlayerAuth>> cachedAuths;
private final ListeningExecutorService executorService;
/** /**
* Constructor for CacheDataSource. * Constructor for CacheDataSource.
@ -27,25 +33,35 @@ public class CacheDataSource implements DataSource {
* @param src DataSource * @param src DataSource
*/ */
public CacheDataSource(DataSource src) { public CacheDataSource(DataSource src) {
this.source = src; source = src;
this.cachedAuths = CacheBuilder.newBuilder() executorService = MoreExecutors.listeningDecorator(
.expireAfterWrite(8, TimeUnit.MINUTES) Executors.newCachedThreadPool(new ThreadFactoryBuilder()
.removalListener(new RemovalListener<String, Optional<PlayerAuth>>() { .setDaemon(true)
.setNameFormat("AuthMe-CacheLoader")
.build())
);
cachedAuths = CacheBuilder.newBuilder()
.refreshAfterWrite(8, TimeUnit.MINUTES)
.build(new CacheLoader<String, Optional<PlayerAuth>>() {
@Override @Override
public void onRemoval(RemovalNotification<String, Optional<PlayerAuth>> removalNotification) { public Optional<PlayerAuth> load(String key) {
String name = removalNotification.getKey(); return Optional.fromNullable(source.getAuth(key));
if (PlayerCache.getInstance().isAuthenticated(name)) {
cachedAuths.getUnchecked(name);
}
} }
})
.build( @Override
new CacheLoader<String, Optional<PlayerAuth>>() { public ListenableFuture<Optional<PlayerAuth>> reload(final String key, Optional<PlayerAuth> oldValue) {
@Override return executorService.submit(new Callable<Optional<PlayerAuth>>() {
public Optional<PlayerAuth> load(String key) { @Override
return Optional.fromNullable(source.getAuth(key)); public Optional<PlayerAuth> call() {
} return load(key);
}); }
});
}
});
}
public LoadingCache<String, Optional<PlayerAuth>> getCachedAuths() {
return cachedAuths;
} }
@Override @Override
@ -137,6 +153,13 @@ public class CacheDataSource implements DataSource {
@Override @Override
public synchronized void close() { public synchronized void close() {
source.close(); source.close();
cachedAuths.invalidateAll();
executorService.shutdown();
try {
executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
ConsoleLogger.writeStackTrace(e);
}
} }
@Override @Override
@ -160,8 +183,8 @@ public class CacheDataSource implements DataSource {
} }
@Override @Override
public synchronized List<String> getAllAuthsByEmail(final String email) { public synchronized int countAuthsByEmail(final String email) {
return source.getAllAuthsByEmail(email); return source.countAuthsByEmail(email);
} }
@Override @Override
@ -201,12 +224,6 @@ public class CacheDataSource implements DataSource {
return source.getAccountsRegistered(); return source.getAccountsRegistered();
} }
@Override
public void updateName(final String oldOne, final String newOne) { // unused method
source.updateName(oldOne, newOne);
cachedAuths.invalidate(oldOne);
}
@Override @Override
public boolean updateRealName(String user, String realName) { public boolean updateRealName(String user, String realName) {
boolean result = source.updateRealName(user, realName); boolean result = source.updateRealName(user, realName);

View File

@ -101,12 +101,12 @@ public interface DataSource {
List<String> getAllAuthsByIp(String ip); List<String> getAllAuthsByIp(String ip);
/** /**
* Return all usernames associated with the given email address. * Return the number of accounts associated with the given email address.
* *
* @param email The email address to look up * @param email The email address to look up
* @return Users using the given email address * @return Number of accounts using the given email address
*/ */
List<String> getAllAuthsByEmail(String email); int countAuthsByEmail(String email);
/** /**
* Update the email of the PlayerAuth in the data source. * Update the email of the PlayerAuth in the data source.
@ -169,14 +169,6 @@ public interface DataSource {
*/ */
int getAccountsRegistered(); int getAccountsRegistered();
/**
* Method updateName.
*
* @param oldOne String
* @param newOne String
*/
void updateName(String oldOne, String newOne);
boolean updateRealName(String user, String realName); boolean updateRealName(String user, String realName);
boolean updateIp(String user, String ip); boolean updateIp(String user, String ip);

View File

@ -487,25 +487,21 @@ public class FlatFile implements DataSource {
} }
@Override @Override
public List<String> getAllAuthsByEmail(String email) { public int countAuthsByEmail(String email) {
BufferedReader br = null; BufferedReader br = null;
List<String> countEmail = new ArrayList<>(); int countEmail = 0;
try { try {
br = new BufferedReader(new FileReader(source)); br = new BufferedReader(new FileReader(source));
String line; String line;
while ((line = br.readLine()) != null) { while ((line = br.readLine()) != null) {
String[] args = line.split(":"); String[] args = line.split(":");
if (args.length > 8 && args[8].equals(email)) { if (args.length > 8 && args[8].equals(email)) {
countEmail.add(args[0]); ++countEmail;
} }
} }
return countEmail; return countEmail;
} catch (FileNotFoundException ex) {
ConsoleLogger.showError(ex.getMessage());
return new ArrayList<>();
} catch (IOException ex) { } catch (IOException ex) {
ConsoleLogger.showError(ex.getMessage()); ConsoleLogger.showError(ex.getMessage());
return new ArrayList<>();
} finally { } finally {
if (br != null) { if (br != null) {
try { try {
@ -514,6 +510,7 @@ public class FlatFile implements DataSource {
} }
} }
} }
return 0;
} }
@Override @Override
@ -602,14 +599,6 @@ public class FlatFile implements DataSource {
return result; return result;
} }
@Override
public void updateName(String oldOne, String newOne) {
PlayerAuth auth = this.getAuth(oldOne);
auth.setNickname(newOne);
this.saveAuth(auth);
this.removeAuth(oldOne);
}
@Override @Override
public boolean updateRealName(String user, String realName) { public boolean updateRealName(String user, String realName) {
return false; return false;

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException; import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
@ -22,7 +23,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -82,6 +82,20 @@ public class MySQL implements DataSource {
} }
} }
@VisibleForTesting
MySQL(NewSetting settings, HikariDataSource hikariDataSource) {
this.host = settings.getProperty(DatabaseSettings.MYSQL_HOST);
this.port = settings.getProperty(DatabaseSettings.MYSQL_PORT);
this.username = settings.getProperty(DatabaseSettings.MYSQL_USERNAME);
this.password = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD);
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.columnOthers = settings.getProperty(HooksSettings.MYSQL_OTHER_USERNAME_COLS);
this.col = new Columns(settings);
this.hashAlgorithm = settings.getProperty(SecuritySettings.PASSWORD_HASH);
ds = hikariDataSource;
}
private synchronized void setConnectionArguments() throws RuntimeException { private synchronized void setConnectionArguments() throws RuntimeException {
ds = new HikariDataSource(); ds = new HikariDataSource();
ds.setPoolName("AuthMeMYSQLPool"); ds.setPoolName("AuthMeMYSQLPool");
@ -131,7 +145,7 @@ public class MySQL implements DataSource {
+ col.REAL_NAME + " VARCHAR(255) NOT NULL," + col.REAL_NAME + " VARCHAR(255) NOT NULL,"
+ col.PASSWORD + " VARCHAR(255) NOT NULL," + col.PASSWORD + " VARCHAR(255) NOT NULL,"
+ col.IP + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1'," + col.IP + " VARCHAR(40) NOT NULL DEFAULT '127.0.0.1',"
+ col.LAST_LOGIN + " TIMESTAMP NOT NULL DEFAULT current_timestamp," + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0,"
+ col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_X + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Y + " DOUBLE NOT NULL DEFAULT '0.0',"
+ col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0'," + col.LASTLOC_Z + " DOUBLE NOT NULL DEFAULT '0.0',"
@ -182,9 +196,9 @@ public class MySQL implements DataSource {
rs = md.getColumns(null, null, tableName, col.LAST_LOGIN); rs = md.getColumns(null, null, tableName, col.LAST_LOGIN);
if (!rs.next()) { if (!rs.next()) {
st.executeUpdate("ALTER TABLE " + tableName st.executeUpdate("ALTER TABLE " + tableName
+ " ADD COLUMN " + col.LAST_LOGIN + " TIMESTAMP NOT NULL DEFAULT current_timestamp;"); + " ADD COLUMN " + col.LAST_LOGIN + " BIGINT NOT NULL DEFAULT 0;");
} else { } else {
migrateLastLoginColumnToTimestamp(con, rs); migrateLastLoginColumnToBigInt(con, rs);
} }
rs.close(); rs.close();
@ -234,66 +248,72 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean isAuthAvailable(String user) { public synchronized boolean isAuthAvailable(String user) {
try (Connection con = getConnection()) { String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.NAME + "=?;"; ResultSet rs = null;
PreparedStatement pst = con.prepareStatement(sql); try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase()); pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery(); rs = pst.executeQuery();
return rs.next(); return rs.next();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} finally {
close(rs);
} }
return false; return false;
} }
@Override @Override
public HashedPassword getPassword(String user) { public HashedPassword getPassword(String user) {
try (Connection con = getConnection()) { String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName
String sql = "SELECT " + col.PASSWORD + "," + col.SALT + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
+ " WHERE " + col.NAME + "=?;"; ResultSet rs = null;
PreparedStatement pst = con.prepareStatement(sql); try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, user.toLowerCase()); pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery(); rs = pst.executeQuery();
if (rs.next()) { if (rs.next()) {
return new HashedPassword(rs.getString(col.PASSWORD), return new HashedPassword(rs.getString(col.PASSWORD),
!col.SALT.isEmpty() ? rs.getString(col.SALT) : null); !col.SALT.isEmpty() ? rs.getString(col.SALT) : null);
} }
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} finally {
close(rs);
} }
return null; return null;
} }
@Override @Override
public synchronized PlayerAuth getAuth(String user) { public synchronized PlayerAuth getAuth(String user) {
PlayerAuth pAuth; String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection()) { PlayerAuth auth;
String sql = "SELECT * FROM " + tableName + " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, user.toLowerCase()); pst.setString(1, user.toLowerCase());
ResultSet rs = pst.executeQuery(); int id;
if (!rs.next()) { try (ResultSet rs = pst.executeQuery()) {
return null; if (!rs.next()) {
return null;
}
id = rs.getInt(col.ID);
auth = buildAuthFromResultSet(rs);
} }
int id = rs.getInt(col.ID);
pAuth = buildAuthFromResultSet(rs);
rs.close();
pst.close();
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;"); try (PreparedStatement pst2 = con.prepareStatement(
pst.setInt(1, id); "SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) {
rs = pst.executeQuery(); pst2.setInt(1, id);
if (rs.next()) { try (ResultSet rs = pst2.executeQuery()) {
Blob blob = rs.getBlob("data"); if (rs.next()) {
byte[] bytes = blob.getBytes(1, (int) blob.length()); Blob blob = rs.getBlob("data");
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); byte[] bytes = blob.getBytes(1, (int) blob.length());
auth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
}
} }
} }
return auth;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
return null;
} }
return pAuth; return null;
} }
@Override @Override
@ -314,7 +334,7 @@ public class MySQL implements DataSource {
pst.setString(1, auth.getNickname()); pst.setString(1, auth.getNickname());
pst.setString(2, auth.getPassword().getHash()); pst.setString(2, auth.getPassword().getHash());
pst.setString(3, auth.getIp()); pst.setString(3, auth.getIp());
pst.setTimestamp(4, new Timestamp(auth.getLastLogin())); pst.setLong(4, auth.getLastLogin());
pst.setString(5, auth.getRealName()); pst.setString(5, auth.getRealName());
pst.setString(6, auth.getEmail()); pst.setString(6, auth.getEmail());
if (useSalt) { if (useSalt) {
@ -566,7 +586,7 @@ public class MySQL implements DataSource {
+ col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; + col.IP + "=?, " + col.LAST_LOGIN + "=?, " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
pst.setString(1, auth.getIp()); pst.setString(1, auth.getIp());
pst.setTimestamp(2, new Timestamp(auth.getLastLogin())); pst.setLong(2, auth.getLastLogin());
pst.setString(3, auth.getRealName()); pst.setString(3, auth.getRealName());
pst.setString(4, auth.getNickname()); pst.setString(4, auth.getNickname());
pst.executeUpdate(); pst.executeUpdate();
@ -580,20 +600,19 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized List<String> autoPurgeDatabase(long until) { public synchronized List<String> autoPurgeDatabase(long until) {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
try (Connection con = getConnection()) { String select = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; String delete = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;";
PreparedStatement st = con.prepareStatement(sql); try (Connection con = getConnection();
st.setLong(1, until); PreparedStatement selectPst = con.prepareStatement(select);
ResultSet rs = st.executeQuery(); PreparedStatement deletePst = con.prepareStatement(delete)) {
while (rs.next()) { selectPst.setLong(1, until);
list.add(rs.getString(col.NAME)); try (ResultSet rs = selectPst.executeQuery()) {
while (rs.next()) {
list.add(rs.getString(col.NAME));
}
} }
rs.close(); deletePst.setLong(1, until);
sql = "DELETE FROM " + tableName + " WHERE " + col.LAST_LOGIN + "<?;"; deletePst.executeUpdate();
st = con.prepareStatement(sql);
st.setLong(1, until);
st.executeUpdate();
st.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -634,18 +653,16 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean updateQuitLoc(PlayerAuth auth) { public synchronized boolean updateQuitLoc(PlayerAuth auth) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName
String sql = "UPDATE " + tableName + " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?"
+ " SET " + col.LASTLOC_X + " =?, " + col.LASTLOC_Y + "=?, " + col.LASTLOC_Z + "=?, " + col.LASTLOC_WORLD + "=?" + " WHERE " + col.NAME + "=?;";
+ " WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setDouble(1, auth.getQuitLocX()); pst.setDouble(1, auth.getQuitLocX());
pst.setDouble(2, auth.getQuitLocY()); pst.setDouble(2, auth.getQuitLocY());
pst.setDouble(3, auth.getQuitLocZ()); pst.setDouble(3, auth.getQuitLocZ());
pst.setString(4, auth.getWorld()); pst.setString(4, auth.getWorld());
pst.setString(5, auth.getNickname()); pst.setString(5, auth.getNickname());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
return true; return true;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
@ -655,13 +672,11 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean updateEmail(PlayerAuth auth) { public synchronized boolean updateEmail(PlayerAuth auth) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.EMAIL + " =? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, auth.getEmail()); pst.setString(1, auth.getEmail());
pst.setString(2, auth.getNickname()); pst.setString(2, auth.getNickname());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
return true; return true;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
@ -690,16 +705,14 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized List<String> getAllAuthsByIp(String ip) { public synchronized List<String> getAllAuthsByIp(String ip) {
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>();
try (Connection con = getConnection()) { String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;";
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.IP + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, ip); pst.setString(1, ip);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) { while (rs.next()) {
result.add(rs.getString(col.NAME)); result.add(rs.getString(col.NAME));
}
} }
rs.close();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -707,33 +720,29 @@ public class MySQL implements DataSource {
} }
@Override @Override
public synchronized List<String> getAllAuthsByEmail(String email) { public synchronized int countAuthsByEmail(String email) {
List<String> countEmail = new ArrayList<>(); String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
try (Connection con = getConnection()) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
String sql = "SELECT " + col.NAME + " FROM " + tableName + " WHERE " + col.EMAIL + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, email); pst.setString(1, email);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) { if (rs.next()) {
countEmail.add(rs.getString(col.NAME)); return rs.getInt(1);
}
} }
rs.close();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
return countEmail; return 0;
} }
@Override @Override
public synchronized void purgeBanned(List<String> banned) { public synchronized void purgeBanned(List<String> banned) {
try (Connection con = getConnection()) { String sql = "DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement("DELETE FROM " + tableName + " WHERE " + col.NAME + "=?;"); try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
for (String name : banned) { for (String name : banned) {
pst.setString(1, name); pst.setString(1, name);
pst.executeUpdate(); pst.executeUpdate();
} }
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -746,28 +755,25 @@ public class MySQL implements DataSource {
@Override @Override
public boolean isLogged(String user) { public boolean isLogged(String user) {
boolean isLogged = false; String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
try (Connection con = getConnection()) { try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
String sql = "SELECT " + col.IS_LOGGED + " FROM " + tableName + " WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, user); pst.setString(1, user);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
isLogged = rs.next() && (rs.getInt(col.IS_LOGGED) == 1); return rs.next() && (rs.getInt(col.IS_LOGGED) == 1);
}
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
return isLogged; return false;
} }
@Override @Override
public void setLogged(String user) { public void setLogged(String user) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 1); pst.setInt(1, 1);
pst.setString(2, user.toLowerCase()); pst.setString(2, user.toLowerCase());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -775,13 +781,11 @@ public class MySQL implements DataSource {
@Override @Override
public void setUnlogged(String user) { public void setUnlogged(String user) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 0); pst.setInt(1, 0);
pst.setString(2, user.toLowerCase()); pst.setString(2, user.toLowerCase());
pst.executeUpdate(); pst.executeUpdate();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -789,13 +793,11 @@ public class MySQL implements DataSource {
@Override @Override
public void purgeLogged() { public void purgeLogged() {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IS_LOGGED + "=? WHERE " + col.IS_LOGGED + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setInt(1, 0); pst.setInt(1, 0);
pst.setInt(2, 1); pst.setInt(2, 1);
pst.executeUpdate(); pst.executeUpdate();
pst.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -804,38 +806,23 @@ public class MySQL implements DataSource {
@Override @Override
public int getAccountsRegistered() { public int getAccountsRegistered() {
int result = 0; int result = 0;
try (Connection con = getConnection()) { String sql = "SELECT COUNT(*) FROM " + tableName;
Statement st = con.createStatement(); try (Connection con = getConnection();
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM " + tableName); Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql)) {
if (rs.next()) { if (rs.next()) {
result = rs.getInt(1); result = rs.getInt(1);
} }
rs.close();
st.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
return result; return result;
} }
@Override
public void updateName(String oldOne, String newOne) {
try (Connection con = getConnection()) {
String sql = "UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;";
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, newOne);
pst.setString(2, oldOne);
pst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
}
}
@Override @Override
public boolean updateRealName(String user, String realName) { public boolean updateRealName(String user, String realName) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, realName); pst.setString(1, realName);
pst.setString(2, user); pst.setString(2, user);
pst.executeUpdate(); pst.executeUpdate();
@ -848,9 +835,8 @@ public class MySQL implements DataSource {
@Override @Override
public boolean updateIp(String user, String ip) { public boolean updateIp(String user, String ip) {
try (Connection con = getConnection()) { String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;";
String sql = "UPDATE " + tableName + " SET " + col.IP + "=? WHERE " + col.NAME + "=?;"; try (Connection con = getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, ip); pst.setString(1, ip);
pst.setString(2, user); pst.setString(2, user);
pst.executeUpdate(); pst.executeUpdate();
@ -867,23 +853,23 @@ public class MySQL implements DataSource {
try (Connection con = getConnection()) { try (Connection con = getConnection()) {
Statement st = con.createStatement(); Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM " + tableName); ResultSet rs = st.executeQuery("SELECT * FROM " + tableName);
PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;");
while (rs.next()) { while (rs.next()) {
PlayerAuth pAuth = buildAuthFromResultSet(rs); PlayerAuth pAuth = buildAuthFromResultSet(rs);
if (hashAlgorithm == HashAlgorithm.XFBCRYPT) { if (hashAlgorithm == HashAlgorithm.XFBCRYPT) {
int id = rs.getInt(col.ID); try (PreparedStatement pst = con.prepareStatement("SELECT data FROM xf_user_authenticate WHERE " + col.ID + "=?;")) {
pst.setInt(1, id); int id = rs.getInt(col.ID);
ResultSet rs2 = pst.executeQuery(); pst.setInt(1, id);
if (rs2.next()) { ResultSet rs2 = pst.executeQuery();
Blob blob = rs2.getBlob("data"); if (rs2.next()) {
byte[] bytes = blob.getBytes(1, (int) blob.length()); Blob blob = rs2.getBlob("data");
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes))); byte[] bytes = blob.getBytes(1, (int) blob.length());
pAuth.setPassword(new HashedPassword(XFBCRYPT.getHashFromBlob(bytes)));
}
rs2.close();
} }
rs2.close();
} }
auths.add(pAuth); auths.add(pAuth);
} }
pst.close();
rs.close(); rs.close();
st.close(); st.close();
} catch (SQLException ex) { } catch (SQLException ex) {
@ -922,12 +908,12 @@ public class MySQL implements DataSource {
@Override @Override
public synchronized boolean isEmailStored(String email) { public synchronized boolean isEmailStored(String email) {
String sql = "SELECT 1 FROM " + tableName + " WHERE " + col.EMAIL + " = ?"; String sql = "SELECT 1 FROM " + tableName + " WHERE UPPER(" + col.EMAIL + ") = UPPER(?)";
try (Connection con = ds.getConnection()) { try (Connection con = ds.getConnection(); PreparedStatement pst = con.prepareStatement(sql)) {
PreparedStatement pst = con.prepareStatement(sql);
pst.setString(1, email); pst.setString(1, email);
ResultSet rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
return rs.next(); return rs.next();
}
} catch (SQLException e) { } catch (SQLException e) {
logSqlException(e); logSqlException(e);
} }
@ -941,7 +927,7 @@ public class MySQL implements DataSource {
.name(row.getString(col.NAME)) .name(row.getString(col.NAME))
.realName(row.getString(col.REAL_NAME)) .realName(row.getString(col.REAL_NAME))
.password(row.getString(col.PASSWORD), salt) .password(row.getString(col.PASSWORD), salt)
.lastLogin(safeGetTimestamp(row)) .lastLogin(row.getLong(col.LAST_LOGIN))
.ip(row.getString(col.IP)) .ip(row.getString(col.IP))
.locWorld(row.getString(col.LASTLOC_WORLD)) .locWorld(row.getString(col.LASTLOC_WORLD))
.locX(row.getDouble(col.LASTLOC_X)) .locX(row.getDouble(col.LASTLOC_X))
@ -953,24 +939,15 @@ public class MySQL implements DataSource {
} }
/** /**
* Retrieve the last login timestamp in a safe way. * Check if the lastlogin column is of type timestamp and, if so, revert it to the bigint format.
* *
* @param row The ResultSet to read * @param con Connection to the database
* @return The timestamp (as number of milliseconds since 1970-01-01 00:00:00 GMT) * @param rs ResultSet containing meta data for the lastlogin column
*/ */
private long safeGetTimestamp(ResultSet row) { private void migrateLastLoginColumnToBigInt(Connection con, ResultSet rs) throws SQLException {
try {
return row.getTimestamp(col.LAST_LOGIN).getTime();
} catch (SQLException e) {
ConsoleLogger.logException("Could not get timestamp from resultSet. Defaulting to current time", e);
}
return System.currentTimeMillis();
}
private void migrateLastLoginColumnToTimestamp(Connection con, ResultSet rs) throws SQLException {
final int columnType = rs.getInt("DATA_TYPE"); final int columnType = rs.getInt("DATA_TYPE");
if (columnType == Types.BIGINT) { if (columnType == Types.TIMESTAMP) {
ConsoleLogger.info("Migrating lastlogin column from bigint to timestamp"); ConsoleLogger.info("Migrating lastlogin column from timestamp to bigint");
final String lastLoginOld = col.LAST_LOGIN + "_old"; final String lastLoginOld = col.LAST_LOGIN + "_old";
// Rename lastlogin to lastlogin_old // Rename lastlogin to lastlogin_old
@ -981,12 +958,12 @@ public class MySQL implements DataSource {
// Create lastlogin column // Create lastlogin column
sql = String.format("ALTER TABLE %s ADD COLUMN %s " sql = String.format("ALTER TABLE %s ADD COLUMN %s "
+ "TIMESTAMP NOT NULL DEFAULT current_timestamp AFTER %s", + "BIGINT NOT NULL DEFAULT 0 AFTER %s",
tableName, col.LAST_LOGIN, col.IP); tableName, col.LAST_LOGIN, col.IP);
con.prepareStatement(sql).execute(); con.prepareStatement(sql).execute();
// Set values of lastlogin based on lastlogin_old // Set values of lastlogin based on lastlogin_old
sql = String.format("UPDATE %s SET %s = FROM_UNIXTIME(%s)", sql = String.format("UPDATE %s SET %s = UNIX_TIMESTAMP(%s)",
tableName, col.LAST_LOGIN, lastLoginOld); tableName, col.LAST_LOGIN, lastLoginOld);
con.prepareStatement(sql).execute(); con.prepareStatement(sql).execute();
@ -994,7 +971,7 @@ public class MySQL implements DataSource {
sql = String.format("ALTER TABLE %s DROP COLUMN %s", sql = String.format("ALTER TABLE %s DROP COLUMN %s",
tableName, lastLoginOld); tableName, lastLoginOld);
con.prepareStatement(sql).execute(); con.prepareStatement(sql).execute();
ConsoleLogger.info("Finished migration of lastlogin (bigint to timestamp)"); ConsoleLogger.info("Finished migration of lastlogin (timestamp to bigint)");
} }
} }
@ -1002,4 +979,14 @@ public class MySQL implements DataSource {
ConsoleLogger.logException("Error during SQL operation:", e); ConsoleLogger.logException("Error during SQL operation:", e);
} }
private static void close(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
ConsoleLogger.logException("Could not close ResultSet", e);
}
}
}
} }

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.datasource; package fr.xephi.authme.datasource;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.security.crypts.HashedPassword;
@ -46,6 +47,14 @@ public class SQLite implements DataSource {
} }
} }
@VisibleForTesting
SQLite(NewSetting settings, Connection connection) {
this.database = settings.getProperty(DatabaseSettings.MYSQL_DATABASE);
this.tableName = settings.getProperty(DatabaseSettings.MYSQL_TABLE);
this.col = new Columns(settings);
this.con = connection;
}
private synchronized void connect() throws ClassNotFoundException, SQLException { private synchronized void connect() throws ClassNotFoundException, SQLException {
Class.forName("org.sqlite.JDBC"); Class.forName("org.sqlite.JDBC");
ConsoleLogger.info("SQLite driver loaded"); ConsoleLogger.info("SQLite driver loaded");
@ -341,7 +350,8 @@ public class SQLite implements DataSource {
@Override @Override
public synchronized void close() { public synchronized void close() {
try { try {
con.close(); if (con != null && !con.isClosed())
con.close();
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} }
@ -394,25 +404,19 @@ public class SQLite implements DataSource {
} }
@Override @Override
public List<String> getAllAuthsByEmail(String email) { public int countAuthsByEmail(String email) {
PreparedStatement pst = null; String sql = "SELECT COUNT(1) FROM " + tableName + " WHERE " + col.EMAIL + " = ? COLLATE NOCASE;";
ResultSet rs = null; try (PreparedStatement pst = con.prepareStatement(sql)) {
List<String> countEmail = new ArrayList<>();
try {
pst = con.prepareStatement("SELECT * FROM " + tableName + " WHERE " + col.EMAIL + "=?;");
pst.setString(1, email); pst.setString(1, email);
rs = pst.executeQuery(); try (ResultSet rs = pst.executeQuery()) {
while (rs.next()) { if (rs.next()) {
countEmail.add(rs.getString(col.NAME)); return rs.getInt(1);
}
} }
return countEmail;
} catch (SQLException ex) { } catch (SQLException ex) {
logSqlException(ex); logSqlException(ex);
} finally {
close(rs);
close(pst);
} }
return new ArrayList<>(); return 0;
} }
@Override @Override
@ -519,21 +523,6 @@ public class SQLite implements DataSource {
return 0; return 0;
} }
@Override
public void updateName(String oldOne, String newOne) {
PreparedStatement pst = null;
try {
pst = con.prepareStatement("UPDATE " + tableName + " SET " + col.NAME + "=? WHERE " + col.NAME + "=?;");
pst.setString(1, newOne);
pst.setString(2, oldOne);
pst.executeUpdate();
} catch (SQLException ex) {
logSqlException(ex);
} finally {
close(pst);
}
}
@Override @Override
public boolean updateRealName(String user, String realName) { public boolean updateRealName(String user, String realName) {
String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;"; String sql = "UPDATE " + tableName + " SET " + col.REAL_NAME + "=? WHERE " + col.NAME + "=?;";

View File

@ -17,7 +17,6 @@ import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.GeoLiteAPI;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -243,9 +242,7 @@ public class AuthMePlayerListener implements Listener {
String realName = auth.getRealName(); String realName = auth.getRealName();
if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) { if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER); event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
// TODO: Add a message like : MessageKey.INVALID_NAME_CASE event.setKickMessage(m.retrieveSingle(MessageKey.INVALID_NAME_CASE, realName, event.getName()));
event.setKickMessage("You should join using username: " + ChatColor.AQUA + realName +
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName());
return; return;
} }
if (realName.isEmpty() || realName.equals("Player")) { if (realName.isEmpty() || realName.equals("Player")) {

View File

@ -81,6 +81,8 @@ public class AuthMeServerListener implements Listener {
} }
if (pluginName.equalsIgnoreCase("ProtocolLib")) { if (pluginName.equalsIgnoreCase("ProtocolLib")) {
plugin.inventoryProtector = null; plugin.inventoryProtector = null;
plugin.tablistHider = null;
plugin.tabComplete = null;
ConsoleLogger.showError("ProtocolLib has been disabled, unhook packet inventory protection!"); ConsoleLogger.showError("ProtocolLib has been disabled, unhook packet inventory protection!");
} }
} }

View File

@ -6,32 +6,27 @@ import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
public class AuthMeTabCompletePacketAdapter extends PacketAdapter { public class AuthMeTabCompletePacketAdapter extends PacketAdapter {
public AuthMeTabCompletePacketAdapter(AuthMe plugin) { public AuthMeTabCompletePacketAdapter(AuthMe plugin) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE); super(plugin, ListenerPriority.NORMAL, PacketType.Play.Client.TAB_COMPLETE);
} }
@Override @Override
public void onPacketReceiving(PacketEvent event) public void onPacketReceiving(PacketEvent event) {
{ if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) {
if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) { try {
try if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) {
{ event.setCancelled(true);
if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) { }
event.setCancelled(true); } catch (FieldAccessException e) {
} ConsoleLogger.showError("Couldn't access field.");
}
} }
catch (FieldAccessException e)
{
ConsoleLogger.showError("Couldn't access field.");
}
}
} }
public void register() { public void register() {

View File

@ -6,32 +6,27 @@ import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldAccessException;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
public class AuthMeTablistPacketAdapter extends PacketAdapter { public class AuthMeTablistPacketAdapter extends PacketAdapter {
public AuthMeTablistPacketAdapter(AuthMe plugin) { public AuthMeTablistPacketAdapter(AuthMe plugin) {
super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO); super(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.PLAYER_INFO);
} }
@Override @Override
public void onPacketSending(PacketEvent event) public void onPacketSending(PacketEvent event) {
{ if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) {
if (event.getPacketType() == PacketType.Play.Server.PLAYER_INFO) { try {
try if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) {
{ event.setCancelled(true);
if (!PlayerCache.getInstance().isAuthenticated(event.getPlayer().getName().toLowerCase())) { }
event.setCancelled(true); } catch (FieldAccessException e) {
} ConsoleLogger.showError("Couldn't access field.");
}
} }
catch (FieldAccessException e)
{
ConsoleLogger.showError("Couldn't access field.");
}
}
} }
public void register() { public void register() {

View File

@ -127,7 +127,9 @@ public enum MessageKey {
TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"), TWO_FACTOR_CREATE("two_factor_create", "%code", "%url"),
NOT_OWNER_ERROR("not_owner_error"); NOT_OWNER_ERROR("not_owner_error"),
INVALID_NAME_CASE("invalid_name_case", "%valid", "%invalid");
private String key; private String key;
private String[] tags; private String[] tags;

View File

@ -55,17 +55,7 @@ public class Messages {
* @param replacements The replacements to apply for the tags * @param replacements The replacements to apply for the tags
*/ */
public void send(CommandSender sender, MessageKey key, String... replacements) { public void send(CommandSender sender, MessageKey key, String... replacements) {
String message = retrieveSingle(key); String message = retrieveSingle(key, replacements);
String[] tags = key.getTags();
if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]);
}
} else {
ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'");
send(sender, key);
}
for (String line : message.split("\n")) { for (String line : message.split("\n")) {
sender.sendMessage(line); sender.sendMessage(line);
} }
@ -99,6 +89,27 @@ public class Messages {
return StringUtils.join("\n", retrieve(key)); return StringUtils.join("\n", retrieve(key));
} }
/**
* Retrieve the given message code with the given tag replacements. Note that this method
* logs an error if the number of supplied replacements doesn't correspond to the number of tags
* the message key contains.
*
* @param key The key of the message to send
* @param replacements The replacements to apply for the tags
*/
public String retrieveSingle(MessageKey key, String... replacements) {
String message = retrieveSingle(key);
String[] tags = key.getTags();
if (replacements.length == tags.length) {
for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]);
}
} else {
ConsoleLogger.showError("Invalid number of replacements for message key '" + key + "'");
}
return message;
}
/** /**
* Reload the messages manager. * Reload the messages manager.
* *

View File

@ -206,20 +206,20 @@ public class AsynchronousJoin {
int msgInterval = Settings.getWarnMessageInterval; int msgInterval = Settings.getWarnMessageInterval;
if (timeOut > 0) { if (timeOut > 0) {
BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), timeOut); BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut);
LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id);
} }
String[] msg; MessageKey msg;
if (isAuthAvailable) { if (isAuthAvailable) {
msg = m.retrieve(MessageKey.LOGIN_MESSAGE); msg = MessageKey.LOGIN_MESSAGE;
} else { } else {
msg = Settings.emailRegistration msg = Settings.emailRegistration
? m.retrieve(MessageKey.REGISTER_EMAIL_MESSAGE) ? MessageKey.REGISTER_EMAIL_MESSAGE
: m.retrieve(MessageKey.REGISTER_MESSAGE); : MessageKey.REGISTER_MESSAGE;
} }
if (msgInterval > 0 && LimboCache.getInstance().getLimboPlayer(name) != null) { if (msgInterval > 0 && LimboCache.getInstance().getLimboPlayer(name) != null) {
BukkitTask msgTask = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, msgInterval)); BukkitTask msgTask = sched.runTask(plugin, new MessageTask(plugin, name, msg, msgInterval));
LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask);
} }
} }

View File

@ -105,7 +105,7 @@ public class AsynchronousLogin {
} else { } else {
msg = m.retrieve(MessageKey.REGISTER_MESSAGE); msg = m.retrieve(MessageKey.REGISTER_MESSAGE);
} }
BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin, BukkitTask msgT = Bukkit.getScheduler().runTask(plugin,
new MessageTask(plugin, name, msg, settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL))); new MessageTask(plugin, name, msg, settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL)));
LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT);
} }

View File

@ -74,21 +74,24 @@ public class ProcessSyncronousPlayerLogout implements Runnable {
int interval = Settings.getWarnMessageInterval; int interval = Settings.getWarnMessageInterval;
BukkitScheduler sched = player.getServer().getScheduler(); BukkitScheduler sched = player.getServer().getScheduler();
if (timeOut != 0) { if (timeOut != 0) {
BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), timeOut); BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut);
LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id); LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id);
} }
BukkitTask msgT = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), interval)); BukkitTask msgT = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval));
LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT);
if (player.isInsideVehicle() && player.getVehicle() != null) if (player.isInsideVehicle() && player.getVehicle() != null) {
player.getVehicle().eject(); player.getVehicle().eject();
if (Settings.applyBlindEffect) }
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Settings.getRegistrationTimeout * 20, 2)); if (Settings.applyBlindEffect) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2));
}
player.setOp(false); player.setOp(false);
restoreSpeedEffect(); restoreSpeedEffect();
// Player is now logout... Time to fire event ! // Player is now logout... Time to fire event !
Bukkit.getServer().getPluginManager().callEvent(new LogoutEvent(player)); Bukkit.getServer().getPluginManager().callEvent(new LogoutEvent(player));
if (Settings.bungee) if (Settings.bungee) {
sendBungeeMessage(); sendBungeeMessage();
}
m.send(player, MessageKey.LOGOUT_SUCCESS); m.send(player, MessageKey.LOGOUT_SUCCESS);
ConsoleLogger.info(player.getName() + " logged out"); ConsoleLogger.info(player.getName() + " logged out");
} }

View File

@ -5,34 +5,26 @@ import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.cache.limbo.LimboPlayer;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
/**
*/
public class AsynchronousQuit { public class AsynchronousQuit {
protected final AuthMe plugin; private final AuthMe plugin;
protected final DataSource database; private final DataSource database;
protected final Player player; private final Player player;
private final String name; private final String name;
private boolean isOp = false; private boolean isOp = false;
private boolean needToChange = false; private boolean needToChange = false;
private boolean isKick = false; private boolean isKick = false;
/**
* Constructor for AsynchronousQuit.
*
* @param p Player
* @param plugin AuthMe
* @param database DataSource
* @param isKick boolean
*/
public AsynchronousQuit(Player p, AuthMe plugin, DataSource database, public AsynchronousQuit(Player p, AuthMe plugin, DataSource database,
boolean isKick) { boolean isKick) {
this.player = p; this.player = p;
@ -43,9 +35,7 @@ public class AsynchronousQuit {
} }
public void process() { public void process() {
if (player == null) if (player == null || Utils.isUnrestricted(player)) {
return;
if (Utils.isUnrestricted(player)) {
return; return;
} }
@ -54,7 +44,9 @@ public class AsynchronousQuit {
if (PlayerCache.getInstance().isAuthenticated(name)) { if (PlayerCache.getInstance().isAuthenticated(name)) {
if (Settings.isSaveQuitLocationEnabled) { if (Settings.isSaveQuitLocationEnabled) {
Location loc = player.getLocation(); Location loc = player.getLocation();
PlayerAuth auth = new PlayerAuth(name, loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName(), player.getName()); PlayerAuth auth = PlayerAuth.builder()
.name(name).location(loc)
.realName(player.getName()).build();
database.updateQuitLoc(auth); database.updateQuitLoc(auth);
} }
PlayerAuth auth = new PlayerAuth(name, ip, System.currentTimeMillis(), player.getName()); PlayerAuth auth = new PlayerAuth(name, ip, System.currentTimeMillis(), player.getName());
@ -63,14 +55,11 @@ public class AsynchronousQuit {
LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name); LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name);
if (limbo != null) { if (limbo != null) {
if (limbo.getGroup() != null && !limbo.getGroup().isEmpty()) if (!StringUtils.isEmpty(limbo.getGroup())) {
Utils.addNormal(player, limbo.getGroup()); Utils.addNormal(player, limbo.getGroup());
}
needToChange = true; needToChange = true;
isOp = limbo.getOperator(); isOp = limbo.getOperator();
if (limbo.getTimeoutTaskId() != null)
limbo.getTimeoutTaskId().cancel();
if (limbo.getMessageTaskId() != null)
limbo.getMessageTaskId().cancel();
LimboCache.getInstance().deleteLimboPlayer(name); LimboCache.getInstance().deleteLimboPlayer(name);
} }
if (Settings.isSessionsEnabled && !isKick) { if (Settings.isSessionsEnabled && !isKick) {
@ -100,12 +89,15 @@ public class AsynchronousQuit {
if (plugin.isEnabled()) { if (plugin.isEnabled()) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange));
} }
// remove player from cache
if (database instanceof CacheDataSource) {
((CacheDataSource) database).getCachedAuths().invalidate(name);
}
} }
private void postLogout() { private void postLogout() {
PlayerCache.getInstance().removePlayer(name); PlayerCache.getInstance().removePlayer(name);
if (database.isLogged(name)) database.setUnlogged(name);
database.setUnlogged(name);
plugin.sessions.remove(name); plugin.sessions.remove(name);
} }
} }

View File

@ -98,7 +98,7 @@ public class AsyncRegister {
private void emailRegister() { private void emailRegister() {
if (Settings.getmaxRegPerEmail > 0 if (Settings.getmaxRegPerEmail > 0
&& !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS) && !plugin.getPermissionsManager().hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
&& database.getAllAuthsByEmail(email).size() >= Settings.getmaxRegPerEmail) { && database.countAuthsByEmail(email) >= Settings.getmaxRegPerEmail) {
m.send(player, MessageKey.MAX_REGISTER_EXCEEDED); m.send(player, MessageKey.MAX_REGISTER_EXCEEDED);
return; return;
} }

View File

@ -52,14 +52,13 @@ public class ProcessSyncEmailRegister implements Runnable {
int msgInterval = Settings.getWarnMessageInterval; int msgInterval = Settings.getWarnMessageInterval;
BukkitScheduler sched = plugin.getServer().getScheduler(); BukkitScheduler sched = plugin.getServer().getScheduler();
if (time != 0 && limbo != null) {
limbo.getTimeoutTaskId().cancel();
BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), time);
limbo.setTimeoutTaskId(id);
}
if (limbo != null) { if (limbo != null) {
limbo.getMessageTaskId().cancel(); if (time != 0) {
BukkitTask nwMsg = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval)); BukkitTask id = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), time);
limbo.setTimeoutTaskId(id);
}
BukkitTask nwMsg = sched.runTask(plugin, new MessageTask(plugin, name, m.retrieve(MessageKey.LOGIN_MESSAGE), msgInterval));
limbo.setMessageTaskId(nwMsg); limbo.setMessageTaskId(nwMsg);
} }

View File

@ -1,16 +1,7 @@
package fr.xephi.authme.process.register; package fr.xephi.authme.process.register;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.properties.HooksSettings;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboCache;
@ -19,10 +10,17 @@ import fr.xephi.authme.events.LoginEvent;
import fr.xephi.authme.events.RestoreInventoryEvent; import fr.xephi.authme.events.RestoreInventoryEvent;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.task.MessageTask; import fr.xephi.authme.task.MessageTask;
import fr.xephi.authme.task.TimeoutTask; import fr.xephi.authme.task.TimeoutTask;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
/** /**
*/ */
@ -37,8 +35,8 @@ public class ProcessSyncPasswordRegister implements Runnable {
/** /**
* Constructor for ProcessSyncPasswordRegister. * Constructor for ProcessSyncPasswordRegister.
* *
* @param player Player * @param player Player
* @param plugin AuthMe * @param plugin AuthMe
* @param settings The plugin settings * @param settings The plugin settings
*/ */
public ProcessSyncPasswordRegister(Player player, AuthMe plugin, NewSetting settings) { public ProcessSyncPasswordRegister(Player player, AuthMe plugin, NewSetting settings) {
@ -77,11 +75,10 @@ public class ProcessSyncPasswordRegister implements Runnable {
BukkitScheduler sched = plugin.getServer().getScheduler(); BukkitScheduler sched = plugin.getServer().getScheduler();
BukkitTask task; BukkitTask task;
if (delay != 0) { if (delay != 0) {
task = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), delay); task = sched.runTaskLater(plugin, new TimeoutTask(plugin, name, player), delay);
cache.getLimboPlayer(name).setTimeoutTaskId(task); cache.getLimboPlayer(name).setTimeoutTaskId(task);
} }
task = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, task = sched.runTask(plugin, new MessageTask(plugin, name, MessageKey.LOGIN_MESSAGE, interval));
m.retrieve(MessageKey.LOGIN_MESSAGE), interval));
cache.getLimboPlayer(name).setMessageTaskId(task); cache.getLimboPlayer(name).setMessageTaskId(task);
if (player.isInsideVehicle() && player.getVehicle() != null) { if (player.isInsideVehicle() && player.getVehicle() != null) {
player.getVehicle().eject(); player.getVehicle().eject();

View File

@ -73,12 +73,11 @@ public class AsynchronousUnregister {
int interval = Settings.getWarnMessageInterval; int interval = Settings.getWarnMessageInterval;
BukkitScheduler scheduler = plugin.getServer().getScheduler(); BukkitScheduler scheduler = plugin.getServer().getScheduler();
if (timeOut != 0) { if (timeOut != 0) {
BukkitTask id = scheduler.runTaskLaterAsynchronously(plugin, BukkitTask id = scheduler.runTaskLater(plugin, new TimeoutTask(plugin, name, player), timeOut);
new TimeoutTask(plugin, name, player), timeOut);
limboPlayer.setTimeoutTaskId(id); limboPlayer.setTimeoutTaskId(id);
} }
limboPlayer.setMessageTaskId(scheduler.runTaskAsynchronously(plugin, limboPlayer.setMessageTaskId(scheduler.runTask(plugin,
new MessageTask(plugin, name, m.retrieve(MessageKey.REGISTER_MESSAGE), interval))); new MessageTask(plugin, name, MessageKey.REGISTER_MESSAGE, interval)));
m.send(player, MessageKey.UNREGISTERED_SUCCESS); m.send(player, MessageKey.UNREGISTERED_SUCCESS);
ConsoleLogger.info(player.getDisplayName() + " unregistered himself"); ConsoleLogger.info(player.getDisplayName() + " unregistered himself");
return; return;

View File

@ -82,4 +82,13 @@ public abstract class CustomConfiguration extends YamlConfiguration {
} }
return false; return false;
} }
public boolean containsAll(String... paths) {
for (String path : paths) {
if (!contains(path)) {
return false;
}
}
return true;
}
} }

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings; import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings; import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings; import fr.xephi.authme.settings.properties.RestrictionSettings;
@ -144,8 +145,6 @@ public final class Settings {
denyTabcompleteBeforeLogin = load(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN); denyTabcompleteBeforeLogin = load(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
hideTablistBeforeLogin = load(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN); hideTablistBeforeLogin = load(RestrictionSettings.HIDE_TABLIST_BEFORE_LOGIN);
plugin.checkProtocolLib();
passwordMaxLength = load(SecuritySettings.MAX_PASSWORD_LENGTH); passwordMaxLength = load(SecuritySettings.MAX_PASSWORD_LENGTH);
backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\"); backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\");
isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true); isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true);
@ -176,7 +175,7 @@ public final class Settings {
emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false); emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false);
saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8); saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8);
getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1); getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1);
multiverse = configFile.getBoolean("Hooks.multiverse", true); multiverse = load(HooksSettings.MULTIVERSE);
bungee = configFile.getBoolean("Hooks.bungeecord", false); bungee = configFile.getBoolean("Hooks.bungeecord", false);
getForcedWorlds = configFile.getStringList("settings.restrictions.ForceSpawnOnTheseWorlds"); getForcedWorlds = configFile.getStringList("settings.restrictions.ForceSpawnOnTheseWorlds");
banUnsafeIp = configFile.getBoolean("settings.restrictions.banUnsafedIP", false); banUnsafeIp = configFile.getBoolean("settings.restrictions.banUnsafedIP", false);
@ -213,7 +212,7 @@ public final class Settings {
broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false); broadcastWelcomeMessage = configFile.getBoolean("settings.broadcastWelcomeMessage", false);
forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", false); forceRegKick = configFile.getBoolean("settings.registration.forceKickAfterRegister", false);
forceRegLogin = configFile.getBoolean("settings.registration.forceLoginAfterRegister", false); forceRegLogin = configFile.getBoolean("settings.registration.forceLoginAfterRegister", false);
spawnPriority = configFile.getString("settings.restrictions.spawnPriority", "authme,essentials,multiverse,default"); spawnPriority = load(RestrictionSettings.SPAWN_PRIORITY);
getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0); getMaxLoginPerIp = configFile.getInt("settings.restrictions.maxLoginPerIp", 0);
getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0); getMaxJoinPerIp = configFile.getInt("settings.restrictions.maxJoinPerIp", 0);
checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false); checkVeryGames = configFile.getBoolean("VeryGames.enableIpCheck", false);

View File

@ -1,7 +1,13 @@
package fr.xephi.authme.settings; package fr.xephi.authme.settings;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.io.File; import java.io.File;
@ -12,13 +18,17 @@ import java.io.File;
public class Spawn extends CustomConfiguration { public class Spawn extends CustomConfiguration {
private static Spawn spawn; private static Spawn spawn;
private static String[] spawnPriority;
public Spawn() { private Spawn() {
super(new File("." + File.separator + "plugins" + File.separator + "AuthMe" + File.separator + "spawn.yml")); super(new File(Settings.PLUGIN_FOLDER, "spawn.yml"));
spawn = this;
load(); load();
save(); save();
saveDefault(); spawnPriority = Settings.spawnPriority.split(",");
}
public static void reload() {
spawn = new Spawn();
} }
/** /**
@ -33,111 +43,106 @@ public class Spawn extends CustomConfiguration {
return spawn; return spawn;
} }
private void saveDefault() {
if (!contains("spawn")) {
set("spawn.world", "");
set("spawn.x", "");
set("spawn.y", "");
set("spawn.z", "");
set("spawn.yaw", "");
set("spawn.pitch", "");
save();
}
if (!contains("firstspawn")) {
set("firstspawn.world", "");
set("firstspawn.x", "");
set("firstspawn.y", "");
set("firstspawn.z", "");
set("firstspawn.yaw", "");
set("firstspawn.pitch", "");
save();
}
}
/**
* Method setSpawn.
*
* @param location Location
*
* @return boolean
*/
public boolean setSpawn(Location location) { public boolean setSpawn(Location location) {
try { if (location == null || location.getWorld() == null) {
set("spawn.world", location.getWorld().getName());
set("spawn.x", location.getX());
set("spawn.y", location.getY());
set("spawn.z", location.getZ());
set("spawn.yaw", location.getYaw());
set("spawn.pitch", location.getPitch());
save();
return true;
} catch (NullPointerException npe) {
return false; return false;
} }
set("spawn.world", location.getWorld().getName());
set("spawn.x", location.getX());
set("spawn.y", location.getY());
set("spawn.z", location.getZ());
set("spawn.yaw", location.getYaw());
set("spawn.pitch", location.getPitch());
save();
return true;
} }
/**
* Method setFirstSpawn.
*
* @param location Location
*
* @return boolean
*/
public boolean setFirstSpawn(Location location) { public boolean setFirstSpawn(Location location) {
try { if (location == null || location.getWorld() == null) {
set("firstspawn.world", location.getWorld().getName());
set("firstspawn.x", location.getX());
set("firstspawn.y", location.getY());
set("firstspawn.z", location.getZ());
set("firstspawn.yaw", location.getYaw());
set("firstspawn.pitch", location.getPitch());
save();
return true;
} catch (NullPointerException npe) {
return false; return false;
} }
set("firstspawn.world", location.getWorld().getName());
set("firstspawn.x", location.getX());
set("firstspawn.y", location.getY());
set("firstspawn.z", location.getZ());
set("firstspawn.yaw", location.getYaw());
set("firstspawn.pitch", location.getPitch());
save();
return true;
} }
/**
* Method getLocation.
*
* @return Location
*/
@Deprecated
public Location getLocation() {
return getSpawn();
}
/**
* Method getSpawn.
*
* @return Location
*/
public Location getSpawn() { public Location getSpawn() {
try { if (containsAll("spawn.world", "spawn.x", "spawn.y", "spawn.z", "spawn.yaw", "spawn.pitch")) {
if (this.getString("spawn.world").isEmpty() || this.getString("spawn.world").equals("")) String worldName = getString("spawn.world");
return null; World world = Bukkit.getWorld(worldName);
Location location = new Location(Bukkit.getWorld(this.getString("spawn.world")), this.getDouble("spawn.x"), this.getDouble("spawn.y"), this.getDouble("spawn.z"), Float.parseFloat(this.getString("spawn.yaw")), Float.parseFloat(this.getString("spawn.pitch"))); if (!StringUtils.isEmpty(worldName) && world != null) {
return location; return new Location(
} catch (NullPointerException | NumberFormatException npe) { world, getDouble("spawn.x"), getDouble("spawn.y"), getDouble("spawn.z"),
return null; Float.parseFloat(getString("spawn.yaw")), Float.parseFloat(getString("spawn.pitch"))
);
}
} }
return null;
} }
/**
* Method getFirstSpawn.
*
* @return Location
*/
public Location getFirstSpawn() { public Location getFirstSpawn() {
try { if (containsAll("firstspawn.world", "firstspawn.x", "firstspawn.y",
if (this.getString("firstspawn.world").isEmpty() || this.getString("firstspawn.world").equals("")) "firstspawn.z", "firstspawn.yaw", "firstspawn.pitch")) {
return null; String worldName = getString("firstspawn.world");
Location location = new Location(Bukkit.getWorld(this.getString("firstspawn.world")), this.getDouble("firstspawn.x"), this.getDouble("firstspawn.y"), this.getDouble("firstspawn.z"), Float.parseFloat(this.getString("firstspawn.yaw")), Float.parseFloat(this.getString("firstspawn.pitch"))); World world = Bukkit.getWorld(worldName);
return location; if (!StringUtils.isEmpty(worldName) && world != null) {
} catch (NullPointerException | NumberFormatException npe) { return new Location(
return null; world, getDouble("firstspawn.x"), getDouble("firstspawn.y"), getDouble("firstspawn.z"),
Float.parseFloat(getString("firstspawn.yaw")), Float.parseFloat(getString("firstspawn.pitch"))
);
}
} }
return null;
} }
// Return the spawn location of a player
public Location getSpawnLocation(Player player) {
AuthMe plugin = AuthMe.getInstance();
if (plugin == null || player == null || player.getWorld() == null) {
return null;
}
World world = player.getWorld();
Location spawnLoc = null;
for (String priority : spawnPriority) {
switch (priority.toLowerCase()) {
case "default":
if (world.getSpawnLocation() != null) {
spawnLoc = world.getSpawnLocation();
}
break;
case "multiverse":
if (Settings.multiverse && plugin.multiverse != null) {
MVWorldManager manager = plugin.multiverse.getMVWorldManager();
if (manager.isMVWorld(world)) {
spawnLoc = manager.getMVWorld(world).getSpawnLocation();
}
}
break;
case "essentials":
spawnLoc = plugin.essentialsSpawn;
break;
case "authme":
String playerNameLower = player.getName().toLowerCase();
if (PlayerCache.getInstance().isAuthenticated(playerNameLower)) {
spawnLoc = getSpawn();
} else if ((getFirstSpawn() != null) && (!player.hasPlayedBefore() ||
(!plugin.getDataSource().isAuthAvailable(playerNameLower)))) {
spawnLoc = getFirstSpawn();
} else {
spawnLoc = getSpawn();
}
break;
}
if (spawnLoc != null) {
return spawnLoc;
}
}
return world.getSpawnLocation(); // return default location
}
} }

View File

@ -3,6 +3,7 @@ package fr.xephi.authme.task;
import fr.xephi.authme.AuthMe; import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboCache;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
@ -24,32 +25,31 @@ public class MessageTask implements Runnable {
* @param strings String[] * @param strings String[]
* @param interval int * @param interval int
*/ */
public MessageTask(AuthMe plugin, String name, String[] strings, public MessageTask(AuthMe plugin, String name, String[] strings, int interval) {
int interval) {
this.plugin = plugin; this.plugin = plugin;
this.name = name; this.name = name;
this.msg = strings; this.msg = strings;
this.interval = interval; this.interval = interval;
} }
/** public MessageTask(AuthMe plugin, String name, MessageKey messageKey, int interval) {
* Method run. this(plugin, name, plugin.getMessages().retrieve(messageKey), interval);
* }
* @see java.lang.Runnable#run()
*/
@Override @Override
public void run() { public void run() {
if (PlayerCache.getInstance().isAuthenticated(name)) if (PlayerCache.getInstance().isAuthenticated(name)) {
return; return;
}
for (Player player : Utils.getOnlinePlayers()) { for (Player player : Utils.getOnlinePlayers()) {
if (player.getName().toLowerCase().equals(name)) { if (player.getName().equalsIgnoreCase(name)) {
for (String ms : msg) { for (String ms : msg) {
player.sendMessage(ms); player.sendMessage(ms);
} }
BukkitTask late = plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, this, interval * 20); BukkitTask nextTask = plugin.getServer().getScheduler().runTaskLater(plugin, this, interval * 20);
if (LimboCache.getInstance().hasLimboPlayer(name)) { if (LimboCache.getInstance().hasLimboPlayer(name)) {
LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(late); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(nextTask);
} }
return; return;
} }

View File

@ -4,14 +4,10 @@ import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages; import fr.xephi.authme.output.Messages;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
/**
*/
public class TimeoutTask implements Runnable { public class TimeoutTask implements Runnable {
private final AuthMe plugin;
private final String name; private final String name;
private final Messages m; private final Messages m;
private final Player player; private final Player player;
@ -25,38 +21,14 @@ public class TimeoutTask implements Runnable {
*/ */
public TimeoutTask(AuthMe plugin, String name, Player player) { public TimeoutTask(AuthMe plugin, String name, Player player) {
this.m = plugin.getMessages(); this.m = plugin.getMessages();
this.plugin = plugin;
this.name = name; this.name = name;
this.player = player; this.player = player;
} }
/**
* Method getName.
*
* @return String
*/
public String getName() {
return name;
}
/**
* Method run.
*
* @see java.lang.Runnable#run()
*/
@Override @Override
public void run() { public void run() {
if (PlayerCache.getInstance().isAuthenticated(name)) { if (!PlayerCache.getInstance().isAuthenticated(name)) {
return; player.kickPlayer(m.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR));
} }
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
@Override
public void run() {
if (player.isOnline()) {
player.kickPlayer(m.retrieveSingle(MessageKey.LOGIN_TIMEOUT_ERROR));
}
}
});
} }
} }

View File

@ -58,6 +58,7 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod автоматично изключ
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # TODO invalid_session: '&cYour IP has been changed and your session data has expired!'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -44,7 +44,7 @@ usage_captcha: '&3Para logar você deve resolver o captcha, por favor use o coma
wrong_captcha: '&cCaptcha inválido, por favor escreva "/captcha THE_CAPTCHA"' wrong_captcha: '&cCaptcha inválido, por favor escreva "/captcha THE_CAPTCHA"'
valid_captcha: '&2Código do captcha correto!' valid_captcha: '&2Código do captcha correto!'
kick_forvip: '&3Um vip entrou no servidor!' kick_forvip: '&3Um vip entrou no servidor!'
kick_fullserver: '&4Servidor esta cheio! Para entrar mesmo cheio compre vip no site www.site.com.br' kick_fullserver: '&4Servidor esta cheio, tente outra vez mais tarde'
usage_email_add: '&cUse: /email add <email> <email>' usage_email_add: '&cUse: /email add <email> <email>'
usage_email_change: '&cUse: /email change <email antigo> <email novo>' usage_email_change: '&cUse: /email change <email antigo> <email novo>'
usage_email_recovery: '&cUse: /email recovery <email>' usage_email_recovery: '&cUse: /email recovery <email>'
@ -56,10 +56,10 @@ email_confirm: '&cPor favor confirme o email!'
email_changed: '&2Email mudado com sucesso!' email_changed: '&2Email mudado com sucesso!'
email_send: '&2Email de recuperação enviado com sucesso! !' email_send: '&2Email de recuperação enviado com sucesso! !'
email_exists: '&cUm email de recuperação já foi enviado! Você pode reenviar outro usando o comando:' email_exists: '&cUm email de recuperação já foi enviado! Você pode reenviar outro usando o comando:'
country_banned: '&4Seu país foi banido do servidor! Your country is banned from this server!' country_banned: '&4Seu país foi banido do servidor!'
antibot_auto_enabled: '&4[AntiBotService] AntiBot ativado devido ao grande número de conexões!' antibot_auto_enabled: '&4[AntiBotService] AntiBot ativado devido ao grande número de conexões!'
antibot_auto_disabled: '&2[AntiBotService] AntiBot desativado após %m minutos!' antibot_auto_disabled: '&2[AntiBotService] AntiBot desativado após %m minutos!'
# TODO two_factor_create: Missing tag %url two_factor_create: '&2Seu código secreto é %code. Você pode escanear ele daqui %url'
two_factor_create: '&2Seu código secreto é %code' email_already_used: '&4Este endereço de email já está em uso'
# TODO email_already_used: '&4The email address is already being used' not_owner_error: 'Você não é o dono desta conta. Por favor, tente outro nome!'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' invalid_name_case: 'Você deve entrar usando %valid, não %invalid.'

View File

@ -58,5 +58,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod automaticky ukoncen po %m minutach,
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -60,4 +60,5 @@ kick_antibot: 'AntiBotMod ist aktiviert! Bitte warte einige Minuten, bevor du di
# TODO two_factor_create: Missing tag %url # TODO two_factor_create: Missing tag %url
two_factor_create: '&2Dein geheimer Code ist %code' two_factor_create: '&2Dein geheimer Code ist %code'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -60,3 +60,4 @@ antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m mi
email_already_used: '&4The email address is already being used' email_already_used: '&4The email address is already being used'
two_factor_create: '&2Your secret code is %code. You can scan it from here %url' two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
not_owner_error: 'You are not the owner of this account. Please try another name!' not_owner_error: 'You are not the owner of this account. Please try another name!'
invalid_name_case: 'You should join using username %valid, not %invalid.'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automáticamente luego d
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -52,6 +52,7 @@ country_banned: '[AuthMe] Zure herrialdea blokeatuta dago zerbitzari honetan'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO invalid_session: '&cYour IP has been changed and your session data has expired!' # TODO invalid_session: '&cYour IP has been changed and your session data has expired!'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'

View File

@ -55,6 +55,7 @@ email_send: '[AuthMe] Palautus sähköposti lähetetty!'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'

View File

@ -61,4 +61,5 @@ email_exists: '&cUn email de restauration a déjà été envoyé ! Vous pouvez l
# TODO two_factor_create: Missing tag %url # TODO two_factor_create: Missing tag %url
two_factor_create: '&2Votre code secret est %code' two_factor_create: '&2Votre code secret est %code'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -60,5 +60,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivouse automáticamente despo
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -58,5 +58,6 @@ antibot_auto_enabled: '&4[AntiBot] Az AntiBot védelem bekapcsolt a nagy számú
antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m múlva!' antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m múlva!'
kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérünk várj pár másodpercet a csatlakozáshoz.' kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérünk várj pár másodpercet a csatlakozáshoz.'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -55,6 +55,7 @@ antibot_auto_enabled: '&4[AntiBotService] AntiBot diaktifkan dikarenakan banyak
antibot_auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!' antibot_auto_disabled: '&2[AntiBotService] AntiBot dimatikan setelah %m menit!'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO usage_unreg: '&cUsage: /unregister <password>' # TODO usage_unreg: '&cUsage: /unregister <password>'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'

View File

@ -60,4 +60,5 @@ antibot_auto_disabled: "Il servizio di AntiBot è stato automaticamente disabili
kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.' kick_antibot: 'Il servizio di AntiBot è attualmente attivo! Devi aspettare qualche minuto prima di poter entrare nel server.'
two_factor_create: '&2Il tuo codice segreto è: &f%code&n&2Puoi anche scannerizzare il codice QR da qui: &f%url' two_factor_create: '&2Il tuo codice segreto è: &f%code&n&2Puoi anche scannerizzare il codice QR da qui: &f%url'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -61,5 +61,6 @@ antibot_auto_disabled: '[AuthMe] 봇차단모드가 %m 분 후에 자동적으
# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...' # TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -46,6 +46,7 @@ kick_fullserver: '&cServeris yra pilnas, Atsiprasome.'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO new_email_invalid: '&cInvalid new email, try again!' # TODO new_email_invalid: '&cInvalid new email, try again!'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!' # TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!'
# TODO usage_email_recovery: '&cUsage: /email recovery <Email>' # TODO usage_email_recovery: '&cUsage: /email recovery <Email>'
# TODO email_confirm: '&cPlease confirm your email address!' # TODO email_confirm: '&cPlease confirm your email address!'

View File

@ -59,5 +59,6 @@ kick_antibot: 'AntiBot is aangezet! Wacht alsjeblieft enkele minuten voor je met
two_factor_create: '&2Je geheime code is %code' two_factor_create: '&2Je geheime code is %code'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO reg_email_msg: '&3Please, register to the server with the command "/register <email> <confirmEmail>"' # TODO reg_email_msg: '&3Please, register to the server with the command "/register <email> <confirmEmail>"'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -55,6 +55,7 @@ email_send: '[AuthMe] Email z odzyskaniem wyslany!'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!' # TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod desactivado automaticamente após %m
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -58,5 +58,6 @@ antibot_auto_disabled: '&a[AuthMe] AntiBot-режим автоматичски
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -38,28 +38,29 @@ name_len: '&cTvoje meno je velmi krátke alebo dlhé'
regex: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: REG_EX' regex: '&cTvoje meno obsahuje zakázané znaky. Povolené znaky: REG_EX'
add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"' add_email: '&cPridaj svoj e-mail príkazom "/email add email zopakujEmail"'
recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery <tvojEmail>' recovery_email: '&cZabudol si heslo? Pouzi príkaz /email recovery <tvojEmail>'
# TODO email_already_used: '&4The email address is already being used'
# TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...'
# TODO usage_email_change: '&cUsage: /email change <oldEmail> <newEmail>' # TODO usage_email_change: '&cUsage: /email change <oldEmail> <newEmail>'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_nick: '&cYou can''t use your name as password, please choose another one...'
# TODO email_already_used: '&4The email address is already being used'
# TODO new_email_invalid: '&cInvalid new email, try again!' # TODO new_email_invalid: '&cInvalid new email, try again!'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!'
# TODO email_confirm: '&cPlease confirm your email address!'
# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"'
# TODO usage_email_recovery: '&cUsage: /email recovery <Email>'
# TODO email_changed: '&2Email address changed correctly!'
# TODO old_email_invalid: '&cInvalid old email, try again!' # TODO old_email_invalid: '&cInvalid old email, try again!'
# TODO email_changed: '&2Email address changed correctly!'
# TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!' # TODO antibot_auto_disabled: '&2[AntiBotService] AntiBot disabled disabled after %m minutes!'
# TODO kick_fullserver: '&4The server is full, try again later!'
# TODO email_added: '&2Email address successfully added to your account!'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO country_banned: '&4Your country is banned from this server!' # TODO country_banned: '&4Your country is banned from this server!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO email_invalid: '&cInvalid email address, try again!'
# TODO kick_forvip: '&3A VIP player has joined the server when it was full!'
# TODO usage_email_add: '&cUsage: /email add <email> <confirmEmail>' # TODO usage_email_add: '&cUsage: /email add <email> <confirmEmail>'
# TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!' # TODO wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
# TODO valid_captcha: '&2Captcha code solved correctly!' # TODO valid_captcha: '&2Captcha code solved correctly!'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO email_send: '&2Recovery email sent successfully! Please check your email inbox!'
# TODO usage_email_recovery: '&cUsage: /email recovery <Email>'
# TODO usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"'
# TODO email_confirm: '&cPlease confirm your email address!'
# TODO kick_fullserver: '&4The server is full, try again later!'
# TODO email_added: '&2Email address successfully added to your account!'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO kick_forvip: '&3A VIP player has joined the server when it was full!'
# TODO email_invalid: '&cInvalid email address, try again!'
# TODO antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -58,5 +58,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMode %m dakika sonra otomatik olarak isg
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBotMod автоматично вимкну
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -59,5 +59,6 @@ antibot_auto_disabled: '[AuthMe] AntiBot tự huỷ kích hoạt sau %m phút, h
# TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...' # TODO password_error_unsafe: '&cThe chosen password isn''t safe, please choose another one...'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -63,5 +63,6 @@ antibot_auto_disabled: '&8[&6用戶系統&8] 防止機械人程序檢查到不
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' # TODO email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -63,4 +63,5 @@ email_already_used: '&4邮箱已被使用'
kick_antibot: '[AuthMe] 防机器人程序已启用 !请稍等几分钟後才再次进入服务器' kick_antibot: '[AuthMe] 防机器人程序已启用 !请稍等几分钟後才再次进入服务器'
email_exists: '&c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:' email_exists: '&c恢复邮件已发送 ! 你可以丢弃它然後使用以下的指令来发送新的邮件:'
two_factor_create: '&2你的代码是 %code你可以使用 %url 来扫描' two_factor_create: '&2你的代码是 %code你可以使用 %url 来扫描'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -63,5 +63,6 @@ antibot_auto_enabled: '&b【AuthMe】&6AntiBotMod已自動啟用!'
antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod將會於 &c%m &6分鐘後自動關閉' antibot_auto_disabled: '&b【AuthMe】&6AntiBotMod將會於 &c%m &6分鐘後自動關閉'
# TODO email_already_used: '&4The email address is already being used' # TODO email_already_used: '&4The email address is already being used'
# TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' # TODO kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.'
# TODO invalid_name_case: 'You should join using username %valid, not %invalid.'
# TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url' # TODO two_factor_create: '&2Your secret code is %code. You can scan it from here %url'
# TODO not_owner_error: 'You are not the owner of this account. Please try another name!' # TODO not_owner_error: 'You are not the owner of this account. Please try another name!'

View File

@ -2,6 +2,8 @@ package fr.xephi.authme;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
/** /**
* AuthMe test utilities. * AuthMe test utilities.
@ -18,11 +20,31 @@ public final class TestHelper {
* @return The project file * @return The project file
*/ */
public static File getJarFile(String path) { public static File getJarFile(String path) {
URL url = getUrlOrThrow(path);
return new File(url.getFile());
}
/**
* Return a {@link Path} to a file in the JAR's resources (main or test).
*
* @param path The absolute path to the file
* @return The Path object to the file
*/
public static Path getJarPath(String path) {
String sqlFilePath = getUrlOrThrow(path).getPath();
// Windows preprends the path with a '/' or '\', which Paths cannot handle
String appropriatePath = System.getProperty("os.name").contains("indow")
? sqlFilePath.substring(1)
: sqlFilePath;
return Paths.get(appropriatePath);
}
private static URL getUrlOrThrow(String path) {
URL url = TestHelper.class.getResource(path); URL url = TestHelper.class.getResource(path);
if (url == null) { if (url == null) {
throw new IllegalStateException("File '" + path + "' could not be loaded"); throw new IllegalStateException("File '" + path + "' could not be loaded");
} }
return new File(url.getFile()); return url;
} }
} }

View File

@ -0,0 +1,295 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static fr.xephi.authme.datasource.AuthMeMatchers.equalToHash;
import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthBasicData;
import static fr.xephi.authme.datasource.AuthMeMatchers.hasAuthLocation;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assume.assumeThat;
/**
* Abstract class for data source integration tests.
*/
public abstract class AbstractDataSourceIntegrationTest {
protected abstract DataSource getDataSource();
@Test
public void shouldReturnIfAuthIsAvailableOrNot() {
// given
DataSource dataSource = getDataSource();
// when
boolean isBobbyAvailable = dataSource.isAuthAvailable("bobby");
boolean isChrisAvailable = dataSource.isAuthAvailable("chris");
boolean isUserAvailable = dataSource.isAuthAvailable("USER");
// then
assertThat(isBobbyAvailable, equalTo(true));
assertThat(isChrisAvailable, equalTo(false));
assertThat(isUserAvailable, equalTo(true));
}
@Test
public void shouldReturnPassword() {
// given
DataSource dataSource = getDataSource();
// when
HashedPassword bobbyPassword = dataSource.getPassword("bobby");
HashedPassword invalidPassword = dataSource.getPassword("doesNotExist");
HashedPassword userPassword = dataSource.getPassword("user");
// then
assertThat(bobbyPassword, equalToHash("$SHA$11aa0706173d7272$dbba966"));
assertThat(invalidPassword, nullValue());
assertThat(userPassword, equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"));
}
@Test
public void shouldGetAuth() {
// given
DataSource dataSource = getDataSource();
// when
PlayerAuth invalidAuth = dataSource.getAuth("notInDB");
PlayerAuth bobbyAuth = dataSource.getAuth("Bobby");
PlayerAuth userAuth = dataSource.getAuth("user");
// then
assertThat(invalidAuth, nullValue());
assertThat(bobbyAuth, hasAuthBasicData("bobby", "Bobby", "your@email.com", "123.45.67.89"));
assertThat(bobbyAuth, hasAuthLocation(1.05, 2.1, 4.2, "world"));
assertThat(bobbyAuth.getLastLogin(), equalTo(1449136800L));
assertThat(bobbyAuth.getPassword(), equalToHash("$SHA$11aa0706173d7272$dbba966"));
assertThat(userAuth, hasAuthBasicData("user", "user", "user@example.org", "34.56.78.90"));
assertThat(userAuth, hasAuthLocation(124.1, 76.3, -127.8, "nether"));
assertThat(userAuth.getLastLogin(), equalTo(1453242857L));
assertThat(userAuth.getPassword(), equalToHash("b28c32f624a4eb161d6adc9acb5bfc5b", "f750ba32"));
}
@Test
public void shouldFindIfEmailExists() {
// given
DataSource dataSource = getDataSource();
// when
boolean isUserMailPresent = dataSource.isEmailStored("user@example.org");
boolean isUserMailPresentCaseInsensitive = dataSource.isEmailStored("user@example.ORG");
boolean isInvalidMailPresent = dataSource.isEmailStored("not-in-database@example.com");
// then
assertThat(isUserMailPresent, equalTo(true));
assertThat(isUserMailPresentCaseInsensitive, equalTo(true));
assertThat(isInvalidMailPresent, equalTo(false));
}
@Test
public void shouldCountAuthsByEmail() {
// given
DataSource dataSource = getDataSource();
// when
int userMailCount = dataSource.countAuthsByEmail("user@example.ORG");
int invalidMailCount = dataSource.countAuthsByEmail("not.in.db@example.com");
boolean response = dataSource.saveAuth(
PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").build());
int newUserCount = dataSource.countAuthsByEmail("user@Example.org");
// then
assertThat(userMailCount, equalTo(1));
assertThat(invalidMailCount, equalTo(0));
assertThat(response, equalTo(true));
assertThat(newUserCount, equalTo(2));
}
@Test
public void shouldReturnAllAuths() {
// given
DataSource dataSource = getDataSource();
// when
List<PlayerAuth> authList = dataSource.getAllAuths();
boolean response = dataSource.saveAuth(
PlayerAuth.builder().name("Test").email("user@EXAMPLE.org").build());
List<PlayerAuth> newAuthList = dataSource.getAllAuths();
// then
assertThat(response, equalTo(true));
assertThat(authList, hasSize(2));
assertThat(newAuthList, hasSize(3));
boolean hasBobby = false;
for (PlayerAuth auth : authList) {
if (auth.getNickname().equals("bobby")) {
hasBobby = true;
break;
}
}
assertThat(hasBobby, equalTo(true));
}
@Test
public void shouldUpdatePassword() {
// given
DataSource dataSource = getDataSource();
HashedPassword newHash = new HashedPassword("new_hash");
// when
boolean response1 = dataSource.updatePassword("user", newHash);
boolean response2 = dataSource.updatePassword("non-existent-name", new HashedPassword("sd"));
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getPassword("user"), equalToHash(newHash));
}
@Test
public void shouldRemovePlayerAuth() {
// given
DataSource dataSource = getDataSource();
// when
boolean response1 = dataSource.removeAuth("bobby");
boolean response2 = dataSource.removeAuth("does-not-exist");
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getAuth("bobby"), nullValue());
assertThat(dataSource.isAuthAvailable("bobby"), equalTo(false));
}
@Test
public void shouldUpdateSession() {
// given
DataSource dataSource = getDataSource();
PlayerAuth bobby = PlayerAuth.builder()
.name("bobby").realName("BOBBY").lastLogin(123L)
.ip("12.12.12.12").build();
// when
boolean response = dataSource.updateSession(bobby);
// then
assertThat(response, equalTo(true));
PlayerAuth result = dataSource.getAuth("bobby");
assertThat(result, hasAuthBasicData("bobby", "BOBBY", "your@email.com", "12.12.12.12"));
assertThat(result.getLastLogin(), equalTo(123L));
}
@Test
public void shouldUpdateLastLoc() {
// given
DataSource dataSource = getDataSource();
PlayerAuth user = PlayerAuth.builder()
.name("user").locX(143).locY(-42.12).locZ(29.47)
.locWorld("the_end").build();
// when
boolean response = dataSource.updateQuitLoc(user);
// then
assertThat(response, equalTo(true));
assertThat(dataSource.getAuth("user"), hasAuthLocation(143, -42.12, 29.47, "the_end"));
}
@Test
public void shouldDeletePlayers() {
// given
DataSource dataSource = getDataSource();
List<String> playersToDelete = Arrays.asList("bobby", "doesNotExist");
assumeThat(dataSource.getAccountsRegistered(), equalTo(2));
// when
dataSource.purgeBanned(playersToDelete);
// then
assertThat(dataSource.getAccountsRegistered(), equalTo(1));
assertThat(dataSource.isAuthAvailable("bobby"), equalTo(false));
assertThat(dataSource.isAuthAvailable("user"), equalTo(true));
}
@Test
public void shouldUpdateEmail() {
// given
DataSource dataSource = getDataSource();
String email = "new-user@mail.tld";
PlayerAuth userAuth = PlayerAuth.builder().name("user").email(email).build();
PlayerAuth invalidAuth = PlayerAuth.builder().name("invalid").email("addr@example.com").build();
// when
boolean response1 = dataSource.updateEmail(userAuth);
boolean response2 = dataSource.updateEmail(invalidAuth);
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("user", "user", email, "34.56.78.90")));
}
@Test
public void shouldUpdateIp() {
// given
DataSource dataSource = getDataSource();
String ip = "250.230.67.73";
// when
boolean response1 = dataSource.updateIp("bobby", ip);
boolean response2 = dataSource.updateIp("bogus", "123.123.123.123");
// then
assertThat(response1 && response2, equalTo(true));
assertThat(dataSource.getAllAuths(), hasItem(hasAuthBasicData("bobby", "Bobby", "your@email.com", ip)));
}
@Test
public void shouldCountAuths() {
// given
DataSource dataSource = getDataSource();
// when
int initialCount = dataSource.getAccountsRegistered();
for (int i = 0; i < 4; ++i) {
dataSource.saveAuth(PlayerAuth.builder().name("test-" + i).build());
}
int endCount = dataSource.getAccountsRegistered();
// then
assertThat(initialCount, equalTo(2));
assertThat(endCount, equalTo(6));
}
@Test
public void shouldGetAllUsersByIp() {
// given
DataSource dataSource = getDataSource();
// when
List<String> initialList = dataSource.getAllAuthsByIp("123.45.67.89");
List<String> emptyList = dataSource.getAllAuthsByIp("8.8.8.8");
for (int i = 0; i < 3; ++i) {
dataSource.saveAuth(PlayerAuth.builder().name("test-" + i).ip("123.45.67.89").build());
}
List<String> updatedList = dataSource.getAllAuthsByIp("123.45.67.89");
// then
assertThat(initialList, hasSize(1));
assertThat(initialList.get(0), equalTo("bobby"));
assertThat(emptyList, hasSize(0));
assertThat(updatedList, hasSize(4));
assertThat(updatedList, hasItem(equalTo("bobby")));
assertThat(updatedList, hasItem(equalTo("test-1")));
}
}

View File

@ -0,0 +1,84 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import java.util.Objects;
/**
* Custom matchers for AuthMe entities.
*/
public final class AuthMeMatchers {
private AuthMeMatchers() {
}
public static Matcher<? super HashedPassword> equalToHash(final String hash) {
return equalToHash(new HashedPassword(hash));
}
public static Matcher<? super HashedPassword> equalToHash(final String hash, final String salt) {
return equalToHash(new HashedPassword(hash, salt));
}
public static Matcher<? super HashedPassword> equalToHash(final HashedPassword hash) {
return new TypeSafeMatcher<HashedPassword>() {
@Override
public boolean matchesSafely(HashedPassword item) {
return Objects.equals(hash.getHash(), item.getHash())
&& Objects.equals(hash.getSalt(), item.getSalt());
}
@Override
public void describeTo(Description description) {
String representation = "'" + hash.getHash() + "'";
if (hash.getSalt() != null) {
representation += ", '" + hash.getSalt() + "'";
}
description.appendValue("HashedPassword(" + representation + ")");
}
};
}
public static Matcher<? super PlayerAuth> hasAuthBasicData(final String name, final String realName,
final String email, final String ip) {
return new TypeSafeMatcher<PlayerAuth>() {
@Override
public boolean matchesSafely(PlayerAuth item) {
return Objects.equals(name, item.getNickname())
&& Objects.equals(realName, item.getRealName())
&& Objects.equals(email, item.getEmail())
&& Objects.equals(ip, item.getIp());
}
@Override
public void describeTo(Description description) {
description.appendValue(String.format("PlayerAuth with name %s, realname %s, email %s, ip %s",
name, realName, email, ip));
}
};
}
public static Matcher<? super PlayerAuth> hasAuthLocation(final double x, final double y, final double z,
final String world) {
return new TypeSafeMatcher<PlayerAuth>() {
@Override
public boolean matchesSafely(PlayerAuth item) {
return Objects.equals(x, item.getQuitLocX())
&& Objects.equals(y, item.getQuitLocY())
&& Objects.equals(z, item.getQuitLocZ())
&& Objects.equals(world, item.getWorld());
}
@Override
public void describeTo(Description description) {
description.appendValue(String.format("PlayerAuth with quit location (x: %f, y: %f, z: %f, world: %s)",
x, y, z, world));
}
};
}
}

View File

@ -0,0 +1,96 @@
package fr.xephi.authme.datasource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Integration test for {@link MySQL}.
*/
public class MySqlIntegrationTest extends AbstractDataSourceIntegrationTest {
/** Mock of a settings instance. */
private static NewSetting settings;
/** SQL statement to execute before running a test. */
private static String sqlInitialize;
/** Connection to the H2 test database. */
private HikariDataSource hikariSource;
/**
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
*/
@BeforeClass
public static void initializeSettings() throws IOException, ClassNotFoundException {
// Check that we have an H2 driver
Class.forName("org.h2.jdbcx.JdbcDataSource");
settings = mock(NewSetting.class);
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((Property) invocation.getArguments()[0]).getDefaultValue();
}
});
set(DatabaseSettings.MYSQL_DATABASE, "h2_test");
set(DatabaseSettings.MYSQL_TABLE, "authme");
set(DatabaseSettings.MYSQL_COL_SALT, "salt");
ConsoleLoggerTestInitializer.setupLogger();
Path sqlInitFile = TestHelper.getJarPath("/datasource-integration/sql-initialize.sql");
sqlInitialize = new String(Files.readAllBytes(sqlInitFile));
}
@Before
public void initializeConnectionAndTable() throws SQLException {
silentClose(hikariSource);
HikariConfig config = new HikariConfig();
config.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
config.setConnectionTestQuery("VALUES 1");
config.addDataSourceProperty("URL", "jdbc:h2:mem:test");
config.addDataSourceProperty("user", "sa");
config.addDataSourceProperty("password", "sa");
HikariDataSource ds = new HikariDataSource(config);
Connection connection = ds.getConnection();
try (Statement st = connection.createStatement()) {
st.execute("DROP TABLE IF EXISTS authme");
st.execute(sqlInitialize);
}
hikariSource = ds;
}
@Override
protected DataSource getDataSource() {
return new MySQL(settings, hikariSource);
}
private static <T> void set(Property<T> property, T value) {
when(settings.getProperty(property)).thenReturn(value);
}
private static void silentClose(HikariDataSource con) {
if (con != null && !con.isClosed()) {
con.close();
}
}
}

View File

@ -0,0 +1,96 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.ConsoleLoggerTestInitializer;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.settings.NewSetting;
import fr.xephi.authme.settings.domain.Property;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Integration test for {@link SQLite}.
*/
public class SQLiteIntegrationTest extends AbstractDataSourceIntegrationTest {
/** Mock of a settings instance. */
private static NewSetting settings;
/** Collection of SQL statements to execute for initialization of a test. */
private static String[] sqlInitialize;
/** Connection to the SQLite test database. */
private Connection con;
/**
* Set up the settings mock to return specific values for database settings and load {@link #sqlInitialize}.
*/
@BeforeClass
public static void initializeSettings() throws IOException, ClassNotFoundException {
// Check that we have an implementation for SQLite
Class.forName("org.sqlite.JDBC");
settings = mock(NewSetting.class);
when(settings.getProperty(any(Property.class))).thenAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((Property) invocation.getArguments()[0]).getDefaultValue();
}
});
set(DatabaseSettings.MYSQL_DATABASE, "sqlite-test");
set(DatabaseSettings.MYSQL_TABLE, "authme");
set(DatabaseSettings.MYSQL_COL_SALT, "salt");
ConsoleLoggerTestInitializer.setupLogger();
Path sqlInitFile = TestHelper.getJarPath("/datasource-integration/sql-initialize.sql");
// Note ljacqu 20160221: It appears that we can only run one statement per Statement.execute() so we split
// the SQL file by ";\n" as to get the individual statements
sqlInitialize = new String(Files.readAllBytes(sqlInitFile)).split(";(\\r?)\\n");
}
@Before
public void initializeConnectionAndTable() throws SQLException {
silentClose(con);
Connection connection = DriverManager.getConnection("jdbc:sqlite::memory:");
try (Statement st = connection.createStatement()) {
st.execute("DROP TABLE IF EXISTS authme");
for (String statement : sqlInitialize) {
st.execute(statement);
}
}
con = connection;
}
@Override
protected DataSource getDataSource() {
return new SQLite(settings, con);
}
private static <T> void set(Property<T> property, T value) {
when(settings.getProperty(property)).thenReturn(value);
}
private static void silentClose(Connection con) {
if (con != null) {
try {
if (!con.isClosed()) {
con.close();
}
} catch (SQLException e) {
// silent
}
}
}
}

View File

@ -266,4 +266,16 @@ public class MessagesIntegrationTest {
assertThat(messages.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE), assertThat(messages.retrieveSingle(MessageKey.MUST_REGISTER_MESSAGE),
equalTo("Message from default file")); equalTo("Message from default file"));
} }
@Test
public void shouldRetrieveMessageWithReplacements() {
// given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
// when
String result = messages.retrieveSingle(key, "24680");
// then
assertThat(result, equalTo("Use /captcha 24680 to solve the captcha"));
}
} }

View File

@ -0,0 +1,22 @@
-- Important: separate SQL statements by ; followed directly by a newline. We split the file contents by ";\n"
CREATE TABLE authme (
id INTEGER AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
ip VARCHAR(40) NOT NULL,
lastlogin BIGINT,
x DOUBLE NOT NULL DEFAULT '0.0',
y DOUBLE NOT NULL DEFAULT '0.0',
z DOUBLE NOT NULL DEFAULT '0.0',
world VARCHAR(255) NOT NULL DEFAULT 'world',
email VARCHAR(255) DEFAULT 'your@email.com',
isLogged INT DEFAULT '0', realname VARCHAR(255) NOT NULL DEFAULT 'Player',
salt varchar(255),
CONSTRAINT table_const_prim PRIMARY KEY (id)
);
INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, email, isLogged, realname, salt)
VALUES (1,'bobby','$SHA$11aa0706173d7272$dbba966','123.45.67.89',1449136800,1.05,2.1,4.2,'world','your@email.com',0,'Bobby',NULL);
INSERT INTO authme (id, username, password, ip, lastlogin, x, y, z, world, email, isLogged, realname, salt)
VALUES (NULL,'user','b28c32f624a4eb161d6adc9acb5bfc5b','34.56.78.90',1453242857,124.1,76.3,-127.8,'nether','user@example.org',0,'user','f750ba32');