diff --git a/pom.xml b/pom.xml
index 3ec71875..7bf92c8f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -126,7 +126,7 @@
org.apache.maven.plugins
maven-enforcer-plugin
- 3.2.1
+ 3.5.0
enforce-environment
@@ -216,6 +216,13 @@
org.apache.maven.plugins
maven-jar-plugin
3.4.1
+
+
+
+ mojang
+
+
+
@@ -463,6 +470,18 @@
com.alessiodp.libby
fr.xephi.authme.libs.com.alessiodp.libby
+
+ net.kyori.adventure
+ fr.xephi.authme.libs.net.kyori.adventure
+
+
+ net.kyori.examination
+ fr.xephi.authme.libs.net.kyori.examination
+
+
+ net.kyori.option
+ fr.xephi.authme.libs.net.kyori.option
+
@@ -475,7 +494,6 @@
META-INF/*.DSA
META-INF/*.RSA
META-INF/*.RSA
- META-INF/*.MF
META-INF/DEPENDENCIES
META-INF/**/module-info.class
@@ -495,13 +513,13 @@
org.apache.maven.plugins
maven-install-plugin
- 3.1.0
+ 3.1.2
org.apache.maven.plugins
maven-deploy-plugin
- 3.0.0
+ 3.1.2
@@ -539,6 +557,12 @@
https://repo.opencollab.dev/maven-snapshots/
+
+
+ sonatype-oss-snapshots1
+ https://s01.oss.sonatype.org/content/repositories/snapshots/
+
+
apache-snapshots
@@ -807,14 +831,14 @@
junit
junit
-
- bungeecord-chat
- net.md-5
-
com.googlecode.json-simple
json-simple
+
+ bungeecord-chat
+ net.md-5
+
@@ -882,6 +906,23 @@
+
+
+ net.kyori
+ adventure-text-minimessage
+ 4.17.0
+
+
+ net.kyori
+ adventure-platform-bukkit
+ 4.3.2
+
+
+ net.kyori
+ adventure-text-serializer-gson
+ 4.17.0
+
+
net.luckperms
@@ -1152,7 +1193,7 @@
org.xerial
sqlite-jdbc
- 3.45.3.0
+ 3.46.0.0
test
diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java
index 3bec3edb..8d079e8e 100644
--- a/src/main/java/fr/xephi/authme/AuthMe.java
+++ b/src/main/java/fr/xephi/authme/AuthMe.java
@@ -44,6 +44,7 @@ import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.task.CleanupTask;
+import fr.xephi.authme.task.Updater;
import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.util.ExceptionUtils;
import org.bukkit.Server;
@@ -55,15 +56,10 @@ import org.jetbrains.annotations.NotNull;
import javax.inject.Inject;
import java.io.File;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
-import java.util.Scanner;
import java.util.function.Consumer;
-import java.util.logging.Level;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.util.Utils.isClassLoaded;
@@ -218,14 +214,15 @@ public class AuthMe extends JavaPlugin {
}
//detect server brand with classloader
checkServerType();
- Objects.requireNonNull(getCommand("register")).setTabCompleter(new TabCompleteHandler());
- Objects.requireNonNull(getCommand("login")).setTabCompleter(new TabCompleteHandler());
+ try {
+ Objects.requireNonNull(getCommand("register")).setTabCompleter(new TabCompleteHandler());
+ Objects.requireNonNull(getCommand("login")).setTabCompleter(new TabCompleteHandler());
+ } catch (NullPointerException ignored) {
+ }
logger.info("AuthMeReReloaded is enabled successfully!");
// Purge on start if enabled
PurgeService purgeService = injector.getSingleton(PurgeService.class);
purgeService.runAutoPurge();
- // 注册玩家加入事件监听
-// register3rdPartyListeners();
logger.info("GitHub: https://github.com/HaHaWTH/AuthMeReReloaded/");
if (settings.getProperty(SecuritySettings.CHECK_FOR_UPDATES)) {
checkForUpdates();
@@ -233,8 +230,6 @@ public class AuthMe extends JavaPlugin {
}
- //Migrated
-
/**
* Load the version and build number of the plugin from the description file.
*
@@ -402,7 +397,7 @@ public class AuthMe extends JavaPlugin {
if (onShutdownPlayerSaver != null) {
onShutdownPlayerSaver.saveAllPlayers();
}
- if (settings.getProperty(EmailSettings.SHUTDOWN_MAIL)){
+ if (settings != null && settings.getProperty(EmailSettings.SHUTDOWN_MAIL)) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy'.'MM'.'dd'.' HH:mm:ss");
Date date = new Date(System.currentTimeMillis());
emailService.sendShutDown(settings.getProperty(EmailSettings.SHUTDOWN_MAIL_ADDRESS),dateFormat.format(date));
@@ -422,55 +417,19 @@ public class AuthMe extends JavaPlugin {
ConsoleLogger.closeFileWriter();
}
- private static final String owner = "HaHaWTH";
-// private static final String owner_gitee = "Shixuehan114514";
- private static final String repo = "AuthMeReReloaded";
-
private void checkForUpdates() {
logger.info("Checking for updates...");
+ Updater updater = new Updater(pluginBuild + pluginBuildNumber);
bukkitService.runTaskAsynchronously(() -> {
- try {
- // 从南通集线器获取最新版本号
- URL url = new URL("https://api.github.com/repos/" + owner + "/" + repo + "/releases/latest");
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- conn.setConnectTimeout(10000); // 设置连接超时为10秒
- conn.setReadTimeout(10000); // 设置读取超时为10秒
- Scanner scanner = new Scanner(conn.getInputStream());
- String response = scanner.useDelimiter("\\Z").next();
- scanner.close();
-
- // 处理JSON响应
- String latestVersion = response.substring(response.indexOf("tag_name") + 11);
- latestVersion = latestVersion.substring(0, latestVersion.indexOf("\""));
- if (isUpdateAvailable(latestVersion)) {
- String message = "New version available! Latest:" + latestVersion + " Current:" + pluginBuild + pluginBuildNumber;
- getLogger().log(Level.WARNING, message);
- getLogger().log(Level.WARNING, "Download from here: https://github.com/HaHaWTH/AuthMeReReloaded/releases/latest");
- } else {
- getLogger().log(Level.INFO, "You are running the latest version.");
- }
- } catch (IOException ignored) {
+ if (updater.isUpdateAvailable()) {
+ String message = "New version available! Latest:" + updater.getLatestVersion() + " Current:" + pluginBuild + pluginBuildNumber;
+ logger.warning(message);
+ logger.warning("Download from here: https://github.com/HaHaWTH/AuthMeReReloaded/releases/latest");
+ } else {
+ logger.info("You are running the latest version.");
}
});
}
- private boolean isUpdateAvailable(String latestVersion) {
- // Extract the first character and the remaining digits from the version string
- char latestChar = latestVersion.charAt(0);
- int latestNumber = Integer.parseInt(latestVersion.substring(1));
-
- char currentChar = pluginBuild.charAt(0);
- int currentNumber = Integer.parseInt(pluginBuildNumber);
-
- // Compare the characters first
- if (latestChar > currentChar) {
- return true;
- } else if (latestChar < currentChar) {
- return false;
- } else {
- // If the characters are the same, compare the numbers
- return latestNumber > currentNumber;
- }
- }
private void checkServerType() {
diff --git a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java b/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java
index 3d8ab194..fd487d66 100644
--- a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java
+++ b/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java
@@ -265,6 +265,16 @@ public class AuthMeApi {
management.forceLogin(player);
}
+ /**
+ * Force a player to login, i.e. the player is logged in without needing his password.
+ *
+ * @param player The player to log in
+ * @param quiet Whether to suppress the login message
+ */
+ public void forceLogin(Player player, boolean quiet) {
+ management.forceLogin(player, quiet);
+ }
+
/**
* Force a player to logout.
*
diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java
index 7bd7d7cc..9eba3d15 100644
--- a/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java
+++ b/src/main/java/fr/xephi/authme/command/executable/authme/FirstSpawnCommand.java
@@ -1,9 +1,9 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
+import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
-import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.TeleportUtils;
import org.bukkit.entity.Player;
@@ -18,17 +18,17 @@ public class FirstSpawnCommand extends PlayerCommand {
private Settings settings;
@Inject
private SpawnLoader spawnLoader;
+ @Inject
+ private BukkitService bukkitService;
@Override
public void runCommand(Player player, List arguments) {
if (spawnLoader.getFirstSpawn() == null) {
player.sendMessage("[AuthMe] First spawn has failed, please try to define the first spawn");
} else {
//String name= player.getName();
- if(settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
+ bukkitService.runTaskIfFolia(player, () -> {
TeleportUtils.teleport(player, spawnLoader.getFirstSpawn());
- } else {
- player.teleport(spawnLoader.getFirstSpawn());
- }
+ });
//player.teleport(spawnLoader.getFirstSpawn());
}
}
diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java
index 3c011e6d..92ad0a30 100644
--- a/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java
+++ b/src/main/java/fr/xephi/authme/command/executable/authme/SpawnCommand.java
@@ -1,7 +1,9 @@
package fr.xephi.authme.command.executable.authme;
import fr.xephi.authme.command.PlayerCommand;
+import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.SpawnLoader;
+import fr.xephi.authme.util.TeleportUtils;
import org.bukkit.entity.Player;
import javax.inject.Inject;
@@ -11,13 +13,15 @@ public class SpawnCommand extends PlayerCommand {
@Inject
private SpawnLoader spawnLoader;
+ @Inject
+ private BukkitService bukkitService;
@Override
public void runCommand(Player player, List arguments) {
if (spawnLoader.getSpawn() == null) {
player.sendMessage("[AuthMe] Spawn has failed, please try to define the spawn");
} else {
- player.teleport(spawnLoader.getSpawn());
+ bukkitService.runTaskIfFolia(player, () -> TeleportUtils.teleport(player, spawnLoader.getSpawn()));
}
}
}
diff --git a/src/main/java/fr/xephi/authme/listener/BedrockAutoLoginListener.java b/src/main/java/fr/xephi/authme/listener/BedrockAutoLoginListener.java
index 9a0adfb3..4f63e610 100644
--- a/src/main/java/fr/xephi/authme/listener/BedrockAutoLoginListener.java
+++ b/src/main/java/fr/xephi/authme/listener/BedrockAutoLoginListener.java
@@ -46,7 +46,7 @@ public class BedrockAutoLoginListener implements Listener {
UUID uuid = event.getPlayer().getUniqueId();
bukkitService.runTaskLater(player, () -> {
if (isBedrockPlayer(uuid) && !authmeApi.isAuthenticated(player) && authmeApi.isRegistered(name)) {
- authmeApi.forceLogin(player);
+ authmeApi.forceLogin(player, true);
messages.send(player, MessageKey.BEDROCK_AUTO_LOGGED_IN);
}
},20L);
diff --git a/src/main/java/fr/xephi/authme/listener/LoginLocationFixListener.java b/src/main/java/fr/xephi/authme/listener/LoginLocationFixListener.java
index 344290ac..1c64b68b 100644
--- a/src/main/java/fr/xephi/authme/listener/LoginLocationFixListener.java
+++ b/src/main/java/fr/xephi/authme/listener/LoginLocationFixListener.java
@@ -75,11 +75,7 @@ public class LoginLocationFixListener implements Listener {
boolean solved = false;
for (BlockFace face : faces) {
if (JoinBlock.getRelative(face).getType().equals(Material.AIR) && JoinBlock.getRelative(face).getRelative(BlockFace.UP).getType().equals(Material.AIR)) {
- if (settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
- TeleportUtils.teleport(player, JoinBlock.getRelative(face).getLocation().add(0.5, 0.1, 0.5));
- } else {
- player.teleport(JoinBlock.getRelative(face).getLocation().add(0.5, 0.1, 0.5));
- }
+ TeleportUtils.teleport(player, JoinBlock.getRelative(face).getLocation().add(0.5, 0.1, 0.5));
solved = true;
break;
}
@@ -107,20 +103,12 @@ public class LoginLocationFixListener implements Listener {
if (JoinBlock.getRelative(BlockFace.DOWN).getType().equals(Material.LAVA)) {
JoinBlock.getRelative(BlockFace.DOWN).setType(Material.DIRT);
}
- if (settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
- TeleportUtils.teleport(player, JoinBlock.getLocation().add(0.5, 0.1, 0.5));
- } else {
- player.teleport(JoinBlock.getLocation().add(0.5, 0.1, 0.5));
- }
+ TeleportUtils.teleport(player, JoinBlock.getLocation().add(0.5, 0.1, 0.5));
messages.send(player, MessageKey.LOCATION_FIX_UNDERGROUND);
break;
}
if (i == MaxHeight) {
- if (settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
- TeleportUtils.teleport(player, JoinBlock.getLocation().add(0.5, 1.1, 0.5));
- } else {
- player.teleport(JoinBlock.getLocation().add(0.5, 1.1, 0.5));
- }
+ TeleportUtils.teleport(player, JoinBlock.getLocation().add(0.5, 1.1, 0.5));
messages.send(player, MessageKey.LOCATION_FIX_UNDERGROUND_CANT_FIX);
}
}
diff --git a/src/main/java/fr/xephi/authme/listener/PlayerListener.java b/src/main/java/fr/xephi/authme/listener/PlayerListener.java
index 85056448..d79620b3 100644
--- a/src/main/java/fr/xephi/authme/listener/PlayerListener.java
+++ b/src/main/java/fr/xephi/authme/listener/PlayerListener.java
@@ -19,8 +19,8 @@ import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
-import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.TeleportUtils;
+import fr.xephi.authme.util.message.MiniMessageUtils;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.entity.HumanEntity;
@@ -220,7 +220,7 @@ public class PlayerListener implements Listener{
String customJoinMessage = settings.getProperty(RegistrationSettings.CUSTOM_JOIN_MESSAGE);
if (!customJoinMessage.isEmpty()) {
- customJoinMessage = ChatColor.translateAlternateColorCodes('&', customJoinMessage);
+ customJoinMessage = ChatColor.translateAlternateColorCodes('&', MiniMessageUtils.parseMiniMessageToLegacy(customJoinMessage));
event.setJoinMessage(customJoinMessage
.replace("{PLAYERNAME}", player.getName())
.replace("{DISPLAYNAME}", player.getDisplayName())
@@ -375,17 +375,9 @@ public class PlayerListener implements Listener{
Location spawn = spawnLoader.getSpawnLocation(player);
if (spawn != null && spawn.getWorld() != null) {
if (!player.getWorld().equals(spawn.getWorld())) {
- if(settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
- TeleportUtils.teleport(player,spawn);
- } else {
- player.teleport(spawn);
- }
+ TeleportUtils.teleport(player,spawn);
} else if (spawn.distance(player.getLocation()) > settings.getProperty(ALLOWED_MOVEMENT_RADIUS)) {
- if(settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
- TeleportUtils.teleport(player,spawn);
- } else {
- player.teleport(spawn);
- }
+ TeleportUtils.teleport(player,spawn);
}
}
}
diff --git a/src/main/java/fr/xephi/authme/message/Messages.java b/src/main/java/fr/xephi/authme/message/Messages.java
index 2877e484..0d7ec317 100644
--- a/src/main/java/fr/xephi/authme/message/Messages.java
+++ b/src/main/java/fr/xephi/authme/message/Messages.java
@@ -6,6 +6,7 @@ import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.util.expiring.Duration;
import fr.xephi.authme.util.message.I18NUtils;
+import fr.xephi.authme.util.message.MiniMessageUtils;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -129,8 +130,8 @@ public class Messages {
if (sender instanceof Player) {
displayName = ((Player) sender).getDisplayName();
}
-
- return ChatColor.translateAlternateColorCodes('&', message)
+
+ return ChatColor.translateAlternateColorCodes('&', MiniMessageUtils.parseMiniMessageToLegacy(message))
.replace(NEWLINE_TAG, "\n")
.replace(USERNAME_TAG, sender.getName())
.replace(DISPLAYNAME_TAG, displayName);
@@ -146,7 +147,7 @@ public class Messages {
private String retrieveMessage(MessageKey key, String name) {
String message = messagesFileHandler.getMessage(key.getKey());
- return ChatColor.translateAlternateColorCodes('&', message)
+ return ChatColor.translateAlternateColorCodes('&', MiniMessageUtils.parseMiniMessageToLegacy(message))
.replace(NEWLINE_TAG, "\n")
.replace(USERNAME_TAG, name)
.replace(DISPLAYNAME_TAG, name);
diff --git a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java b/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java
index 5e6f953f..4b6af1e5 100644
--- a/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java
+++ b/src/main/java/fr/xephi/authme/process/logout/AsynchronousLogout.java
@@ -13,7 +13,6 @@ import fr.xephi.authme.service.bungeecord.BungeeSender;
import fr.xephi.authme.service.bungeecord.MessageType;
import fr.xephi.authme.service.velocity.VMessageType;
import fr.xephi.authme.service.velocity.VelocitySender;
-import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.entity.Player;
import javax.inject.Inject;
@@ -65,11 +64,11 @@ public class AsynchronousLogout implements AsynchronousProcess {
PlayerAuth auth = playerCache.getAuth(name);
database.updateSession(auth);
// TODO: send an update when a messaging service will be implemented (SESSION)
- if (service.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) {
- auth.setQuitLocation(player.getLocation());
- database.updateQuitLoc(auth);
+ //if (service.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) {
+ auth.setQuitLocation(player.getLocation());
+ database.updateQuitLoc(auth);
// TODO: send an update when a messaging service will be implemented (QUITLOC)
- }
+ //} AuthMeReReloaded - Always save quit location
playerCache.removePlayer(name);
codeManager.unverify(name);
diff --git a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java
index f4b1d325..31bb7078 100644
--- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java
+++ b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java
@@ -12,7 +12,6 @@ import fr.xephi.authme.service.SessionService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.PluginSettings;
-import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -68,13 +67,16 @@ public class AsynchronousQuit implements AsynchronousProcess {
boolean wasLoggedIn = playerCache.isAuthenticated(name);
if (wasLoggedIn) {
- if (service.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) {
- Location loc = spawnLoader.getPlayerLocationOrSpawn(player);
- PlayerAuth auth = PlayerAuth.builder()
- .name(name).location(loc)
- .realName(player.getName()).build();
- database.updateQuitLoc(auth);
- }
+ //if (service.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) {
+ // AuthMeReReloaded - Always save quit location on quit
+ Location loc = spawnLoader.getPlayerLocationOrSpawn(player);
+ PlayerAuth authLoc = PlayerAuth.builder()
+ .name(name).location(loc)
+ .realName(player.getName()).build();
+ database.updateQuitLoc(authLoc);
+ // AuthMeReReloaded - Fix AuthMe#2769 -1
+ //}
+
String ip = PlayerUtils.getPlayerIp(player);
PlayerAuth auth = PlayerAuth.builder()
diff --git a/src/main/java/fr/xephi/authme/service/TeleportationService.java b/src/main/java/fr/xephi/authme/service/TeleportationService.java
index bbc204cd..c50b0129 100644
--- a/src/main/java/fr/xephi/authme/service/TeleportationService.java
+++ b/src/main/java/fr/xephi/authme/service/TeleportationService.java
@@ -14,7 +14,6 @@ import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.RestrictionSettings;
-import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.TeleportUtils;
import org.bukkit.Location;
import org.bukkit.World;
@@ -189,10 +188,8 @@ public class TeleportationService implements Reloadable {
private void performTeleportation(final Player player, final AbstractTeleportEvent event) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(() -> {
bukkitService.callEvent(event);
- if (player.isOnline() && isEventValid(event) && settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)) {
+ if (player.isOnline() && isEventValid(event)) {
TeleportUtils.teleport(player, event.getTo());
- } else if (player.isOnline() && isEventValid(event)) {
- player.teleport(event.getTo());
}
});
}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java
index a5e62c02..7becd390 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java
@@ -58,7 +58,7 @@ public final class ProtectionSettings implements SettingsHolder {
@Comment("Kicks the player that issued a command before the defined time after the join process")
public static final Property QUICK_COMMANDS_DENIED_BEFORE_MILLISECONDS =
- newProperty("Protection.quickCommands.denyCommandsBeforeMilliseconds", 3000);
+ newProperty("Protection.quickCommands.denyCommandsBeforeMilliseconds", 1000);
private ProtectionSettings() {
}
diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
index ed938518..c8469e86 100644
--- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
+++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java
@@ -48,11 +48,6 @@ public final class SecuritySettings implements SettingsHolder {
public static final Property ADVANCED_SHULKER_FIX =
newProperty("3rdPartyFeature.fixes.advancedShulkerFix", false);
- @Comment({"Choose the best teleport method by server brand?",
- "(Enable this if you are using Paper/Folia)"})
- public static final Property SMART_ASYNC_TELEPORT =
- newProperty("3rdPartyFeature.optimizes.smartAsyncTeleport", true);
-
@Comment("Send a GUI captcha to unregistered players?(Requires ProtocolLib)")
public static final Property GUI_CAPTCHA =
newProperty("3rdPartyFeature.features.captcha.guiCaptcha", false);
diff --git a/src/main/java/fr/xephi/authme/task/Updater.java b/src/main/java/fr/xephi/authme/task/Updater.java
new file mode 100644
index 00000000..5616d88a
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/task/Updater.java
@@ -0,0 +1,67 @@
+package fr.xephi.authme.task;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
+import java.util.Scanner;
+
+public class Updater {
+ private final String currentVersion;
+ private String latestVersion;
+ private static boolean isUpdateAvailable = false;
+ private static final String owner = "HaHaWTH";
+ private static final String repo = "AuthMeReReloaded";
+ private static final String UPDATE_URL = "https://api.github.com/repos/" + owner + "/" + repo + "/releases/latest";
+
+ public Updater(String currentVersion) {
+ this.currentVersion = currentVersion;
+ }
+
+
+ /**
+ * Check if there is an update available
+ * Note: This method will perform a network request!
+ *
+ * @return true if there is an update available, false otherwise
+ */
+ public boolean isUpdateAvailable() {
+ URI uri = URI.create(UPDATE_URL);
+ try {
+ URL url = uri.toURL();
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setConnectTimeout(10000);
+ conn.setReadTimeout(10000);
+ Scanner scanner = new Scanner(conn.getInputStream());
+ String response = scanner.useDelimiter("\\Z").next();
+ scanner.close();
+ String latestVersion = response.substring(response.indexOf("tag_name") + 11);
+ latestVersion = latestVersion.substring(0, latestVersion.indexOf("\""));
+ this.latestVersion = latestVersion;
+ isUpdateAvailable = !currentVersion.equals(latestVersion);
+ return isUpdateAvailable;
+ } catch (IOException ignored) {
+ this.latestVersion = null;
+ isUpdateAvailable = false;
+ return false;
+ }
+ }
+
+ public String getLatestVersion() {
+ return latestVersion;
+ }
+
+ public String getCurrentVersion() {
+ return currentVersion;
+ }
+
+ /**
+ * Returns true if there is an update available, false otherwise
+ * Must be called after {@link Updater#isUpdateAvailable()}
+ *
+ * @return A boolean indicating whether there is an update available
+ */
+ public static boolean hasUpdate() {
+ return isUpdateAvailable;
+ }
+}
diff --git a/src/main/java/fr/xephi/authme/util/PlayerUtils.java b/src/main/java/fr/xephi/authme/util/PlayerUtils.java
index d9b867d7..3c9b6f4f 100644
--- a/src/main/java/fr/xephi/authme/util/PlayerUtils.java
+++ b/src/main/java/fr/xephi/authme/util/PlayerUtils.java
@@ -10,7 +10,7 @@ public final class PlayerUtils {
// Utility class
private PlayerUtils() {
}
- private static final boolean IS_LEAVES_SERVER = Utils.isClassLoaded("top.leavesmc.leaves.LeavesConfig");
+ private static final boolean isLeavesServer = Utils.isClassLoaded("top.leavesmc.leaves.LeavesConfig") || Utils.isClassLoaded("org.leavesmc.leaves.LeavesConfig");
/**
* Returns the IP of the given player.
@@ -29,7 +29,7 @@ public final class PlayerUtils {
* @return True if the player is an NPC, false otherwise
*/
public static boolean isNpc(Player player) {
- if (IS_LEAVES_SERVER) {
+ if (isLeavesServer) {
return player.hasMetadata("NPC") || player.getAddress() == null;
} else {
return player.hasMetadata("NPC");
diff --git a/src/main/java/fr/xephi/authme/util/TeleportUtils.java b/src/main/java/fr/xephi/authme/util/TeleportUtils.java
index a6594447..4a043b17 100644
--- a/src/main/java/fr/xephi/authme/util/TeleportUtils.java
+++ b/src/main/java/fr/xephi/authme/util/TeleportUtils.java
@@ -3,24 +3,24 @@ package fr.xephi.authme.util;
import org.bukkit.Location;
import org.bukkit.entity.Player;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.concurrent.CompletableFuture;
/**
* This class is a utility class for handling async teleportation of players in game.
*/
public class TeleportUtils {
- private static Method teleportAsyncMethod;
+ private static MethodHandle teleportAsyncMethodHandle;
static {
- try {//Detect Paper class
- Class> paperClass = Class.forName("com.destroystokyo.paper.PaperConfig");
- teleportAsyncMethod = Player.class.getMethod("teleportAsync", Location.class);
- teleportAsyncMethod.setAccessible(true);
- // if detected,use teleportAsync()
- } catch (ClassNotFoundException | NoSuchMethodException e) {
- teleportAsyncMethod = null;
- //if not, set method to null
+ try {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ teleportAsyncMethodHandle = lookup.findVirtual(Player.class, "teleportAsync", MethodType.methodType(CompletableFuture.class, Location.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ teleportAsyncMethodHandle = null;
+ // if not, set method handle to null
}
}
@@ -31,10 +31,10 @@ public class TeleportUtils {
* @param location Where should the player be teleported
*/
public static void teleport(Player player, Location location) {
- if (teleportAsyncMethod != null) {
+ if (teleportAsyncMethodHandle != null) {
try {
- teleportAsyncMethod.invoke(player, location);
- } catch (IllegalAccessException | InvocationTargetException e) {
+ teleportAsyncMethodHandle.invoke(player, location);
+ } catch (Throwable throwable) {
player.teleport(location);
}
} else {
diff --git a/src/main/java/fr/xephi/authme/util/message/MiniMessageUtils.java b/src/main/java/fr/xephi/authme/util/message/MiniMessageUtils.java
new file mode 100644
index 00000000..c8f59afd
--- /dev/null
+++ b/src/main/java/fr/xephi/authme/util/message/MiniMessageUtils.java
@@ -0,0 +1,23 @@
+package fr.xephi.authme.util.message;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+
+public class MiniMessageUtils {
+ private static final MiniMessage miniMessage = MiniMessage.miniMessage();
+
+ /**
+ * Parse a MiniMessage string into a legacy string.
+ *
+ * @param message The message to parse.
+ * @return The parsed message.
+ */
+ public static String parseMiniMessageToLegacy(String message) {
+ Component component = miniMessage.deserialize(message);
+ return LegacyComponentSerializer.legacyAmpersand().serialize(component);
+ }
+
+ private MiniMessageUtils() {
+ }
+}
diff --git a/src/main/resources/messages/messages_ru.yml b/src/main/resources/messages/messages_ru.yml
index 6d9b6860..cf822763 100644
--- a/src/main/resources/messages/messages_ru.yml
+++ b/src/main/resources/messages/messages_ru.yml
@@ -1,152 +1,181 @@
+# List of global tags:
+# %nl% - Goes to new line.
+# %username% - Replaces the username of the player receiving the message.
+# %displayname% - Replaces the nickname (and colors) of the player receiving the message.
+
# Registration
registration:
- disabled: '&cРегистрация отключена.'
- name_taken: '&cИгрок с таким никнеймом уже зарегистрирован.'
- register_request: '&3Регистрация: /reg <пароль> <повтор пароля>'
- command_usage: '&cИспользование: /reg <пароль> <повтор пароля>'
- reg_only: '&4Вход только для зарегистрированных! Посетите http://сайт_сервера.ru для регистрации.'
- success: '&2Вы успешно зарегистрировались!'
- kicked_admin_registered: 'Администратор зарегистрировал вас. Авторизуйтесь снова.'
+ register_request: '&3Регистрация: /reg <пароль> <повтор пароля>'
+ command_usage: '&cИспользование: /reg <пароль> <повтор пароля>'
+ reg_only: '&4Вход только для зарегистрированных! Посетите http://сайт_сервера.ru для регистрации.'
+ kicked_admin_registered: 'Администратор зарегистрировал вас. Авторизуйтесь снова.'
+ success: '&2Вы успешно зарегистрировались!'
+ disabled: '&cРегистрация отключена.'
+ name_taken: '&cИгрок с таким никнеймом уже зарегистрирован.'
# Password errors on registration
password:
- match_error: '&cПароли не совпадают.'
- name_in_password: '&cНельзя использовать свой никнейм в качестве пароля.'
- unsafe_password: '&cТакой пароль небезопасен.'
- forbidden_characters: '&4Пароль содержит запрещённые символы. Разрешённые: %valid_chars'
- wrong_length: '&cПароль слишком длинный/короткий.'
+ match_error: '&cПароли не совпадают.'
+ name_in_password: '&cНельзя использовать свой никнейм в качестве пароля.'
+ unsafe_password: '&cТакой пароль небезопасен.'
+ forbidden_characters: '&4Пароль содержит запрещённые символы. Разрешённые: %valid_chars'
+ wrong_length: '&cПароль слишком длинный/короткий.'
+ pwned_password: '&cВыбранный вами пароль небезопасен. Он уже был использован %pwned_count раз! Пожалуйста, используйте надежный пароль...'
# Login
login:
- command_usage: '&cИспользование: /login <пароль>'
- wrong_password: '&cНеправильный пароль!'
- success: '&2Вы успешно вошли!'
- login_request: '&3Авторизация: /login <Пароль>'
- timeout_error: '&4Время авторизации истекло.'
+ command_usage: '&cИспользование: /login <пароль>'
+ wrong_password: '&cНеправильный пароль!'
+ success: '&2Вы успешно вошли!'
+ login_request: '&3Авторизация: /login <Пароль>'
+ timeout_error: '&4Время авторизации истекло.'
# Errors
error:
- denied_command: '&cНеобходимо авторизоваться для использования этой команды!'
- denied_chat: '&cНеобходимо авторизоваться, чтобы писать в чат!'
- unregistered_user: '&cИгрок с таким именем не зарегистрирован.'
- not_logged_in: '&cВы ещё не вошли!'
- no_permission: '&4Недостаточно прав.'
- unexpected_error: '&cПроизошла ошибка. Свяжитесь с администратором.'
- max_registration: '&cПревышено максимальное количество регистраций на сервере! (%reg_count/%max_acc %reg_names)'
- logged_in: '&cВы уже авторизированы!'
- kick_for_vip: '&3VIP-игрок зашёл на переполненный сервер.'
- kick_unresolved_hostname: '&cПроизошла ошибка: неразрешенное имя узла игрока!'
- tempban_max_logins: '&cВы временно заблокированы из-за большого количества неудачных попыток авторизоваться.'
+ unregistered_user: '&cИгрок с таким именем не зарегистрирован.'
+ denied_command: '&cНеобходимо авторизоваться для использования этой команды!'
+ denied_chat: '&cНеобходимо авторизоваться, чтобы писать в чат!'
+ not_logged_in: '&cВы ещё не вошли!'
+ tempban_max_logins: '&cВы временно заблокированы из-за большого количества неудачных попыток авторизоваться.'
+ max_registration: '&cПревышено максимальное количество регистраций на сервере! (%reg_count/%max_acc %reg_names)'
+ no_permission: '&4Недостаточно прав.'
+ unexpected_error: '&cПроизошла ошибка. Свяжитесь с администратором.'
+ kick_for_vip: '&3VIP-игрок зашёл на переполненный сервер.'
+ logged_in: '&cВы уже авторизированы!'
+ kick_unresolved_hostname: '&cПроизошла ошибка: неразрешенное имя узла игрока!'
# AntiBot
antibot:
- kick_antibot: 'Сработала защита против ботов! Необходимо подождать перед следующим входом на сервер.'
- auto_enabled: '&4[AuthMe] AntiBot-режим включился из-за большого количества входов!'
- auto_disabled: '&2[AuthMe] AntiBot-режим отключился спустя %m мин.'
+ kick_antibot: 'Сработала защита против ботов! Необходимо подождать перед следующим входом на сервер.'
+ auto_enabled: '&4[AuthMe] AntiBot-режим включился из-за большого количества входов!'
+ auto_disabled: '&2[AuthMe] AntiBot-режим отключился спустя %m мин.'
-# Unregister
unregister:
- success: '&cУчётная запись успешно удалена!'
- command_usage: '&cИспользование: /unregister <пароль>'
+ success: '&cУчётная запись успешно удалена!'
+ command_usage: '&cИспользование: /unregister <пароль>'
# Other messages
misc:
- account_not_activated: '&cВаша уч. запись ещё не активирована. Проверьте электронную почту!'
- password_changed: '&2Ваш пароль изменён!'
- logout: '&2Вы успешно вышли.'
- reload: '&6Конфигурация и база данных перезагружены.'
- usage_change_password: '&cИспользование: /changepassword <пароль> <новый пароль>'
- accounts_owned_self: 'У вас %count уч. записей:'
- accounts_owned_other: 'У игрока %name %count уч. записей:'
+ accounts_owned_self: 'У вас %count уч. записей:'
+ accounts_owned_other: 'У игрока %name %count уч. записей:'
+ account_not_activated: '&cВаша уч. запись ещё не активирована. Проверьте электронную почту!'
+ password_changed: '&2Ваш пароль изменён!'
+ reload: '&6Конфигурация и база данных перезагружены.'
+ usage_change_password: '&cИспользование: /changepassword <пароль> <новый пароль>'
# Session messages
session:
- valid_session: '&2Вы автоматически авторизовались!'
- invalid_session: '&cСессия некорректна. Дождитесь, пока она закончится.'
+ invalid_session: '&cСессия некорректна. Дождитесь, пока она закончится.'
+ valid_session: '&2Вы автоматически авторизовались!'
# Error messages when joining
on_join_validation:
- same_ip_online: 'Игрок с данным IP-адресом уже играет на сервере!'
- same_nick_online: '&4Игрок с данным никнеймом уже играет на сервере!'
- name_length: '&4Ваш никнейм слишком длинный/короткий.'
- characters_in_name: '&4Ваш никнейм содержит запрещённые символы. Разрешённые: %valid_chars'
- kick_full_server: '&4Сервер полон. Попробуйте зайти позже!'
- country_banned: '&4Вход с IP-адресов вашей страны запрещён на этом сервере.'
- not_owner_error: 'Вы не являетесь владельцем данной уч. записи. Выберите себе другой никнейм!'
- invalid_name_case: 'Неверный никнейм! Зайдите под никнеймом %valid, а не %invalid.'
- quick_command: 'Вы вводили команды слишком часто! Пожалуйста, переподключитесь и вводите команды медленнее.'
+ name_length: '&4Ваш никнейм слишком длинный/короткий.'
+ characters_in_name: '&4Ваш никнейм содержит запрещённые символы. Разрешённые: %valid_chars'
+ country_banned: '&4Вход с IP-адресов вашей страны запрещён на этом сервере.'
+ not_owner_error: 'Вы не являетесь владельцем данной уч. записи. Выберите себе другой никнейм!'
+ kick_full_server: '&4Сервер полон. Попробуйте зайти позже!'
+ same_nick_online: '&4Игрок с данным никнеймом уже играет на сервере!'
+ invalid_name_case: 'Неверный никнейм! Зайдите под никнеймом %valid, а не %invalid.'
+ same_ip_online: 'Игрок с данным IP-адресом уже играет на сервере!'
+ quick_command: 'Вы вводили команды слишком часто! Пожалуйста, переподключитесь и вводите команды медленнее.'
# Email
email:
- add_email_request: '&3Добавьте электронную почту: /email add <эл. почта> <повтор эл. почты>'
- usage_email_add: '&cИспользование: /email add <эл. почта> <повтор эл. почты>'
- usage_email_change: '&cИспользование: /email change <эл. почта> <новая эл. почта>'
- new_email_invalid: '&cНедействительная новая электронная почта!'
- old_email_invalid: '&cНедействительная старая электронная почта!'
- invalid: '&cНедействительный адрес электронной почты!'
- added: '&2Электронная почта успешно добавлена!'
- add_not_allowed: '&cДобавление электронной почты не было разрешено.'
- request_confirmation: '&cПодтвердите свою электронную почту!'
- changed: '&2Адрес электронной почты изменён!'
- change_not_allowed: '&cИзменение электронной почты не было разрешено.'
- email_show: '&2Текущий адрес электронной почты — &f%email'
- no_email_for_account: '&2К вашей уч. записи не привязана электронная почта.'
- already_used: '&4Эта электронная почта уже используется.'
- incomplete_settings: 'Ошибка: не все необходимые параметры установлены для отправки электронной почты. Свяжитесь с администратором.'
- send_failure: 'Письмо не может быть отправлено. Свяжитесь в администратором.'
- change_password_expired: 'Больше нельзя сменить свой пароль, используя эту команду.'
- email_cooldown_error: '&cПисьмо было отправлено недавно. Подождите %time, прежде чем отправить новое.'
+ usage_email_add: '&cИспользование: /email add <эл. почта> <повтор эл. почты>'
+ usage_email_change: '&cИспользование: /email change <эл. почта> <новая эл. почта>'
+ new_email_invalid: '&cНедействительная новая электронная почта!'
+ old_email_invalid: '&cНедействительная старая электронная почта!'
+ invalid: '&cНедействительный адрес электронной почты!'
+ added: '&2Электронная почта успешно добавлена!'
+ request_confirmation: '&cПодтвердите свою электронную почту!'
+ changed: '&2Адрес электронной почты изменён!'
+ email_show: '&2Текущий адрес электронной почты — &f%email'
+ incomplete_settings: 'Ошибка: не все необходимые параметры установлены для отправки электронной почты. Свяжитесь с администратором.'
+ already_used: '&4Эта электронная почта уже используется.'
+ send_failure: 'Письмо не может быть отправлено. Свяжитесь в администратором.'
+ no_email_for_account: '&2К вашей уч. записи не привязана электронная почта.'
+ add_email_request: '&3Добавьте электронную почту: /email add <эл. почта> <повтор эл. почты>'
+ change_password_expired: 'Больше нельзя сменить свой пароль, используя эту команду.'
+ email_cooldown_error: '&cПисьмо было отправлено недавно. Подождите %time, прежде чем отправить новое.'
+ add_not_allowed: '&cДобавление электронной почты не было разрешено.'
+ change_not_allowed: '&cИзменение электронной почты не было разрешено.'
# Password recovery by email
recovery:
- forgot_password_hint: '&Забыли пароль? Используйте «/email recovery <эл. почта>».'
- command_usage: '&cИспользование: /email recovery <эл. почта>'
- email_sent: '&2Письмо с инструкциями для восстановления было отправлено на вашу электронную почту!'
- code:
- code_sent: 'Код восстановления для сброса пароля был отправлен на электронную почту.'
- incorrect: 'Неверный код восстановления! Попыток осталось: %count.'
- tries_exceeded: 'Вы слишком много раз неверно ввели код восстановления. Используйте «/email recovery [эл. почта]», чтобы получить новый код.'
- correct: 'Код восстановления введён верно!'
- change_password: 'Используйте «/email setpassword <новый пароль>», чтобы сменить свой пароль.'
+ forgot_password_hint: '&Забыли пароль? Используйте «/email recovery <эл. почта>».'
+ command_usage: '&cИспользование: /email recovery <эл. почта>'
+ email_sent: '&2Письмо с инструкциями для восстановления было отправлено на вашу электронную почту!'
+ code:
+ code_sent: 'Код восстановления для сброса пароля был отправлен на электронную почту.'
+ incorrect: 'Неверный код восстановления! Попыток осталось: %count.'
+ tries_exceeded: 'Вы слишком много раз неверно ввели код восстановления. Используйте «/email recovery [эл. почта]», чтобы получить новый код.'
+ correct: 'Код восстановления введён верно!'
+ change_password: 'Используйте «/email setpassword <новый пароль>», чтобы сменить свой пароль.'
# Captcha
captcha:
- usage_captcha: '&3Необходимо ввести текст с каптчи. Используйте «/captcha %captcha_code»'
- wrong_captcha: '&cНеверно! Используйте «/captcha %captcha_code».'
- valid_captcha: '&2Вы успешно решили каптчу!'
- captcha_for_registration: 'Чтобы зарегистрироваться, решите каптчу используя команду: «/captcha %captcha_code»'
- register_captcha_valid: '&2Вы успешно решили каптчу! Теперь вы можете зарегистрироваться командой «/register»'
+ usage_captcha: '&3Необходимо ввести текст с каптчи. Используйте «/captcha %captcha_code»'
+ wrong_captcha: '&cНеверно! Используйте «/captcha %captcha_code».'
+ valid_captcha: '&2Вы успешно решили каптчу!'
+ captcha_for_registration: 'Чтобы зарегистрироваться, решите каптчу используя команду: «/captcha %captcha_code»'
+ register_captcha_valid: '&2Вы успешно решили каптчу! Теперь вы можете зарегистрироваться командой «/register»'
# Verification code
verification:
- code_required: '&3Эта команда чувствительна и требует подтверждения электронной почты! Проверьте свою почту и следуйте инструкциям в письме.'
- command_usage: '&cИспользование: /verification <код>'
- incorrect_code: '&cНеверный код, используйте «/verification <код>», подставив код из полученного письма.'
- success: '&2Ваша личность подтверждена! Теперь можно выполнять все чувствительные команды в текущем сеансе!'
- already_verified: '&2Вы уже можете выполнять все чувствительные команды в текущем сеансе!'
- code_expired: '&3Срок действия кода истёк! Выполните чувствительную команду, чтобы получить новый код!'
- email_needed: '&3Чтобы подтвердить вашу личность, необходимо привязать электронную почту к учётной записи!!'
-
-# Time units
-time:
- second: 'с.'
- seconds: 'с.'
- minute: 'мин.'
- minutes: 'мин.'
- hour: 'ч.'
- hours: 'ч.'
- day: 'дн.'
- days: 'дн.'
+ code_required: '&3Эта команда чувствительна и требует подтверждения электронной почты! Проверьте свою почту и следуйте инструкциям в письме.'
+ command_usage: '&cИспользование: /verification <код>'
+ incorrect_code: '&cНеверный код, используйте «/verification <код>», подставив код из полученного письма.'
+ success: '&2Ваша личность подтверждена! Теперь можно выполнять все чувствительные команды в текущем сеансе!'
+ already_verified: '&2Вы уже можете выполнять все чувствительные команды в текущем сеансе!'
+ code_expired: '&3Срок действия кода истёк! Выполните чувствительную команду, чтобы получить новый код!'
+ email_needed: '&3Чтобы подтвердить вашу личность, необходимо привязать электронную почту к учётной записи!!'
# Two-factor authentication
two_factor:
- code_created: '&2Ваш секретный код — %code. Просканируйте его здесь: %url'
- confirmation_required: 'Пожалуйста, подтвердите ваш код с помощью /2fa confirm <код>'
- code_required: 'Пожалуйста, введите ваш код двухфакторной аутентификации используя команду /2fa code <код>'
- already_enabled: 'Двухфакторная аутентификация уже активирована для вашего аккаунта!'
- enable_error_no_code: 'Код двухфакторной аутентификации не был сгенерирован или истек. Пожалуйста, введите /2fa add'
- enable_success: 'Двухфакторная аутентификация для вашего аккаунта успешно подключена'
- enable_error_wrong_code: 'Срок действия кода истек или код неверный. Введите /2fa add'
- not_enabled_error: 'Двухфакторная аутентификация не включена для вашего аккаунта. Введите /2fa add'
- removed_success: 'Двухфакторная аутентификация успешно удалена с вашего аккаунта!'
- invalid_code: 'Неверный код!'
+ code_created: '&2Ваш секретный код — %code. Просканируйте его здесь: %url'
+ confirmation_required: 'Пожалуйста, подтвердите ваш код с помощью /2fa confirm <код>'
+ code_required: 'Пожалуйста, введите ваш код двухфакторной аутентификации используя команду /2fa code <код>'
+ already_enabled: 'Двухфакторная аутентификация уже активирована для вашего аккаунта!'
+ enable_error_no_code: 'Код двухфакторной аутентификации не был сгенерирован или истек. Пожалуйста, введите /2fa add'
+ enable_success: 'Двухфакторная аутентификация для вашего аккаунта успешно подключена'
+ enable_error_wrong_code: 'Срок действия кода истек или код неверный. Введите /2fa add'
+ not_enabled_error: 'Двухфакторная аутентификация не включена для вашего аккаунта. Введите /2fa add'
+ removed_success: 'Двухфакторная аутентификация успешно удалена с вашего аккаунта!'
+ invalid_code: 'Неверный код!'
+
+# Time units
+time:
+ second: 'с.'
+ seconds: 'с.'
+ minute: 'мин.'
+ minutes: 'мин.'
+ hour: 'ч.'
+ hours: 'ч.'
+ day: 'дн.'
+ days: 'дн.'
+
+# 3rd party features: GUI Captcha
+gui_captcha:
+ success: '&aВы успешно прошли проверку на робота!'
+ bedrock_auto_verify_success: '&aВы обошли проверку на робота потому что вы играете с Bedrock!'
+ captcha_window_name: '%random Верификация'
+ captcha_clickable_name: '%random Я человек'
+ message_on_retry: '&cНеверно! %times попыток осталось'
+ denied_message_sending: '&cНеобходимо верифицироваться, чтобы писать в чат!'
+ kick_on_failed: '&cВы провалили верификацию!'
+ kick_on_timeout: '&cВремя верификации истекло!'
+
+# 3rd party features: Bedrock Auto Login
+bedrock_auto_login:
+ success: '&aВы автоматически авторизовались потому что вы играете с Bedrock!'
+
+# 3rd party features: Login Location Fix
+login_location_fix:
+ fix_portal: '&aВы застряли в портале во время авторизации.'
+ fix_underground: '&aВы застряли под землей во время авторизации.'
+ cannot_fix_underground: '&aВы застряли под землей во время авторизации, но мы не можем это исправить.'
+
+# 3rd party features: Double Login Fix
+double_login_fix:
+ fix_message: '&cВы были отключены из-за двойного входа.'