Merge remote-tracking branch 'origin/master' into gradle

# Conflicts:
#	.github/workflows/maven.yml
#	pom.xml
#	src/main/resources/plugin.yml
This commit is contained in:
HaHaWTH 2024-12-13 07:44:32 -08:00
commit 4bd22c1e91
34 changed files with 214 additions and 107 deletions

0
.github/workflows/maven.yml vendored Normal file
View File

View File

@ -27,22 +27,19 @@
10. Player login logic improvement to reduce lag
11. Automatically purge bot data
12. **Folia support (in active testing)**
13. Offhand Menu compatibility(Thats amazing)
14. **Velocity support (See [Velocity Support](./vc-support.md))**
15. Support Virtual Threads caching
16. Automatically fix portal stuck issue
17. Automatically login for Bedrock players(configurable)
18. Fix shulker box crash bug on legacy versions(MC 1.13-)
19. **H2 database support**
20. **100% compatibility with original authme and extensions**
21. More......
13. **Velocity support (See [Velocity Support](./vc-support.md))**
14. Support Virtual Threads caching
15. Automatically fix portal stuck issue
16. Automatically login for Bedrock players(configurable)
17. Fix shulker box crash bug on legacy versions(MC 1.13-)
18. **H2 database support**
19. **100% compatibility with original authme and extensions**
20. More......
**Download links:**
[Releases](https://github.com/HaHaWTH/AuthMeReReloaded/releases/latest)
[Actions(Dev builds, use at your own risk!)](https://github.com/HaHaWTH/AuthMeReReloaded/actions/workflows/maven.yml)
If you are using FRP(内网穿透) for your server, this plugin may help [HAProxy-Detector](https://github.com/HaHaWTH/HAProxy-Detector)
**Pull Requests and suggestions are welcome!**
## Building

0
pom.xml Normal file
View File

View File

@ -74,9 +74,9 @@ public class AuthMe extends JavaPlugin {
private static final int CLEANUP_INTERVAL = 5 * TICKS_PER_MINUTE;
// Version and build number values
private static String pluginVersion = "5.6.0-Fork";
private static String pluginVersion = "5.7.0-Fork";
private static final String pluginBuild = "b";
private static String pluginBuildNumber = "50";
private static String pluginBuildNumber = "53";
// Private instances
private EmailService emailService;
private CommandHandler commandHandler;

View File

@ -32,6 +32,7 @@ import java.util.Optional;
* AuthMeApi authmeApi = AuthMeApi.getInstance();
* </code>
*/
@SuppressWarnings("unused")
public class AuthMeApi {
private static AuthMeApi singleton;

View File

@ -8,6 +8,7 @@ import fr.xephi.authme.process.Management;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.service.ValidationService.ValidationResult;
import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.entity.Player;
import javax.inject.Inject;
@ -42,12 +43,15 @@ public class ChangePasswordCommand extends PlayerCommand {
commonService.send(player, MessageKey.NOT_LOGGED_IN);
return;
}
if (commonService.getProperty(SecuritySettings.CHANGE_PASSWORD_EMAIL_VERIFICATION_REQUIRED)) {
// Check if the user has been verified or not
if (codeManager.isVerificationRequired(player)) {
codeManager.codeExistOrGenerateNew(name);
commonService.send(player, MessageKey.VERIFICATION_CODE_REQUIRED);
return;
}
}
String oldPassword = arguments.get(0);
String newPassword = arguments.get(1);

View File

@ -22,6 +22,8 @@ import java.util.Locale;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static fr.xephi.authme.AuthMe.getScheduler;
public class VerificationCodeManager implements SettingsDependent, HasCleanup {
private final EmailService emailService;
@ -133,8 +135,9 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup {
* @param name the name of the player to generate a code for
*/
private void generateCode(String name) {
getScheduler().runTaskAsynchronously(() -> {
DataSourceValue<String> emailResult = dataSource.getEmail(name);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy'年'MM'月'dd'日' HH:mm:ss");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy'-'MM'-'dd'-' HH:mm:ss");
Date date = new Date(System.currentTimeMillis());
if (emailResult.rowExists()) {
final String email = emailResult.getValue();
@ -144,6 +147,7 @@ public class VerificationCodeManager implements SettingsDependent, HasCleanup {
emailService.sendVerificationMail(name, email, code, dateFormat.format(date));
}
}
});
}
/**

View File

@ -75,9 +75,7 @@ class ListenerService implements SettingsDependent {
* @param player the player to verify
* @return true if the associated event should be canceled, false otherwise
*/
public boolean shouldCancelEvent(Player player) {
return player != null && !checkAuth(player.getName()) && !PlayerUtils.isNpc(player);
}
@Override

View File

@ -16,9 +16,11 @@ import fr.xephi.authme.service.ValidationService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SpawnLoader;
import fr.xephi.authme.settings.properties.HooksSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.util.TeleportUtils;
import fr.xephi.authme.util.message.I18NUtils;
import fr.xephi.authme.util.message.MiniMessageUtils;
import org.bukkit.ChatColor;
import org.bukkit.Location;
@ -93,7 +95,6 @@ public class PlayerListener implements Listener {
@Inject
private QuickCommandsProtectionManager quickCommandsProtectionManager;
// Lowest priority to apply fast protection checks
@EventHandler(priority = EventPriority.LOWEST)
public void onAsyncPlayerPreLoginEventLowest(AsyncPlayerPreLoginEvent event) {
@ -248,6 +249,11 @@ public class PlayerListener implements Listener {
}
}
// Remove data from locale map when player quit
if (settings.getProperty(PluginSettings.I18N_MESSAGES)) {
I18NUtils.removeLocale(player.getUniqueId());
}
if (antiBotService.wasPlayerKicked(player.getName())) {
return;
}
@ -533,12 +539,4 @@ public class PlayerListener implements Listener {
event.setCancelled(true);
}
}
// @EventHandler(priority = EventPriority.LOWEST)
// public void onSwitchHand(PlayerSwapHandItemsEvent event) {
// Player player = event.getPlayer();
// if (!player.isSneaking() || !player.hasPermission("keybindings.use"))
// return;
// event.setCancelled(true);
// Bukkit.dispatchCommand(event.getPlayer(), "help");
// }
}

View File

@ -1,14 +1,10 @@
package fr.xephi.authme.listener;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.PlayerSwapHandItemsEvent;
import javax.inject.Inject;
@ -26,12 +22,4 @@ public class PlayerListenerHigherThan18 implements Listener {
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void onSwitchHand(PlayerSwapHandItemsEvent event) {
Player player = event.getPlayer();
if (player.isSneaking() && player.hasPermission("keybindings.use") && settings.getProperty(PluginSettings.MENU_UNREGISTER_COMPATIBILITY)) {
event.setCancelled(true);
Bukkit.dispatchCommand(event.getPlayer(), "help");
}
}
}

View File

@ -53,6 +53,9 @@ public class ServerListener implements Listener {
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.disable();
logger.warning("ProtocolLib has been disabled, unhooking packet adapters!");
} else if ("PlaceholderAPI".equalsIgnoreCase(pluginName)) {
pluginHookService.unhookPlaceholderApi();
logger.info("PlaceholderAPI has been disabled: unhooking placeholders");
}
}
@ -74,6 +77,8 @@ public class ServerListener implements Listener {
spawnLoader.loadCmiSpawn();
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
protocolLibService.setup();
} else if ("PlaceholderAPI".equalsIgnoreCase(pluginName)) {
pluginHookService.tryHookToPlaceholderApi();
}
}
}

View File

@ -29,8 +29,6 @@ import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.GameMode;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
import java.util.Locale;
@ -208,7 +206,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
int blindTimeOut = (registrationTimeout <= 0) ? 99999 : registrationTimeout;
// AuthMeReReloaded - Fix potion apply on Folia
bukkitService.runTaskIfFolia(player,() -> player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blindTimeOut, 2)));
bukkitService.runTaskIfFolia(player, () -> player.addPotionEffect(bukkitService.createBlindnessEffect(blindTimeOut)));
}
commandManager.runCommandsOnJoin(player);
});

View File

@ -14,8 +14,6 @@ import fr.xephi.authme.settings.commandconfig.CommandManager;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
@ -75,7 +73,7 @@ public class ProcessSyncPlayerLogout implements SynchronousProcess {
// Apply Blindness effect
if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) {
int timeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeout, 2));
player.addPotionEffect(bukkitService.createBlindnessEffect(timeout));
}
// Set player's data to unauthenticated

View File

@ -29,7 +29,7 @@ public class ProcessSyncPlayerQuit implements SynchronousProcess {
} else {
limboService.restoreData(player);
if (!UniversalScheduler.isFolia) { // AuthMeReReloaded - Fix #146 (Very stupid solution, but works)
player.saveData(); // #1238: Speed is sometimes not restored properly
// player.saveData(); // #1238: Speed is sometimes not restored properly
}
}
player.leaveVehicle();

View File

@ -23,8 +23,6 @@ import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
@ -150,7 +148,7 @@ public class AsynchronousUnregister implements AsynchronousProcess {
private void applyBlindEffect(Player player) {
if (service.getProperty(RegistrationSettings.APPLY_BLIND_EFFECT)) {
int timeout = service.getProperty(RestrictionSettings.TIMEOUT) * TICKS_PER_SECOND;
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, timeout, 2));
bukkitService.runTaskIfFolia(player, () -> player.addPotionEffect(bukkitService.createBlindnessEffect(timeout)));
}
}

View File

@ -18,6 +18,8 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import javax.inject.Inject;
import java.util.Collection;
@ -327,6 +329,16 @@ public class BukkitService implements SettingsDependent {
return event;
}
/**
* Creates a PotionEffect with blindness for the given duration in ticks.
*
* @param timeoutInTicks duration of the effect in ticks
* @return blindness potion effect
*/
public PotionEffect createBlindnessEffect(int timeoutInTicks) {
return new PotionEffect(PotionEffectType.BLINDNESS, timeoutInTicks, 2);
}
/**
* Gets the world with the given name.
*
@ -380,7 +392,11 @@ public class BukkitService implements SettingsDependent {
* @param bytes the message
*/
public void sendVelocityMessage(Player player, byte[] bytes) {
if (player != null) {
player.sendPluginMessage(authMe, "authmevelocity:main", bytes);
} else {
Bukkit.getServer().sendPluginMessage(authMe, "authmevelocity:main", bytes);
}
}

View File

@ -6,6 +6,8 @@ import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.service.hook.papi.AuthMeExpansion;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
@ -26,6 +28,8 @@ public class PluginHookService {
private Essentials essentials;
private Plugin cmi;
private MultiverseCore multiverse;
private PlaceholderAPIPlugin placeholderApi;
private AuthMeExpansion authMeExpansion;
/**
* Constructor.
@ -38,6 +42,7 @@ public class PluginHookService {
tryHookToEssentials();
tryHookToCmi();
tryHookToMultiverse();
tryHookToPlaceholderApi();
}
/**
@ -133,6 +138,20 @@ public class PluginHookService {
}
}
/**
* Attempts to create a hook into PlaceholderAPI.
*/
public void tryHookToPlaceholderApi() {
try {
placeholderApi = getPlugin(pluginManager, "PlaceholderAPI", PlaceholderAPIPlugin.class);
authMeExpansion = new AuthMeExpansion();
authMeExpansion.register();
} catch (Exception | NoClassDefFoundError ignored) {
placeholderApi = null;
authMeExpansion = null;
}
}
/**
* Attempts to create a hook into CMI.
*/
@ -180,6 +199,16 @@ public class PluginHookService {
multiverse = null;
}
/**
* Unhooks from PlaceholderAPI.
*/
public void unhookPlaceholderApi() {
if (placeholderApi != null) {
authMeExpansion.unregister();
placeholderApi = null;
}
}
// ------
// Helpers
// ------

View File

@ -144,7 +144,7 @@ public class TeleportationService implements Reloadable {
if (settings.getProperty(RestrictionSettings.SAVE_QUIT_LOCATION)) {
Location location = buildLocationFromAuth(player, auth);
Location playerLoc = player.getLocation();
if (location.getX() == playerLoc.getX() && location.getY() == location.getY() && location.getZ() == playerLoc.getZ()
if (location.getX() == playerLoc.getX() && location.getY() == playerLoc.getY() && location.getZ() == playerLoc.getZ()
&& location.getWorld() == playerLoc.getWorld()) return;
logger.debug("Teleporting `{0}` after login, based on the player auth", player.getName());
teleportBackFromSpawn(player, location);

View File

@ -0,0 +1,72 @@
package fr.xephi.authme.service.hook.papi;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.HooksSettings;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* AuthMe PlaceholderAPI expansion class.
* @author Kobe 8
*/
public class AuthMeExpansion extends PlaceholderExpansion {
private final Settings settings = AuthMe.settings;
@Override
public @NotNull String getIdentifier() {
return "authme";
}
@Override
public @NotNull String getAuthor() {
return "HaHaWTH";
}
@Override
public @NotNull String getVersion() {
return AuthMe.getPluginVersion();
}
@Override
public boolean persist() {
return true;
}
@Override
public String onRequest(OfflinePlayer player, @NotNull String params) {
if (!settings.getProperty(HooksSettings.PLACEHOLDER_API)) return null;
AuthMeApi authMeApi = AuthMeApi.getInstance();
if (authMeApi == null) return null;
if (params.equalsIgnoreCase("version")) {
return getVersion();
}
if (params.equalsIgnoreCase("is_registered")) {
if (player != null) {
Player onlinePlayer = player.getPlayer();
if (onlinePlayer != null) {
return String.valueOf(authMeApi.isRegistered(onlinePlayer.getName()));
}
}
}
if (params.equalsIgnoreCase("is_authenticated")) {
if (player != null) {
Player onlinePlayer = player.getPlayer();
if (onlinePlayer != null) {
return String.valueOf(authMeApi.isAuthenticated(onlinePlayer));
}
}
}
if (params.equalsIgnoreCase("last_login_time")) {
if (player != null) {
Player onlinePlayer = player.getPlayer();
if (onlinePlayer != null) {
return authMeApi.getLastLoginTime(onlinePlayer.getName()).toString();
}
}
}
return null;
}
}

View File

@ -129,9 +129,9 @@ public class CommandManager implements Reloadable {
if (predicate.test(cmd)) {
long delay = cmd.getDelay();
if (delay > 0) {
bukkitService.scheduleSyncDelayedTask(() -> dispatchCommand(player, cmd), delay);
bukkitService.runTaskLater(player, () -> dispatchCommand(player, cmd), delay);
} else {
dispatchCommand(player, cmd);
bukkitService.runTaskIfFolia(player, () -> dispatchCommand(player, cmd));
}
}
}

View File

@ -15,6 +15,10 @@ public final class HooksSettings implements SettingsHolder {
public static final Property<Boolean> MULTIVERSE =
newProperty("Hooks.multiverse", true);
@Comment("Do we need to hook with PlaceholderAPI for AuthMe placeholders?")
public static final Property<Boolean> PLACEHOLDER_API =
newProperty("Hooks.placeholderapi", false);
@Comment("Do we need to hook with BungeeCord?")
public static final Property<Boolean> BUNGEECORD =
newProperty("Hooks.bungeecord", false);

View File

@ -11,17 +11,10 @@ import static ch.jalu.configme.properties.PropertyInitializer.newLowercaseString
import static ch.jalu.configme.properties.PropertyInitializer.newProperty;
public final class PluginSettings implements SettingsHolder {
@Comment({
"Should we execute /help command when unregistered players press Shift+F?",
"This keeps compatibility with some menu plugins",
"If you are using TrMenu, don't enable this because TrMenu already implemented this."
})
public static final Property<Boolean> MENU_UNREGISTER_COMPATIBILITY =
newProperty("3rdPartyFeature.compatibility.menuPlugins", false);
@Comment({
"Send i18n messages to player based on their client settings, this option will override `settings.messagesLanguage`",
"(Requires Protocollib or Packetevents)",
"(Requires ProtocolLib)",
"This will not affect language of AuthMe help command."
})
public static final Property<Boolean> I18N_MESSAGES =

View File

@ -173,7 +173,7 @@ public final class RestrictionSettings implements SettingsHolder {
@Comment("Regex syntax for allowed chars in email.")
public static final Property<String> ALLOWED_EMAIL_REGEX =
newProperty("settings.restrictions.allowedEmailCharacters", "^[A-Za-z0-9_.]{3,20}@(qq|outlook|163|gmail|icloud)\\.com$");
newProperty("settings.restrictions.allowedEmailCharacters", "^([a-zA-Z0-9_.+-]+)@([a-zA-Z0-9-]+)\\.([a-zA-Z]{2,})$");
@Comment("Force survival gamemode when player joins?")

View File

@ -62,6 +62,11 @@ public final class SecuritySettings implements SettingsHolder {
public static final Property<Integer> HAVE_I_BEEN_PWNED_LIMIT =
newProperty("Security.account.haveIBeenPwned.limit", 0);
@Comment({"Require email verification when changing password if email feature enabled.",
"Original behavior is true"})
public static final Property<Boolean> CHANGE_PASSWORD_EMAIL_VERIFICATION_REQUIRED =
newProperty("Security.account.emailVerification.required", true);
@Comment("Enable captcha when a player uses wrong password too many times")
public static final Property<Boolean> ENABLE_LOGIN_FAILURE_CAPTCHA =
newProperty("Security.captcha.useCaptcha", false);

View File

@ -1,10 +1,13 @@
package fr.xephi.authme.task;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.Scanner;
public class Updater {
private final String currentVersion;
@ -32,14 +35,16 @@ public class Updater {
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);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/vnd.github+json");
try (InputStreamReader reader = new InputStreamReader(conn.getInputStream())) {
JsonObject jsonObject = new JsonParser().parse(reader).getAsJsonObject();
String latest = jsonObject.get("tag_name").getAsString();
latestVersion = latest;
isUpdateAvailable = !currentVersion.equals(latest);
reader.close();
return isUpdateAvailable;
}
} catch (IOException ignored) {
this.latestVersion = null;
isUpdateAvailable = false;

View File

@ -60,6 +60,10 @@ public class I18NUtils {
PLAYER_LOCALE.put(uuid, locale);
}
public static void removeLocale(UUID uuid) {
PLAYER_LOCALE.remove(uuid);
}
/**
* Returns the AuthMe messages file language code, by given locale and settings.
* Dreeam - Hard mapping, based on mc1.20.6, 5/29/2024

View File

@ -2,9 +2,7 @@ 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.bungeecord.BungeeComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
public class MiniMessageUtils {
private static final MiniMessage miniMessage = MiniMessage.miniMessage();
@ -19,18 +17,6 @@ public class MiniMessageUtils {
Component component = miniMessage.deserialize(message);
return LegacyComponentSerializer.legacyAmpersand().serialize(component);
}
/**
* Parse a MiniMessage string into a BaseComponent.
*
* @param message The message to parse.
* @return The parsed message.
*/
public static BaseComponent[] parseMiniMessageToBaseComponent(String message) {
Component component = miniMessage.deserialize(message);
return BungeeComponentSerializer.legacy().serialize(component);
}
private MiniMessageUtils() {
}
}

View File

@ -104,7 +104,7 @@
<td>
<p style="color:#b0adc5;">© 2024 HomoCraft. All rights reserved.</p>
<a href="1919810.com" target="_blank"
style="text-decoration: none; font-size: 16px">wdsj.in</a>
style="text-decoration: none; font-size: 16px">example.com</a>
</td>
</tr>
</tbody>

View File

@ -110,7 +110,7 @@
<td>
<p style="color:#b0adc5;">© 2024 HomoCraft. All rights reserved.</p>
<a href="1919810.com" target="_blank"
style="text-decoration: none; font-size: 16px">wdsj.in</a>
style="text-decoration: none; font-size: 16px">example.com</a>
</td>
</tr>
</tbody>

View File

@ -1,10 +1,13 @@
name: AuthMe
authors: [sgdc3, games647, Hex3l, krusic22, DGun Otto]
# noinspection YAMLSchemaValidation
name: ${pluginDescription.name}
# noinspection YAMLSchemaValidation
authors: [${pluginDescription.authors}]
website: https://github.com/HaHaWTH/AuthMeReReloaded/
description: A fork of AuthMeReloaded that contains bug fixes
main: fr.xephi.authme.AuthMe
# noinspection YAMLSchemaValidation
main: ${pluginDescription.main}
folia-supported: true
version: 5.6.0-FORK-b50
version: 5.7.0-FORK-b53
api-version: 1.13
softdepend:
- Vault
@ -17,6 +20,7 @@ softdepend:
- EssentialsSpawn
- ProtocolLib
- floodgate
- PlaceholderAPI
commands:
authme:
description: AuthMe op commands

View File

@ -103,7 +103,7 @@
<td>
<p style="color:#b0adc5;">© 2024 HomoCraft. All rights reserved.</p>
<a href="1919810.com" target="_blank"
style="text-decoration: none; font-size: 16px">wdsj.in</a>
style="text-decoration: none; font-size: 16px">example.com</a>
</td>
</tr>
</tbody>

View File

@ -101,7 +101,7 @@
<td>
<p style="color:#b0adc5;">© 2024 HomoCraft. All rights reserved.</p>
<a href="1919810.com" target="_blank"
style="text-decoration: none; font-size: 16px">wdsj.in</a>
style="text-decoration: none; font-size: 16px">example.com</a>
</td>
</tr>
</tbody>

View File

@ -103,7 +103,7 @@
<td>
<p style="color:#b0adc5;">© 2024 HomoCraft. All rights reserved.</p>
<a href="1919810.com" target="_blank"
style="text-decoration: none; font-size: 16px">wdsj.in</a>
style="text-decoration: none; font-size: 16px">example.com</a>
</td>
</tr>
</tbody>