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:
@@ -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/<uuid or name>/" 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user