Package cleanup

- authme.cache to authme.data
- Rename PlayerData to LimboPlayer to match with LimboCache
- Move authme.converter to authme.datasource.converter
- Split output package into output and message
This commit is contained in:
Gabriele C
2016-10-04 21:49:14 +02:00
committed by ljacqu
parent 7e2912cc60
commit 58c42cf300
149 changed files with 491 additions and 475 deletions
@@ -0,0 +1,132 @@
package fr.xephi.authme.data;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.util.RandomStringUtils;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import javax.inject.Inject;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manager for the handling of captchas.
*/
public class CaptchaManager implements SettingsDependent {
private final ConcurrentHashMap<String, Integer> playerCounts;
private final ConcurrentHashMap<String, String> captchaCodes;
private boolean isEnabled;
private int threshold;
private int captchaLength;
@Inject
CaptchaManager(Settings settings) {
this.playerCounts = new ConcurrentHashMap<>();
this.captchaCodes = new ConcurrentHashMap<>();
reload(settings);
}
/**
* Increases the failure count for the given player.
*
* @param name the player's name
*/
public void increaseCount(String name) {
if (isEnabled) {
String playerLower = name.toLowerCase();
Integer currentCount = playerCounts.get(playerLower);
if (currentCount == null) {
playerCounts.put(playerLower, 1);
} else {
playerCounts.put(playerLower, currentCount + 1);
}
}
}
/**
* Returns whether the given player is required to solve a captcha.
*
* @param name the name of the player to verify
* @return true if the player has to solve a captcha, false otherwise
*/
public boolean isCaptchaRequired(String name) {
if (isEnabled) {
Integer count = playerCounts.get(name.toLowerCase());
return count != null && count >= threshold;
}
return false;
}
/**
* Returns the stored captcha code for the player.
*
* @param name the player's name
* @return the code the player is required to enter, or null if none registered
*/
public String getCaptchaCode(String name) {
return captchaCodes.get(name.toLowerCase());
}
/**
* Returns the stored captcha for the player or generates and saves a new one.
*
* @param name the player's name
* @return the code the player is required to enter
*/
public String getCaptchaCodeOrGenerateNew(String name) {
String code = getCaptchaCode(name);
return code == null ? generateCode(name) : code;
}
/**
* Generates a code for the player and returns it.
*
* @param name the name of the player to generate a code for
* @return the generated code
*/
public String generateCode(String name) {
String code = RandomStringUtils.generate(captchaLength);
captchaCodes.put(name.toLowerCase(), code);
return code;
}
/**
* Checks the given code against the existing one and resets the player's auth failure count upon success.
*
* @param name the name of the player to check
* @param code the supplied code
* @return true if the code matches or if no captcha is required for the player, false otherwise
*/
public boolean checkCode(String name, String code) {
String savedCode = captchaCodes.get(name.toLowerCase());
if (savedCode == null) {
return true;
} else if (savedCode.equalsIgnoreCase(code)) {
captchaCodes.remove(name.toLowerCase());
playerCounts.remove(name.toLowerCase());
return true;
}
return false;
}
/**
* Resets the login count of the given player to 0.
*
* @param name the player's name
*/
public void resetCounts(String name) {
if (isEnabled) {
captchaCodes.remove(name.toLowerCase());
playerCounts.remove(name.toLowerCase());
}
}
@Override
public void reload(Settings settings) {
this.isEnabled = settings.getProperty(SecuritySettings.USE_CAPTCHA);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TRIES_BEFORE_CAPTCHA);
this.captchaLength = settings.getProperty(SecuritySettings.CAPTCHA_LENGTH);
}
}
@@ -0,0 +1,97 @@
package fr.xephi.authme.data;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import javax.inject.Inject;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE;
/**
* Manages sessions, allowing players to be automatically logged in if they join again
* within a configurable amount of time.
*/
public class SessionManager implements SettingsDependent, HasCleanup {
// Player -> expiration of session in milliseconds
private final Map<String, Long> sessions = new ConcurrentHashMap<>();
private boolean enabled;
private int timeoutInMinutes;
@Inject
SessionManager(Settings settings) {
reload(settings);
}
/**
* Check if a session is available for the given player.
*
* @param name The name to check.
* @return True if a session is found.
*/
public boolean hasSession(String name) {
if (enabled) {
Long timeout = sessions.get(name.toLowerCase());
if (timeout != null) {
return System.currentTimeMillis() <= timeout;
}
}
return false;
}
/**
* Add a player session to the cache.
*
* @param name The name of the player.
*/
public void addSession(String name) {
if (enabled) {
long timeout = System.currentTimeMillis() + timeoutInMinutes * MILLIS_PER_MINUTE;
sessions.put(name.toLowerCase(), timeout);
}
}
/**
* Remove a player's session from the cache.
*
* @param name The name of the player.
*/
public void removeSession(String name) {
this.sessions.remove(name.toLowerCase());
}
@Override
public void reload(Settings settings) {
timeoutInMinutes = settings.getProperty(PluginSettings.SESSIONS_TIMEOUT);
boolean oldEnabled = enabled;
enabled = timeoutInMinutes > 0 && settings.getProperty(PluginSettings.SESSIONS_ENABLED);
// With this reload, the sessions feature has just been disabled, so clear all stored sessions
if (oldEnabled && !enabled) {
sessions.clear();
ConsoleLogger.fine("Sessions disabled: cleared all sessions");
}
}
@Override
public void performCleanup() {
if (!enabled) {
return;
}
final long currentTime = System.currentTimeMillis();
Iterator<Map.Entry<String, Long>> iterator = sessions.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Long> entry = iterator.next();
if (entry.getValue() < currentTime) {
iterator.remove();
}
}
}
}
@@ -0,0 +1,192 @@
package fr.xephi.authme.data;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.initialization.HasCleanup;
import fr.xephi.authme.initialization.SettingsDependent;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static fr.xephi.authme.settings.properties.SecuritySettings.TEMPBAN_MINUTES_BEFORE_RESET;
import static fr.xephi.authme.util.Utils.MILLIS_PER_MINUTE;
/**
* Manager for handling temporary bans.
*/
public class TempbanManager implements SettingsDependent, HasCleanup {
private final Map<String, Map<String, TimedCounter>> ipLoginFailureCounts;
private final BukkitService bukkitService;
private final Messages messages;
private boolean isEnabled;
private int threshold;
private int length;
private long resetThreshold;
@Inject
TempbanManager(BukkitService bukkitService, Messages messages, Settings settings) {
this.ipLoginFailureCounts = new ConcurrentHashMap<>();
this.bukkitService = bukkitService;
this.messages = messages;
reload(settings);
}
/**
* Increases the failure count for the given IP address/username combination.
*
* @param address The player's IP address
* @param name The username
*/
public void increaseCount(String address, String name) {
if (isEnabled) {
Map<String, TimedCounter> countsByName = ipLoginFailureCounts.get(address);
if (countsByName == null) {
countsByName = new ConcurrentHashMap<>();
ipLoginFailureCounts.put(address, countsByName);
}
TimedCounter counter = countsByName.get(name);
if (counter == null) {
countsByName.put(name, new TimedCounter(1));
} else {
counter.increment(resetThreshold);
}
}
}
/**
* Set the failure count for a given IP address / username combination to 0.
*
* @param address The IP address
* @param name The username
*/
public void resetCount(String address, String name) {
if (isEnabled) {
Map<String, TimedCounter> map = ipLoginFailureCounts.get(address);
if (map != null) {
map.remove(name);
}
}
}
/**
* Return whether the IP address should be tempbanned.
*
* @param address The player's IP address
* @return True if the IP should be tempbanned
*/
public boolean shouldTempban(String address) {
if (isEnabled) {
Map<String, TimedCounter> countsByName = ipLoginFailureCounts.get(address);
if (countsByName != null) {
int total = 0;
for (TimedCounter counter : countsByName.values()) {
total += counter.getCount(resetThreshold);
}
return total >= threshold;
}
}
return false;
}
/**
* Tempban a player's IP address for failing to log in too many times.
* This calculates the expire time based on the time the method was called.
*
* @param player The player to tempban
*/
public void tempbanPlayer(final Player player) {
if (isEnabled) {
final String ip = PlayerUtils.getPlayerIp(player);
final String reason = messages.retrieveSingle(MessageKey.TEMPBAN_MAX_LOGINS);
final Date expires = new Date();
long newTime = expires.getTime() + (length * MILLIS_PER_MINUTE);
expires.setTime(newTime);
bukkitService.scheduleSyncDelayedTask(new Runnable() {
@Override
public void run() {
bukkitService.banIp(ip, reason, expires, "AuthMe");
player.kickPlayer(reason);
}
});
ipLoginFailureCounts.remove(ip);
}
}
@Override
public void reload(Settings settings) {
this.isEnabled = settings.getProperty(SecuritySettings.TEMPBAN_ON_MAX_LOGINS);
this.threshold = settings.getProperty(SecuritySettings.MAX_LOGIN_TEMPBAN);
this.length = settings.getProperty(SecuritySettings.TEMPBAN_LENGTH);
this.resetThreshold = settings.getProperty(TEMPBAN_MINUTES_BEFORE_RESET) * MILLIS_PER_MINUTE;
}
@Override
public void performCleanup() {
for (Map<String, TimedCounter> countsByIp : ipLoginFailureCounts.values()) {
Iterator<TimedCounter> it = countsByIp.values().iterator();
while (it.hasNext()) {
TimedCounter counter = it.next();
if (counter.getCount(resetThreshold) == 0) {
it.remove();
}
}
}
}
/**
* Counter with an associated timestamp, keeping track of when the last entry has been added.
*/
@VisibleForTesting
static final class TimedCounter {
private int counter;
private long lastIncrementTimestamp = System.currentTimeMillis();
/**
* Constructor.
*
* @param start the initial value to set the counter to
*/
TimedCounter(int start) {
this.counter = start;
}
/**
* Returns the count, taking into account the last entry timestamp.
*
* @param threshold the threshold in milliseconds until when to consider a counter
* @return the counter's value, or {@code 0} if it was last incremented longer ago than the threshold
*/
int getCount(long threshold) {
if (System.currentTimeMillis() - lastIncrementTimestamp > threshold) {
return 0;
}
return counter;
}
/**
* Increments the counter, taking into account the last entry timestamp.
*
* @param threshold in milliseconds, the time span until which to consider the existing number
*/
void increment(long threshold) {
counter = getCount(threshold) + 1;
lastIncrementTimestamp = System.currentTimeMillis();
}
}
}
@@ -0,0 +1,327 @@
package fr.xephi.authme.data.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.Preconditions.checkNotNull;
/**
* AuthMe player data.
*/
public class PlayerAuth {
/** The player's name in lowercase, e.g. "xephi". */
private String nickname;
/** The player's name in the correct casing, e.g. "Xephi". */
private String realName;
private HashedPassword password;
private String email;
private String ip;
private int groupId;
private long lastLogin;
// Fields storing the player's quit location
private double x;
private double y;
private double z;
private String world;
/**
* @param serialized String
*/
public PlayerAuth(String serialized) {
this.deserialize(serialized);
}
/**
* Constructor. Instantiate objects with the {@link #builder() builder}.
*
* @param nickname all lowercase name of the player
* @param password password
* @param groupId the group id
* @param ip the associated ip address
* @param lastLogin player's last login (timestamp)
* @param x quit location: x coordinate
* @param y quit location: y coordinate
* @param z quit location: z coordinate
* @param world quit location: world name
* @param email the associated email
* @param realName the player's name with proper casing
*/
private PlayerAuth(String nickname, HashedPassword password, int groupId, String ip, long lastLogin,
double x, double y, double z, String world, String email, String realName) {
this.nickname = nickname.toLowerCase();
this.password = password;
this.ip = ip;
this.lastLogin = lastLogin;
this.x = x;
this.y = y;
this.z = z;
this.world = world;
this.groupId = groupId;
this.email = email;
this.realName = realName;
}
public void setNickname(String nickname) {
this.nickname = nickname.toLowerCase();
}
public String getNickname() {
return nickname;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public int getGroupId() {
return groupId;
}
public void setQuitLocation(Location location) {
x = location.getBlockX();
y = location.getBlockY();
z = location.getBlockZ();
world = location.getWorld().getName();
}
public double getQuitLocX() {
return x;
}
public void setQuitLocX(double d) {
this.x = d;
}
public double getQuitLocY() {
return y;
}
public void setQuitLocY(double d) {
this.y = d;
}
public double getQuitLocZ() {
return z;
}
public void setQuitLocZ(double d) {
this.z = d;
}
public String getWorld() {
return world;
}
public void setWorld(String world) {
this.world = world;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public long getLastLogin() {
return lastLogin;
}
public void setLastLogin(long lastLogin) {
this.lastLogin = lastLogin;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public HashedPassword getPassword() {
return password;
}
public void setPassword(HashedPassword password) {
this.password = password;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PlayerAuth)) {
return false;
}
PlayerAuth other = (PlayerAuth) obj;
return other.getIp().equals(this.ip) && other.getNickname().equals(this.nickname);
}
@Override
public int hashCode() {
int hashCode = 7;
hashCode = 71 * hashCode + (this.nickname != null ? this.nickname.hashCode() : 0);
hashCode = 71 * hashCode + (this.ip != null ? this.ip.hashCode() : 0);
return hashCode;
}
@Override
public String toString() {
return "Player : " + nickname + " | " + realName
+ " ! IP : " + ip
+ " ! LastLogin : " + lastLogin
+ " ! LastPosition : " + x + "," + y + "," + z + "," + world
+ " ! Email : " + email
+ " ! Password : {" + password.getHash() + ", " + password.getSalt() + "}";
}
/**
* Method to serialize PlayerAuth
*
* @return String
*/
public String serialize() {
StringBuffer str = new StringBuffer();
char d = ';';
str.append(this.nickname).append(d);
str.append(this.realName).append(d);
str.append(this.ip).append(d);
str.append(this.email).append(d);
str.append(this.password.getHash()).append(d);
str.append(this.password.getSalt()).append(d);
str.append(this.groupId).append(d);
str.append(this.lastLogin).append(d);
str.append(this.world).append(d);
str.append(this.x).append(d);
str.append(this.y).append(d);
str.append(this.z);
return str.toString();
}
/**
* Method to deserialize PlayerAuth
*
* @param str String
*/
public void deserialize(String str) {
String[] args = str.split(";");
this.nickname = args[0];
this.realName = args[1];
this.ip = args[2];
this.email = args[3];
this.password = new HashedPassword(args[4], args[5]);
this.groupId = Integer.parseInt(args[6]);
this.lastLogin = Long.parseLong(args[7]);
this.world = args[8];
this.x = Double.parseDouble(args[9]);
this.y = Double.parseDouble(args[10]);
this.z = Double.parseDouble(args[11]);
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private String name;
private String realName;
private HashedPassword password;
private String ip;
private String world;
private String email;
private int groupId = -1;
private double x = 0.0f;
private double y = 0.0f;
private double z = 0.0f;
private long lastLogin = System.currentTimeMillis();
public PlayerAuth build() {
return new PlayerAuth(
checkNotNull(name),
firstNonNull(password, new HashedPassword("")),
groupId,
firstNonNull(ip, "127.0.0.1"),
lastLogin,
x, y, z,
firstNonNull(world, "world"),
firstNonNull(email, "your@email.com"),
firstNonNull(realName, "Player")
);
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder realName(String realName) {
this.realName = realName;
return this;
}
public Builder password(HashedPassword password) {
this.password = password;
return this;
}
public Builder password(String hash, String salt) {
return password(new HashedPassword(hash, salt));
}
public Builder ip(String ip) {
this.ip = ip;
return this;
}
public Builder location(Location location) {
this.x = location.getX();
this.y = location.getY();
this.z = location.getZ();
this.world = location.getWorld().getName();
return this;
}
public Builder locWorld(String world) {
this.world = world;
return this;
}
public Builder locX(double x) {
this.x = x;
return this;
}
public Builder locY(double y) {
this.y = y;
return this;
}
public Builder locZ(double z) {
this.z = z;
return this;
}
public Builder lastLogin(long lastLogin) {
this.lastLogin = lastLogin;
return this;
}
public Builder groupId(int groupId) {
this.groupId = groupId;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
}
}
@@ -0,0 +1,98 @@
package fr.xephi.authme.data.auth;
import java.util.concurrent.ConcurrentHashMap;
/**
* Used to manage player's Authenticated status
*/
public class PlayerCache {
private volatile static PlayerCache singleton;
private final ConcurrentHashMap<String, PlayerAuth> cache;
private PlayerCache() {
cache = new ConcurrentHashMap<>();
}
/**
* Method getInstance.
*
* @return PlayerCache
*/
public static PlayerCache getInstance() {
if (singleton == null) {
singleton = new PlayerCache();
}
return singleton;
}
/**
* Method addPlayer.
*
* @param auth PlayerAuth
*/
public void addPlayer(PlayerAuth auth) {
cache.put(auth.getNickname().toLowerCase(), auth);
}
/**
* Method updatePlayer.
*
* @param auth PlayerAuth
*/
public void updatePlayer(PlayerAuth auth) {
cache.put(auth.getNickname(), auth);
}
/**
* Method removePlayer.
*
* @param user String
*/
public void removePlayer(String user) {
cache.remove(user.toLowerCase());
}
/**
* get player's authenticated status.
*
* @param user player's name
*
* @return true if player is logged in, false otherwise.
*/
public boolean isAuthenticated(String user) {
return cache.containsKey(user.toLowerCase());
}
/**
* Method getAuth.
*
* @param user String
*
* @return PlayerAuth
*/
public PlayerAuth getAuth(String user) {
return cache.get(user.toLowerCase());
}
/**
* Method getLogged.
*
* @return int
*/
public int getLogged() {
return cache.size();
}
/**
* Method getCache.
*
* @return ConcurrentHashMap
*/
public ConcurrentHashMap<String, PlayerAuth> getCache() {
return this.cache;
}
}
@@ -0,0 +1,214 @@
package fr.xephi.authme.data.backup;
import com.google.common.io.Files;
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.data.limbo.LimboPlayer;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
/**
* Class used to store player's data (OP, flying, speed, position) to disk.
*/
public class LimboPlayerStorage {
private final Gson gson;
private final File cacheDir;
private PermissionsManager permissionsManager;
private SpawnLoader spawnLoader;
private BukkitService bukkitService;
@Inject
LimboPlayerStorage(@DataFolder File dataFolder, PermissionsManager permsMan,
SpawnLoader spawnLoader, BukkitService bukkitService) {
this.permissionsManager = permsMan;
this.spawnLoader = spawnLoader;
this.bukkitService = bukkitService;
cacheDir = new File(dataFolder, "playerdata");
if (!cacheDir.exists() && !cacheDir.isDirectory() && !cacheDir.mkdir()) {
ConsoleLogger.warning("Failed to create userdata directory.");
}
gson = new GsonBuilder()
.registerTypeAdapter(LimboPlayer.class, new LimboPlayerSerializer())
.registerTypeAdapter(LimboPlayer.class, new LimboPlayerDeserializer())
.setPrettyPrinting()
.create();
}
/**
* Read and construct new PlayerData from existing player data.
*
* @param player player to read
*
* @return PlayerData object if the data is exist, null otherwise.
*/
public LimboPlayer readData(Player player) {
String id = PlayerUtils.getUUIDorName(player);
File file = new File(cacheDir, id + File.separator + "data.json");
if (!file.exists()) {
return null;
}
try {
String str = Files.toString(file, StandardCharsets.UTF_8);
return gson.fromJson(str, LimboPlayer.class);
} catch (IOException e) {
ConsoleLogger.logException("Could not read player data on disk for '" + player.getName() + "'", e);
return null;
}
}
/**
* Save player data (OP, flying, location, etc) to disk.
*
* @param player player to save
*/
public void saveData(Player player) {
String id = PlayerUtils.getUUIDorName(player);
Location location = spawnLoader.getPlayerLocationOrSpawn(player);
String group = "";
if (permissionsManager.hasGroupSupport()) {
group = permissionsManager.getPrimaryGroup(player);
}
boolean operator = player.isOp();
boolean canFly = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
LimboPlayer limboPlayer = new LimboPlayer(location, operator, group, canFly, walkSpeed, flySpeed);
try {
File file = new File(cacheDir, id + File.separator + "data.json");
Files.createParentDirs(file);
Files.touch(file);
Files.write(gson.toJson(limboPlayer), file, StandardCharsets.UTF_8);
} catch (IOException e) {
ConsoleLogger.logException("Failed to write " + player.getName() + " data.", e);
}
}
/**
* Remove player data, this will delete
* "playerdata/&lt;uuid or name&gt;/" folder from disk.
*
* @param player player to remove
*/
public void removeData(Player player) {
String id = PlayerUtils.getUUIDorName(player);
File file = new File(cacheDir, id);
if (file.exists()) {
FileUtils.purgeDirectory(file);
if (!file.delete()) {
ConsoleLogger.warning("Failed to remove " + player.getName() + " cache.");
}
}
}
/**
* Use to check is player data is exist.
*
* @param player player to check
*
* @return true if data exist, false otherwise.
*/
public boolean hasData(Player player) {
String id = PlayerUtils.getUUIDorName(player);
File file = new File(cacheDir, id + File.separator + "data.json");
return file.exists();
}
private class LimboPlayerDeserializer implements JsonDeserializer<LimboPlayer> {
@Override
public LimboPlayer deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext context) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject == null) {
return null;
}
Location loc = null;
String group = "";
boolean operator = false;
boolean canFly = false;
float walkSpeed = 0.2f;
float flySpeed = 0.2f;
JsonElement e;
if ((e = jsonObject.getAsJsonObject("location")) != null) {
JsonObject obj = e.getAsJsonObject();
World world = bukkitService.getWorld(obj.get("world").getAsString());
if (world != null) {
double x = obj.get("x").getAsDouble();
double y = obj.get("y").getAsDouble();
double z = obj.get("z").getAsDouble();
float yaw = obj.get("yaw").getAsFloat();
float pitch = obj.get("pitch").getAsFloat();
loc = new Location(world, x, y, z, yaw, pitch);
}
}
if ((e = jsonObject.get("group")) != null) {
group = e.getAsString();
}
if ((e = jsonObject.get("operator")) != null) {
operator = e.getAsBoolean();
}
if ((e = jsonObject.get("can-fly")) != null) {
canFly = e.getAsBoolean();
}
if ((e = jsonObject.get("walk-speed")) != null) {
walkSpeed = e.getAsFloat();
}
if ((e = jsonObject.get("fly-speed")) != null) {
flySpeed = e.getAsFloat();
}
return new LimboPlayer(loc, operator, group, canFly, walkSpeed, flySpeed);
}
}
private class LimboPlayerSerializer implements JsonSerializer<LimboPlayer> {
@Override
public JsonElement serialize(LimboPlayer limboPlayer, Type type,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty("group", limboPlayer.getGroup());
Location loc = limboPlayer.getLocation();
JsonObject obj2 = new JsonObject();
obj2.addProperty("world", loc.getWorld().getName());
obj2.addProperty("x", loc.getX());
obj2.addProperty("y", loc.getY());
obj2.addProperty("z", loc.getZ());
obj2.addProperty("yaw", loc.getYaw());
obj2.addProperty("pitch", loc.getPitch());
obj.add("location", obj2);
obj.addProperty("operator", limboPlayer.isOperator());
obj.addProperty("can-fly", limboPlayer.isCanFly());
obj.addProperty("walk-speed", limboPlayer.getWalkSpeed());
obj.addProperty("fly-speed", limboPlayer.getFlySpeed());
return obj;
}
}
}
@@ -0,0 +1,164 @@
package fr.xephi.authme.data.limbo;
import fr.xephi.authme.data.backup.LimboPlayerStorage;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Manages all {@link LimboPlayer} instances.
*/
public class LimboCache {
private final Map<String, LimboPlayer> cache = new ConcurrentHashMap<>();
private LimboPlayerStorage limboPlayerStorage;
private Settings settings;
private PermissionsManager permissionsManager;
private SpawnLoader spawnLoader;
@Inject
LimboCache(Settings settings, PermissionsManager permissionsManager,
SpawnLoader spawnLoader, LimboPlayerStorage limboPlayerStorage) {
this.settings = settings;
this.permissionsManager = permissionsManager;
this.spawnLoader = spawnLoader;
this.limboPlayerStorage = limboPlayerStorage;
}
/**
* Load player data if exist, otherwise current player's data will be stored.
*
* @param player Player instance to add.
*/
public void addPlayerData(Player player) {
String name = player.getName().toLowerCase();
Location location = spawnLoader.getPlayerLocationOrSpawn(player);
boolean operator = player.isOp();
boolean flyEnabled = player.getAllowFlight();
float walkSpeed = player.getWalkSpeed();
float flySpeed = player.getFlySpeed();
String playerGroup = "";
if (permissionsManager.hasGroupSupport()) {
playerGroup = permissionsManager.getPrimaryGroup(player);
}
if (limboPlayerStorage.hasData(player)) {
LimboPlayer cache = limboPlayerStorage.readData(player);
if (cache != null) {
location = cache.getLocation();
playerGroup = cache.getGroup();
operator = cache.isOperator();
flyEnabled = cache.isCanFly();
walkSpeed = cache.getWalkSpeed();
flySpeed = cache.getFlySpeed();
}
} else {
limboPlayerStorage.saveData(player);
}
cache.put(name, new LimboPlayer(location, operator, playerGroup, flyEnabled, walkSpeed, flySpeed));
}
/**
* Restore player's data to player if exist.
*
* @param player Player instance to restore
*/
public void restoreData(Player player) {
String lowerName = player.getName().toLowerCase();
if (cache.containsKey(lowerName)) {
LimboPlayer data = cache.get(lowerName);
player.setOp(data.isOperator());
player.setAllowFlight(data.isCanFly());
float walkSpeed = data.getWalkSpeed();
float flySpeed = data.getFlySpeed();
// Reset the speed value if it was 0
if(walkSpeed == 0f) {
walkSpeed = 0.2f;
}
if(flySpeed == 0f) {
flySpeed = 0.2f;
}
player.setWalkSpeed(walkSpeed);
player.setFlySpeed(flySpeed);
restoreGroup(player, data.getGroup());
data.clearTasks();
}
}
/**
* Remove PlayerData from cache and disk.
*
* @param player Player player to remove.
*/
public void deletePlayerData(Player player) {
removeFromCache(player);
limboPlayerStorage.removeData(player);
}
/**
* Remove PlayerData from cache.
*
* @param player player to remove.
*/
public void removeFromCache(Player player) {
String name = player.getName().toLowerCase();
LimboPlayer cachedPlayer = cache.remove(name);
if (cachedPlayer != null) {
cachedPlayer.clearTasks();
}
}
/**
* Method getPlayerData.
*
* @param name String
*
* @return PlayerData
*/
public LimboPlayer getPlayerData(String name) {
checkNotNull(name);
return cache.get(name.toLowerCase());
}
/**
* Method hasPlayerData.
*
* @param name String
*
* @return boolean
*/
public boolean hasPlayerData(String name) {
checkNotNull(name);
return cache.containsKey(name.toLowerCase());
}
/**
* Method updatePlayerData.
*
* @param player Player
*/
public void updatePlayerData(Player player) {
checkNotNull(player);
removeFromCache(player);
addPlayerData(player);
}
private void restoreGroup(Player player, String group) {
if (!StringUtils.isEmpty(group) && permissionsManager.hasGroupSupport()
&& settings.getProperty(PluginSettings.ENABLE_PERMISSION_CHECK)) {
permissionsManager.setGroup(player, group);
}
}
}
@@ -0,0 +1,126 @@
package fr.xephi.authme.data.limbo;
import org.bukkit.Location;
import org.bukkit.scheduler.BukkitTask;
/**
* Represents a player which is not logged in and keeps track of certain states (like OP status, flying)
* which may be revoked from the player until he has logged in or registered.
*/
public class LimboPlayer {
private final boolean canFly;
private final boolean operator;
private final String group;
private final Location loc;
private final float walkSpeed;
private final float flySpeed;
private BukkitTask timeoutTask = null;
private BukkitTask messageTask = null;
public LimboPlayer(Location loc, boolean operator, String group, boolean fly, float walkSpeed, float flySpeed) {
this.loc = loc;
this.operator = operator;
this.group = group;
this.canFly = fly;
this.walkSpeed = walkSpeed;
this.flySpeed = flySpeed;
}
/**
* Return the player's original location.
*
* @return The player's location
*/
public Location getLocation() {
return loc;
}
/**
* Return whether the player is an operator or not (i.e. whether he is an OP).
*
* @return True if the player has OP status, false otherwise
*/
public boolean isOperator() {
return operator;
}
/**
* Return the player's permissions group.
*
* @return The permissions group the player belongs to
*/
public String getGroup() {
return group;
}
public boolean isCanFly() {
return canFly;
}
public float getWalkSpeed() {
return walkSpeed;
}
public float getFlySpeed() {
return flySpeed;
}
/**
* Return the timeout task, which kicks the player if he hasn't registered or logged in
* after a configurable amount of time.
*
* @return The timeout task associated to the player
*/
public BukkitTask getTimeoutTask() {
return timeoutTask;
}
/**
* Set the timeout task of the player. The timeout task kicks the player after a configurable
* amount of time if he hasn't logged in or registered.
*
* @param timeoutTask The task to set
*/
public void setTimeoutTask(BukkitTask timeoutTask) {
if (this.timeoutTask != null) {
this.timeoutTask.cancel();
}
this.timeoutTask = timeoutTask;
}
/**
* Return the message task reminding the player to log in or register.
*
* @return The task responsible for sending the message regularly
*/
public BukkitTask getMessageTask() {
return messageTask;
}
/**
* Set the messages task responsible for telling the player to log in or register.
*
* @param messageTask The message task to set
*/
public void setMessageTask(BukkitTask messageTask) {
if (this.messageTask != null) {
this.messageTask.cancel();
}
this.messageTask = messageTask;
}
/**
* Clears all tasks associated to the player.
*/
public void clearTasks() {
if (messageTask != null) {
messageTask.cancel();
}
messageTask = null;
if (timeoutTask != null) {
timeoutTask.cancel();
}
timeoutTask = null;
}
}