Merge branch 'master' of https://github.com/AuthMe-Team/AuthMeReloaded into commands-refactor

Conflicts:
	src/main/java/fr/xephi/authme/command/CommandUtils.java
	src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java
This commit is contained in:
ljacqu 2015-12-17 22:32:26 +01:00
commit d871939793
38 changed files with 375 additions and 182 deletions

View File

@ -90,6 +90,14 @@
<include>plugin.yml</include>
</includes>
</resource>
<resource>
<targetPath>.</targetPath>
<filtering>true</filtering>
<directory>src/main/resources/</directory>
<includes>
<include>email.html</include>
</includes>
</resource>
<resource>
<targetPath>.</targetPath>
<filtering>false</filtering>

View File

@ -203,7 +203,7 @@ public class DataManager {
} catch (Exception ignored) {
}
}
ConsoleLogger.info("AutoPurgeDatabase : Removed " + i + " permissions");
ConsoleLogger.info("AutoPurgeDatabase : Removed " + i + "permissions");
/*int i = 0;
for (String name : cleared) {

View File

@ -5,7 +5,6 @@ import com.google.common.io.Files;
import com.google.gson.*;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.Utils;
import org.bukkit.entity.Player;
import java.io.File;
@ -109,7 +108,7 @@ public class JsonCache {
}
File file = new File(cacheDir, path);
if (file.exists()) {
Utils.purgeDirectory(file);
purgeDirectory(file);
if (!file.delete()) {
ConsoleLogger.showError("Failed to remove" + player.getName() + "cache.");
}
@ -194,4 +193,25 @@ public class JsonCache {
}
}
/**
* Delete a given directory and all its content.
*
* @param directory The directory to remove
*/
private static void purgeDirectory(File directory) {
if (!directory.isDirectory()) {
return;
}
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File target : files) {
if (target.isDirectory()) {
purgeDirectory(target);
}
target.delete();
}
}
}

View File

@ -1,7 +1,12 @@
package fr.xephi.authme.command;
import com.google.common.collect.Lists;
import fr.xephi.authme.util.CollectionUtils;
import fr.xephi.authme.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
public final class CommandUtils {
public static int getMinNumberOfArguments(CommandDescription command) {
@ -23,10 +28,20 @@ public final class CommandUtils {
* the items ["authme", "register", "player"] it will return "authme register player".
*
* @param labels The labels to format
*
* @return The space-separated labels
*/
public static String labelsToString(Iterable<String> labels) {
return StringUtils.join(" ", labels);
}
public static String constructCommandPath(CommandDescription command) {
List<String> labels = new ArrayList<>();
CommandDescription currentCommand = command;
while (currentCommand != null) {
labels.add(currentCommand.getLabels().get(0));
currentCommand = currentCommand.getParent();
}
return "/" + labelsToString(Lists.reverse(labels));
}
}

View File

@ -52,7 +52,7 @@ public class UnregisterAdminCommand extends ExecutableCommand {
}
// Unregister the player
Player target = Bukkit.getPlayer(playerNameLowerCase);
Player target = Utils.getPlayer(playerNameLowerCase);
PlayerCache.getInstance().removePlayer(playerNameLowerCase);
Utils.setGroup(target, Utils.GroupType.UNREGISTERED);
if (target != null && target.isOnline()) {
@ -68,11 +68,9 @@ public class UnregisterAdminCommand extends ExecutableCommand {
LimboCache.getInstance().getLimboPlayer(playerNameLowerCase).setMessageTaskId(
scheduler.runTaskAsynchronously(plugin,
new MessageTask(plugin, playerNameLowerCase, m.retrieve(MessageKey.REGISTER_MESSAGE), interval)));
if (Settings.applyBlindEffect)
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Settings.getRegistrationTimeout * 20, 2));
if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
target.setWalkSpeed(0.0f);
target.setFlySpeed(0.0f);
if (Settings.applyBlindEffect) {
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS,
Settings.getRegistrationTimeout * 20, 2));
}
m.send(target, MessageKey.UNREGISTERED_SUCCESS);

View File

@ -57,9 +57,7 @@ public class CaptchaCommand extends ExecutableCommand {
plugin.cap.remove(playerNameLowerCase);
String randStr = new RandomString(Settings.captchaLength).nextString();
plugin.cap.put(playerNameLowerCase, randStr);
for (String s : m.retrieve(MessageKey.CAPTCHA_WRONG_ERROR)) {
player.sendMessage(s.replace("THE_CAPTCHA", plugin.cap.get(playerNameLowerCase)));
}
m.send(player, MessageKey.CAPTCHA_WRONG_ERROR, plugin.cap.get(playerNameLowerCase));
return;
}

View File

@ -1,5 +1,6 @@
package fr.xephi.authme.datasource;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@ -21,7 +22,7 @@ public class CacheDataSource implements DataSource {
private final DataSource source;
private final ExecutorService exec;
private final LoadingCache<String, PlayerAuth> cachedAuths;
private final LoadingCache<String, Optional<PlayerAuth>> cachedAuths;
/**
* Constructor for CacheDataSource.
@ -33,9 +34,9 @@ public class CacheDataSource implements DataSource {
this.exec = Executors.newCachedThreadPool();
cachedAuths = CacheBuilder.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.removalListener(RemovalListeners.asynchronous(new RemovalListener<String, PlayerAuth>() {
.removalListener(RemovalListeners.asynchronous(new RemovalListener<String, Optional<PlayerAuth>>() {
@Override
public void onRemoval(RemovalNotification<String, PlayerAuth> removalNotification) {
public void onRemoval(RemovalNotification<String, Optional<PlayerAuth>> removalNotification) {
String name = removalNotification.getKey();
if (PlayerCache.getInstance().isAuthenticated(name)) {
cachedAuths.getUnchecked(name);
@ -43,9 +44,9 @@ public class CacheDataSource implements DataSource {
}
}, exec))
.build(
new CacheLoader<String, PlayerAuth>() {
public PlayerAuth load(String key) {
return source.getAuth(key);
new CacheLoader<String, Optional<PlayerAuth>>() {
public Optional<PlayerAuth> load(String key) {
return Optional.fromNullable(source.getAuth(key));
}
});
}
@ -76,7 +77,7 @@ public class CacheDataSource implements DataSource {
@Override
public synchronized PlayerAuth getAuth(String user) {
user = user.toLowerCase();
return cachedAuths.getUnchecked(user);
return cachedAuths.getUnchecked(user).orNull();
}
/**
@ -178,9 +179,9 @@ public class CacheDataSource implements DataSource {
public int purgeDatabase(long until) {
int cleared = source.purgeDatabase(until);
if (cleared > 0) {
for (PlayerAuth auth : cachedAuths.asMap().values()) {
if (auth != null && auth.getLastLogin() < until) {
cachedAuths.invalidate(auth.getNickname());
for (Optional<PlayerAuth> auth : cachedAuths.asMap().values()) {
if (auth.isPresent() && auth.get().getLastLogin() < until) {
cachedAuths.invalidate(auth.get().getNickname());
}
}
}

View File

@ -36,6 +36,7 @@ public class AuthMeEntityListener implements Listener {
}
}
// TODO: npc status can be used to bypass security!!!
/**
* Method onEntityDamage.
*
@ -53,7 +54,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC(player)) {
return;
}
@ -78,7 +78,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC((Player) entity)) {
return;
}
@ -104,7 +103,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC(player)) {
return;
}
@ -128,7 +126,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC((Player) entity)) {
return;
}
@ -152,7 +149,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC((Player) entity)) {
return;
}
@ -177,7 +173,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC((Player) entity)) {
return;
}
@ -201,7 +196,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC((Player) entity)) {
return;
}
@ -210,7 +204,6 @@ public class AuthMeEntityListener implements Listener {
}
// TODO: Need to check this, player can't throw snowball but the item is taken.
/**
* Method onProjectileLaunch.
*
@ -245,7 +238,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC(player)) {
return;
}
@ -270,7 +262,6 @@ public class AuthMeEntityListener implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC(player)) {
return;
}

View File

@ -229,7 +229,7 @@ public class AuthMePlayerListener implements Listener {
if (auth != null && !auth.getRealName().equals("Player") && !auth.getRealName().equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
event.setKickMessage("You should join using username: " + ChatColor.AQUA + auth.getRealName() +
ChatColor.RESET + "\nnot :" + ChatColor.RED + event.getName()); // TODO: write a better message
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName()); // TODO: write a better message
return;
}
@ -288,7 +288,7 @@ public class AuthMePlayerListener implements Listener {
pl.kickPlayer(m.retrieveSingle(MessageKey.KICK_FOR_VIP));
event.allow();
} else {
ConsoleLogger.info("The player " + event.getPlayer().getName() + " tryed to join, but the server was full");
ConsoleLogger.info("The player " + event.getPlayer().getName() + " tried to join, but the server was full");
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
}

View File

@ -22,6 +22,7 @@ public class AuthMePlayerListener16 implements Listener {
this.plugin = plugin;
}
// TODO: npc status can be used to bypass security!!!
/**
* Method onPlayerEditBook.
*
@ -33,7 +34,6 @@ public class AuthMePlayerListener16 implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC(event.getPlayer())) {
return;
}

View File

@ -22,6 +22,7 @@ public class AuthMePlayerListener18 implements Listener {
this.plugin = plugin;
}
// TODO: npc status can be used to bypass security!!!
/**
* Method onPlayerInteractAtEntity.
*
@ -33,7 +34,6 @@ public class AuthMePlayerListener18 implements Listener {
return;
}
// TODO: npc status can be used to bypass security!!!
if (Utils.isNPC(event.getPlayer())) {
return;
}

View File

@ -79,7 +79,7 @@ public enum MessageKey {
INVALID_NAME_LENGTH("name_len"),
INVALID_NAME_CHARACTERS("regex"),
INVALID_NAME_CHARACTERS("regex", "REG_EX"),
ADD_EMAIL_MESSAGE("add_email"),
@ -87,7 +87,7 @@ public enum MessageKey {
USAGE_CAPTCHA("usage_captcha"),
CAPTCHA_WRONG_ERROR("wrong_captcha"),
CAPTCHA_WRONG_ERROR("wrong_captcha", "THE_CAPTCHA"),
CAPTCHA_SUCCESS("valid_captcha"),
@ -119,16 +119,32 @@ public enum MessageKey {
ANTIBOT_AUTO_ENABLED_MESSAGE("antibot_auto_enabled"),
ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled");
ANTIBOT_AUTO_DISABLED_MESSAGE("antibot_auto_disabled", "%m");
private String key;
private String[] tags;
MessageKey(String key) {
MessageKey(String key, String... tags) {
this.key = key;
this.tags = tags;
}
/**
* Return the key used in the messages file.
*
* @return The key
*/
public String getKey() {
return key;
}
/**
* Return a list of tags (texts) that are replaced with actual content in AuthMe.
*
* @return List of tags
*/
public String[] getTags() {
return tags;
}
}

View File

@ -47,6 +47,31 @@ public class Messages {
}
}
/**
* Send the given message code to the player with the given tag replacements. Note that this method
* issues an exception if the number of supplied replacements doesn't correspond to the number of tags
* the message key contains.
*
* @param sender The entity to send the message to
* @param key The key of the message to send
* @param replacements The replacements to apply for the tags
*/
public void send(CommandSender sender, MessageKey key, String... replacements) {
String message = retrieveSingle(key);
String[] tags = key.getTags();
if (replacements.length != tags.length) {
throw new RuntimeException("Given replacement size does not match the tags in message key '" + key + "'");
}
for (int i = 0; i < tags.length; ++i) {
message = message.replace(tags[i], replacements[i]);
}
for (String line : message.split("\n")) {
sender.sendMessage(line);
}
}
/**
* Retrieve the message from the text file and return it split by new line as an array.
*

View File

@ -10,9 +10,9 @@ import fr.xephi.authme.events.FirstSpawnTeleportEvent;
import fr.xephi.authme.events.ProtectInventoryEvent;
import fr.xephi.authme.events.SpawnTeleportEvent;
import fr.xephi.authme.listener.AuthMePlayerListener;
import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.output.MessageKey;
import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PlayerPermission;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.Spawn;
import fr.xephi.authme.task.MessageTask;
@ -78,9 +78,9 @@ public class AsynchronousJoin {
return;
}
if (Settings.getMaxJoinPerIp > 0
&& !plugin.getPermissionsManager().hasPermission(player, PlayerPermission.ALLOW_MULTIPLE_ACCOUNTS)
&& !ip.equalsIgnoreCase("127.0.0.1")
&& !ip.equalsIgnoreCase("localhost")) {
&& !plugin.getPermissionsManager().hasPermission(player, PlayerPermission.ALLOW_MULTIPLE_ACCOUNTS)
&& !ip.equalsIgnoreCase("127.0.0.1")
&& !ip.equalsIgnoreCase("localhost")) {
if (plugin.hasJoinedIp(player.getName(), ip)) {
sched.scheduleSyncDelayedTask(plugin, new Runnable() {
@ -203,10 +203,6 @@ public class AsynchronousJoin {
if (Settings.applyBlindEffect) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2));
}
if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
player.setWalkSpeed(0.0f);
player.setFlySpeed(0.0f);
}
}
});
@ -236,8 +232,7 @@ public class AsynchronousJoin {
? m.retrieve(MessageKey.REGISTER_EMAIL_MESSAGE)
: m.retrieve(MessageKey.REGISTER_MESSAGE);
}
if (LimboCache.getInstance().getLimboPlayer(name) != null)
{
if (LimboCache.getInstance().getLimboPlayer(name) != null) {
BukkitTask msgTask = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, msgInterval));
LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask);
}

View File

@ -57,20 +57,10 @@ public class AsynchronousLogin {
this.database = data;
}
/**
* Method getIP.
*
* @return String
*/
protected String getIP() {
return plugin.getIP(player);
}
/**
* Method needsCaptcha.
*
* @return boolean
*/
protected boolean needsCaptcha() {
if (Settings.useCaptcha) {
if (!plugin.captcha.containsKey(name)) {
@ -82,9 +72,7 @@ public class AsynchronousLogin {
}
if (plugin.captcha.containsKey(name) && plugin.captcha.get(name) > Settings.maxLoginTry) {
plugin.cap.putIfAbsent(name, rdm.nextString());
for (String s : m.retrieve(MessageKey.USAGE_CAPTCHA)) {
player.sendMessage(s.replace("THE_CAPTCHA", plugin.cap.get(name)).replace("<theCaptcha>", plugin.cap.get(name)));
}
m.send(player, MessageKey.USAGE_CAPTCHA, plugin.cap.get(name));
return true;
}
}
@ -230,12 +218,6 @@ public class AsynchronousLogin {
}
}
/**
* Method displayOtherAccounts.
*
* @param auth PlayerAuth
* @param p Player
*/
public void displayOtherAccounts(PlayerAuth auth, Player p) {
if (!Settings.displayOtherAccounts) {
return;

View File

@ -189,11 +189,6 @@ public class ProcessSyncronousPlayerLogin implements Runnable {
player.removePotionEffect(PotionEffectType.BLINDNESS);
}
if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
player.setWalkSpeed(0.2f);
player.setFlySpeed(0.1f);
}
// The Login event now fires (as intended) after everything is processed
Bukkit.getServer().getPluginManager().callEvent(new LoginEvent(player, true));
player.saveData();

View File

@ -80,10 +80,6 @@ public class ProcessSyncronousPlayerLogout implements Runnable {
if (!Settings.isMovementAllowed) {
player.setAllowFlight(true);
player.setFlying(true);
if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
player.setFlySpeed(0.0f);
player.setWalkSpeed(0.0f);
}
}
// Player is now logout... Time to fire event !
Bukkit.getServer().getPluginManager().callEvent(new LogoutEvent(player));

View File

@ -141,8 +141,10 @@ public class AsyncRegister {
return;
}
if (!Settings.forceRegLogin) {
PlayerCache.getInstance().addPlayer(auth);
database.setLogged(name);
//PlayerCache.getInstance().addPlayer(auth);
//database.setLogged(name);
// TODO: check this...
plugin.getManagement().performLogin(player, "dontneed", true);
}
plugin.otherAccounts.addPlayer(player.getUniqueId());
ProcessSyncPasswordRegister sync = new ProcessSyncPasswordRegister(player, plugin);

View File

@ -128,11 +128,6 @@ public class ProcessSyncPasswordRegister implements Runnable {
player.removePotionEffect(PotionEffectType.BLINDNESS);
}
if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
player.setWalkSpeed(0.0f);
player.setFlySpeed(0.0f);
}
// The LoginEvent now fires (as intended) after everything is processed
plugin.getServer().getPluginManager().callEvent(new LoginEvent(player, true));
player.saveData();

View File

@ -104,10 +104,6 @@ public class AsynchronousUnregister {
if (Settings.applyBlindEffect) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeOut, 2));
}
if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
player.setWalkSpeed(0.0f);
player.setFlySpeed(0.0f);
}
m.send(player, MessageKey.UNREGISTERED_SUCCESS);
ConsoleLogger.info(player.getDisplayName() + " unregistered himself");
Utils.teleportToSpawn(player);

View File

@ -15,7 +15,6 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
@ -207,27 +206,6 @@ public final class Utils {
}
}
/**
* Delete a given directory and all his content.
*
* @param directory File
*/
public static void purgeDirectory(File directory) {
if (!directory.isDirectory()) {
return;
}
File[] files = directory.listFiles();
if (files == null) {
return;
}
for (File target : files) {
if (target.isDirectory()) {
purgeDirectory(target);
}
target.delete();
}
}
/**
* Safe way to retrieve the list of online players from the server. Depending on the
* implementation of the server, either an array of {@link Player} instances is being returned,

View File

@ -38,7 +38,7 @@ regex: '&cTvuj nick obsahuje nepovolene znaky. Pripustne znaky jsou: REG_EX'
add_email: '&cPridej prosim svuj email pomoci : /email add TvujEmail TvujEmail'
recovery_email: '&cZapomel jsi heslo? Zadej: /email recovery <TvujEmail>'
usage_captcha: '&cPouzij: /captcha <Captcha_text>'
wrong_captcha: '&cSpatne opsana Captcha, pouzij prosim: /captcha CAPTCHA_TEXT'
wrong_captcha: '&cSpatne opsana Captcha, pouzij prosim: /captcha THE_CAPTCHA'
valid_captcha: '&cZadana captcha je v poradku!'
kick_forvip: '&cVIP Hrac se pripojil na plny server!'
kick_fullserver: '&cServer je plne obsazen, zkus to pozdeji prosim!'

View File

@ -14,7 +14,7 @@ user_regged: '&cYou already have registered this username!'
usage_reg: '&cUsage: /register <password> <ConfirmPassword>'
max_reg: '&cYou have exceeded the maximum number of registrations for your connection!'
no_perm: '&4You don''t have the permission to perform this action!'
error: '&4An unexpected error occurred, please contact an Administrator!'
error: '&4An unexpected error occurred, please contact an administrator!'
login_msg: '&cPlease, login with the command "/login <password>"'
reg_msg: '&3Please, register to the server with the command "/register <password> <ConfirmPassword>"'
reg_email_msg: '&3Please, register to the server with the command "/register <email> <confirmEmail>"'
@ -39,20 +39,20 @@ regex: '&4Your username contains illegal characters. Allowed chars: REG_EX'
add_email: '&3Please add your email to your account with the command "/email add <yourEmail> <confirmEmail>"'
recovery_email: '&3Forgot your password? Please use the command "/email recovery <yourEmail>"'
usage_captcha: '&3To login you have to solve a captcha code, please use the command "/captcha <theCaptcha>"'
wrong_captcha: '&cWrong Captcha, please type "/captcha THE_CAPTCHA" into the chat!'
wrong_captcha: '&cWrong captcha, please type "/captcha THE_CAPTCHA" into the chat!'
valid_captcha: '&2Captcha code solved correctly!'
kick_forvip: '&3A VIP Player has joined the server when it was full!'
kick_forvip: '&3A VIP player has joined the server when it was full!'
kick_fullserver: '&4The server is full, try again later!'
usage_email_add: '&cUsage: /email add <email> <confirmEmail>'
usage_email_change: '&cUsage: /email change <oldEmail> <newEmail>'
usage_email_recovery: '&cUsage: /email recovery <Email>'
new_email_invalid: '&cInvalid New Email, try again!'
old_email_invalid: '&cInvalid Old Email, try again!'
email_invalid: '&cInvalid Email address, try again!'
new_email_invalid: '&cInvalid new email, try again!'
old_email_invalid: '&cInvalid old email, try again!'
email_invalid: '&cInvalid email address, try again!'
email_added: '&2Email address successfully added to your account!'
email_confirm: '&cPlease confirm your email address!'
email_changed: '&2Email address changed correctly!'
email_send: '&2Recovery email sent correctly! Check your email inbox!'
email_send: '&2Recovery email sent successfully! Please check your email inbox!'
email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:'
country_banned: '&4Your country is banned from this server!'
antibot_auto_enabled: '&4[AntiBotService] AntiBot enabled due to the huge number of connections!'

View File

@ -39,7 +39,7 @@ regex: '&cTu usuario tiene carácteres no admitidos, los cuales son: REG_EX'
add_email: '&cPor favor agrega tu e-mail con: /email add tuEmail confirmarEmail'
recovery_email: '&c¿Olvidaste tu contraseña? Por favor usa /email recovery <tuEmail>'
usage_captcha: '&cUso: /captcha <elCaptcha>'
wrong_captcha: '&cCaptcha incorrecto, please use : /captcha EL_CAPTCHA'
wrong_captcha: '&cCaptcha incorrecto, please use : /captcha THE_CAPTCHA'
valid_captcha: '&c¡ Captcha ingresado correctamente !'
kick_forvip: '&cUn jugador VIP ha ingresado al servidor lleno!'
kick_fullserver: '&cEl servidor está lleno, lo sentimos!'

View File

@ -1,4 +1,4 @@
reg_only: Csak regisztrált játékosoknak! Jelentkezni a yndicraft@freemail.hu e-mail címen lehet
reg_only: Csak regisztrált játékosoknak! Jelentkezni a mail@email.com e-mail címen lehet
usage_unreg: '&cHasználat: /unregister jelszó'
registered: '&aSikeres regisztráció. Üdvözöllek!'
user_regged: '&cJátékosnév már regisztrálva'

View File

@ -5,6 +5,7 @@ import fr.xephi.authme.permission.PermissionNode;
import fr.xephi.authme.util.StringUtils;
import fr.xephi.authme.util.WrapperMock;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
@ -269,6 +270,42 @@ public class CommandInitializerTest {
walkThroughCommands(commands, adminPermissionChecker);
}
/**
* Tests that multiple CommandDescription instances pointing to the same ExecutableCommand use the same
* count of arguments.
*/
@Test
@Ignore // TODO #306 ljacqu 20151214: Un-ignore this test and fix the offending command
public void shouldPointToSameExecutableCommandWithConsistentArgumentCount() {
// given
final Map<Class<? extends ExecutableCommand>, Integer> mandatoryArguments = new HashMap<>();
final Map<Class<? extends ExecutableCommand>, Integer> totalArguments = new HashMap<>();
BiConsumer argChecker = new BiConsumer() {
@Override
public void accept(CommandDescription command, int depth) {
testCollectionForCommand(command, CommandUtils.getMinNumberOfArguments(command), mandatoryArguments);
testCollectionForCommand(command, CommandUtils.getMaxNumberOfArguments(command), totalArguments);
}
private void testCollectionForCommand(CommandDescription command, int argCount,
Map<Class<? extends ExecutableCommand>, Integer> collection) {
final Class<? extends ExecutableCommand> clazz = command.getExecutableCommand().getClass();
Integer existingCount = collection.get(clazz);
if (existingCount != null) {
String commandDescription = "Command with label '" + command.getLabels().get(0) + "' and parent '"
+ (command.getParent() != null ? command.getLabels().get(0) : "null") + "' ";
assertThat(commandDescription + "should point to " + clazz + " with arguments consistent to others",
argCount, equalTo(existingCount));
} else {
collection.put(clazz, argCount);
}
}
};
// when / then
walkThroughCommands(commands, argChecker);
}
// ------------
// Helper methods

View File

@ -2,9 +2,11 @@ package fr.xephi.authme.output;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.WrapperMock;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.io.File;
@ -14,6 +16,8 @@ import static org.hamcrest.Matchers.arrayWithSize;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
@ -137,4 +141,58 @@ public class MessagesIntegrationTest {
verify(player).sendMessage(line);
}
}
@Test
public void shouldSendMessageToPlayerWithTagReplacement() {
// given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
CommandSender sender = Mockito.mock(CommandSender.class);
// when
messages.send(sender, key, "1234");
// then
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(sender, times(1)).sendMessage(captor.capture());
String message = captor.getValue();
assertThat(message, equalTo("Use /captcha 1234 to solve the captcha"));
}
@Test
public void shouldNotThrowForKeyWithNoTagReplacements() {
// given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
CommandSender sender = mock(CommandSender.class);
// when
messages.send(sender, key);
// then
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
verify(sender, times(1)).sendMessage(captor.capture());
String message = captor.getValue();
assertThat(message, equalTo("Use /captcha THE_CAPTCHA to solve the captcha"));
}
@Test(expected = RuntimeException.class)
public void shouldThrowForInvalidReplacementCount() {
// given
MessageKey key = MessageKey.CAPTCHA_WRONG_ERROR;
// when
messages.send(mock(CommandSender.class), key, "rep", "rep2");
// then - expect exception
}
@Test(expected = RuntimeException.class)
public void shouldThrowForReplacementsOnKeyWithNoTags() {
// given
MessageKey key = MessageKey.UNKNOWN_USER;
// when
messages.send(mock(CommandSender.class), key, "Replacement");
// then - expect exception
}
}

View File

@ -13,9 +13,9 @@ import static org.junit.Assert.fail;
public class AdminPermissionTest {
@Test
public void shouldStartWithAuthMePrefix() {
public void shouldStartWithAuthMeAdminPrefix() {
// given
String requiredPrefix = "authme.";
String requiredPrefix = "authme.admin.";
// when/then
for (AdminPermission permission : AdminPermission.values()) {
@ -26,20 +26,6 @@ public class AdminPermissionTest {
}
}
@Test
public void shouldContainAdminBranch() {
// given
String requiredBranch = ".admin.";
// when/then
for (AdminPermission permission : AdminPermission.values()) {
if (!permission.getNode().contains(requiredBranch)) {
fail("The permission '" + permission + "' does not contain with the required branch '"
+ requiredBranch + "'");
}
}
}
@Test
public void shouldHaveUniqueNodes() {
// given

View File

@ -13,32 +13,13 @@ import static org.junit.Assert.fail;
public class PlayerPermissionTest {
@Test
public void shouldStartWithAuthMePrefix() {
public void shouldStartWithPlayerPrefix() {
// given
String requiredPrefix = "authme.";
String playerBranch = "authme.player.";
// when/then
for (PlayerPermission permission : PlayerPermission.values()) {
if (!permission.getNode().startsWith(requiredPrefix)) {
fail("The permission '" + permission + "' does not start with the required prefix '" + requiredPrefix
+ "'");
}
}
}
@Test
public void shouldContainPlayerBranch() {
// given
String playerBranch = ".player.";
String adminBranch = ".admin.";
// when/then
for (PlayerPermission permission : PlayerPermission.values()) {
if (permission.getNode().contains(adminBranch)) {
fail("The permission '" + permission + "' should not use a node with the admin-specific branch '"
+ adminBranch + "'");
} else if (!permission.getNode().contains(playerBranch)) {
if (!permission.getNode().startsWith(playerBranch)) {
fail("The permission '" + permission + "' should use a node with the player-specific branch '"
+ playerBranch + "'");
}

View File

@ -4,3 +4,4 @@ not_logged_in: 'Apostrophes '' should be loaded correctly, don''t you think?'
reg_voluntarily: 'You can register yourself to the server with the command "/register <password> <ConfirmPassword>"'
usage_log: '&cUsage: /login <password>'
wrong_pwd: '&cWrong password!'
wrong_captcha: 'Use /captcha THE_CAPTCHA to solve the captcha'

View File

@ -0,0 +1,70 @@
package commands;
import fr.xephi.authme.command.CommandArgumentDescription;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.command.CommandInitializer;
import fr.xephi.authme.command.CommandPermissions;
import fr.xephi.authme.command.CommandUtils;
import fr.xephi.authme.permission.PermissionNode;
import utils.ANewMap;
import utils.FileUtils;
import utils.TagReplacer;
import utils.ToolTask;
import utils.ToolsConstants;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
public class CommandPageCreater implements ToolTask {
@Override
public String getTaskName() {
return "createCommandPage";
}
@Override
public void execute(Scanner scanner) {
final Set<CommandDescription> baseCommands = CommandInitializer.getBaseCommands();
final String template = FileUtils.readFromFile(ToolsConstants.TOOLS_SOURCE_ROOT
+ "commands/command_entry.tpl.md");
StringBuilder commandsResult = new StringBuilder();
for (CommandDescription command : baseCommands) {
Map<String, String> tags = ANewMap
.with("command", CommandUtils.constructCommandPath(command))
.and("description", command.getDetailedDescription())
.and("arguments", formatArguments(command.getArguments()))
.and("permissions", formatPermissions(command.getCommandPermissions()))
.build();
commandsResult.append(TagReplacer.applyReplacements(template, tags));
}
FileUtils.generateFileFromTemplate(
ToolsConstants.TOOLS_SOURCE_ROOT + "commands/commands.tpl.md",
ToolsConstants.DOCS_FOLDER + "commands.md",
ANewMap.with("commands", commandsResult.toString()).build());
}
private static String formatPermissions(CommandPermissions permissions) {
if (permissions == null) {
return "";
}
String result = "";
for (PermissionNode node : permissions.getPermissionNodes()) {
result += node.getNode() + " ";
}
return result;
}
private static String formatArguments(Iterable<CommandArgumentDescription> arguments) {
StringBuilder result = new StringBuilder();
for (CommandArgumentDescription argument : arguments) {
String argumentName = argument.isOptional()
? "[" + argument.getDescription() + "]"
: "<" + argument.getDescription() + ">";
result.append(argumentName).append(" ");
}
return result.toString();
}
}

View File

@ -0,0 +1,2 @@
{command}: {description} _{arguments}_
[permissions]Permission: {permissions}[/permissions]

View File

@ -0,0 +1,4 @@
## AuthMe commands
You can use the following commands to use the functions of AuthMe:
{commands}

View File

@ -1,11 +1,15 @@
package messages;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import fr.xephi.authme.output.MessageKey;
import utils.FileUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -22,6 +26,7 @@ public class MessageFileVerifier {
private final Set<String> unknownKeys = new HashSet<>();
// Map with the missing key and a boolean indicating whether or not it was added to the file by this object
private final Map<String, Boolean> missingKeys = new HashMap<>();
private final Multimap<String, String> missingTags = HashMultimap.create();
/**
* Create a verifier that verifies the given messages file.
@ -53,8 +58,17 @@ public class MessageFileVerifier {
return missingKeys;
}
/**
* Return the collection of tags the message key defines that aren't present in the read line.
*
* @return Collection of missing tags per message key. Key = message key, value = missing tag.
*/
public Multimap<String, String> getMissingTags() {
return missingTags;
}
private void verifyKeys() {
Set<String> messageKeys = getAllMessageKeys();
List<MessageKey> messageKeys = getAllMessageKeys();
List<String> fileLines = FileUtils.readLinesFromFile(messagesFile);
for (String line : fileLines) {
// Skip comments and empty lines
@ -64,22 +78,38 @@ public class MessageFileVerifier {
}
// All keys that remain are keys that are absent in the file
for (String missingKey : messageKeys) {
missingKeys.put(missingKey, false);
for (MessageKey missingKey : messageKeys) {
missingKeys.put(missingKey.getKey(), false);
}
}
private void processKeyInFile(String line, Set<String> messageKeys) {
private void processKeyInFile(String line, List<MessageKey> messageKeys) {
if (line.indexOf(':') == -1) {
System.out.println("Skipping line in unknown format: '" + line + "'");
return;
}
final String key = line.substring(0, line.indexOf(':'));
if (messageKeys.contains(key)) {
messageKeys.remove(key);
} else {
unknownKeys.add(key);
final String readKey = line.substring(0, line.indexOf(':'));
boolean foundKey = false;
for (Iterator<MessageKey> it = messageKeys.iterator(); it.hasNext(); ) {
MessageKey messageKey = it.next();
if (messageKey.getKey().equals(readKey)) {
checkTagsInMessage(readKey, line.substring(line.indexOf(':')), messageKey.getTags());
it.remove();
foundKey = true;
break;
}
}
if (!foundKey) {
unknownKeys.add(readKey);
}
}
private void checkTagsInMessage(String key, String message, String[] tags) {
for (String tag : tags) {
if (!message.contains(tag)) {
missingTags.put(key, tag);
}
}
}
@ -105,11 +135,7 @@ public class MessageFileVerifier {
FileUtils.appendToFile(messagesFile, sb.toString());
}
private static Set<String> getAllMessageKeys() {
Set<String> messageKeys = new HashSet<>(MessageKey.values().length);
for (MessageKey key : MessageKey.values()) {
messageKeys.add(key.getKey());
}
return messageKeys;
private static List<MessageKey> getAllMessageKeys() {
return new ArrayList<>(Arrays.asList(MessageKey.values()));
}
}

View File

@ -1,5 +1,6 @@
package messages;
import com.google.common.collect.Multimap;
import fr.xephi.authme.util.StringUtils;
import utils.FileUtils;
import utils.ToolTask;
@ -84,6 +85,11 @@ public final class VerifyMessagesTask implements ToolTask {
if (!unknownKeys.isEmpty()) {
System.out.println(" Unknown keys: " + unknownKeys);
}
Multimap<String, String> missingTags = verifier.getMissingTags();
for (Map.Entry<String, String> entry : missingTags.entries()) {
System.out.println(" Missing tag '" + entry.getValue() + "' in entry with key '" + entry.getKey() + "'");
}
}
private static void verifyFileAndAddKeys(MessageFileVerifier verifier, Map<String, String> defaultMessages) {
@ -106,6 +112,11 @@ public final class VerifyMessagesTask implements ToolTask {
if (!unknownKeys.isEmpty()) {
System.out.println(" Unknown keys: " + unknownKeys);
}
Multimap<String, String> missingTags = verifier.getMissingTags();
for (Map.Entry<String, String> entry : missingTags.entries()) {
System.out.println(" Missing tag '" + entry.getValue() + "' in entry with key '" + entry.getKey() + "'");
}
}
private static Map<String, String> constructDefaultMessages() {

View File

@ -47,7 +47,7 @@ public class PermissionsListWriter implements ToolTask {
private static void generateAndWriteFile() {
final String permissionsTagValue = generatePermissionsList();
Map<String, Object> tags = ANewMap.<String, Object>with("permissions", permissionsTagValue).build();
Map<String, String> tags = ANewMap.with("permissions", permissionsTagValue).build();
FileUtils.generateFileFromTemplate(
ToolsConstants.TOOLS_SOURCE_ROOT + "permissions/permission_nodes.tpl.md", PERMISSIONS_OUTPUT_FILE, tags);
System.out.println("Wrote to '" + PERMISSIONS_OUTPUT_FILE + "'");
@ -62,8 +62,8 @@ public class PermissionsListWriter implements ToolTask {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : permissions.entrySet()) {
Map<String, Object> tags = ANewMap.<String, Object>
with("node", entry.getKey())
Map<String, String> tags = ANewMap
.with("node", entry.getKey())
.and("description", entry.getValue())
.build();
sb.append(TagReplacer.applyReplacements(template, tags));

View File

@ -18,10 +18,9 @@ public final class FileUtils {
private FileUtils() {
}
public static void generateFileFromTemplate(String templateFile, String destinationFile, Map<String, Object> tags) {
public static void generateFileFromTemplate(String templateFile, String destinationFile, Map<String, String> tags) {
String template = readFromFile(templateFile);
String result = TagReplacer.applyReplacements(template, tags);
writeToFile(destinationFile, result);
}

View File

@ -1,5 +1,7 @@
package utils;
import fr.xephi.authme.util.StringUtils;
import java.util.Date;
import java.util.Map;
@ -21,10 +23,15 @@ public class TagReplacer {
* any occurrences of "{foo}" to "bar".
* @return The filled template
*/
public static String applyReplacements(String template, Map<String, Object> tags) {
public static String applyReplacements(String template, Map<String, String> tags) {
String result = template;
for (Map.Entry<String, Object> tagRule : tags.entrySet()) {
result = result.replace("{" + tagRule.getKey() + "}", tagRule.getValue().toString());
for (Map.Entry<String, String> tagRule : tags.entrySet()) {
final String name = tagRule.getKey();
final String value = tagRule.getValue();
String replacement = StringUtils.isEmpty(value) ? "" : "\\1";
result = result.replaceAll("\\[" + name + "\\](.*?)\\[/" + name + "\\]", replacement);
result = result.replace("{" + tagRule.getKey() + "}", tagRule.getValue());
}
return applyReplacements(result);