2023-12-31 17:34:42 +08:00

511 lines
22 KiB
Java

package fr.xephi.authme;
import ch.jalu.injector.Injector;
import ch.jalu.injector.InjectorBuilder;
import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.DataSourceProvider;
import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.initialization.OnStartupTasks;
import fr.xephi.authme.initialization.SettingsProvider;
import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.listener.AdvancedShulkerFixListener;
import fr.xephi.authme.listener.BedrockAutoLoginListener;
import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.DoubleLoginFixListener;
import fr.xephi.authme.listener.EntityListener;
import fr.xephi.authme.listener.GuiCaptchaHandler;
import fr.xephi.authme.listener.LoginLocationFixListener;
import fr.xephi.authme.listener.PlayerListener;
import fr.xephi.authme.listener.PlayerListener111;
import fr.xephi.authme.listener.PlayerListener19;
import fr.xephi.authme.listener.PlayerListener19Spigot;
import fr.xephi.authme.listener.PlayerListenerHigherThan18;
import fr.xephi.authme.listener.ServerListener;
import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.output.ConsoleLoggerFactory;
import fr.xephi.authme.security.crypts.Sha256;
import fr.xephi.authme.service.BackupService;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.MigrationService;
import fr.xephi.authme.service.bungeecord.BungeeReceiver;
import fr.xephi.authme.service.yaml.YamlParseException;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsWarner;
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.purge.PurgeService;
import fr.xephi.authme.util.ExceptionUtils;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitScheduler;
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.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;
/**
* The AuthMe main class.
*/
public class AuthMe extends JavaPlugin {
// Constants
private static final String PLUGIN_NAME = "AuthMeReloaded";
private static final String LOG_FILENAME = "authme.log";
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 final String pluginBuild = "b";
private static String pluginBuildNumber = "39";
// Private instances
private EmailService emailService;
private CommandHandler commandHandler;
@Inject
public static Settings settings;
private DataSource database;
private BukkitService bukkitService;
private Injector injector;
private BackupService backupService;
public static ConsoleLogger logger;
/**
* Constructor.
*/
public AuthMe() {
}
/**
* Get the plugin's build
*
* @return The plugin's build
*/
public static String getPluginBuild() {
return pluginBuild;
}
/**
* Get the plugin's name.
*
* @return The plugin's name.
*/
public static String getPluginName() {
return PLUGIN_NAME;
}
/**
* Get the plugin's version.
*
* @return The plugin's version.
*/
public static String getPluginVersion() {
return pluginVersion;
}
/**
* Get the plugin's build number.
*
* @return The plugin's build number.
*/
public static String getPluginBuildNumber() {
return pluginBuildNumber;
}
/**
* Method called when the server enables the plugin.
*/
@Override
public void onEnable() {
// Load the plugin version data from the plugin description file
loadPluginInfo(getDescription().getVersion());
// Set the Logger instance and log file path
ConsoleLogger.initialize(getLogger(), new File(getDataFolder(), LOG_FILENAME));
logger = ConsoleLoggerFactory.get(AuthMe.class);
logger.info("You are running an unofficial fork version of AuthMe!");
// Check server version
if (!isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")
|| !isClassLoaded("org.bukkit.event.player.PlayerInteractAtEntityEvent")) {
logger.warning("You are running an unsupported server version (" + getServerNameVersionSafe() + "). "
+ "AuthMe requires Spigot 1.8.X or later!");
stopOrUnload();
return;
}
// Prevent running AuthMeBridge due to major exploit issues
if (getServer().getPluginManager().isPluginEnabled("AuthMeBridge")) {
logger.warning("Detected AuthMeBridge, support for it has been dropped as it was "
+ "causing exploit issues, please use AuthMeBungee instead! Aborting!");
stopOrUnload();
return;
}
// Initialize the plugin
try {
initialize();
} catch (Throwable th) {
YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th);
if (yamlParseException == null) {
logger.logException("Aborting initialization of AuthMe:", th);
th.printStackTrace();
} else {
logger.logException("File '" + yamlParseException.getFile() + "' contains invalid YAML. "
+ "Please run its contents through http://yamllint.com", yamlParseException);
}
stopOrUnload();
return;
}
// Show settings warnings
injector.getSingleton(SettingsWarner.class).logWarningsForMisconfigurations();
// Schedule clean up task
CleanupTask cleanupTask = injector.getSingleton(CleanupTask.class);
cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
// Do a backup on start
backupService.doBackup(BackupService.BackupCause.START);
// Set up Metrics
OnStartupTasks.sendMetrics(this, settings);
if (settings.getProperty(SecuritySettings.SHOW_STARTUP_BANNER)) {
String loadColorConfig = settings.getProperty(SecuritySettings.STARTUP_BANNER_COLOR);
if (loadColorConfig.startsWith("§") && !loadColorConfig.endsWith("§") && (loadColorConfig.length() % 2 == 0)) {
logger.info("\n" + loadColorConfig + " ___ __ __ __ ___ \n" +
loadColorConfig + " / | __ __/ /_/ /_ / |/ /__ \n" +
loadColorConfig + " / /| |/ / / / __/ __ \\/ /|_/ / _ \\\n" +
loadColorConfig + " / ___ / /_/ / /_/ / / / / / / __/\n" +
loadColorConfig + "/_/ |_\\__,_/\\__/_/ /_/_/ /_/\\___/ \n" +
loadColorConfig + " ");
} else {
logger.info("\n" + " ___ __ __ __ ___ \n" +
" / | __ __/ /_/ /_ / |/ /__ \n" +
" / /| |/ / / / __/ __ \\/ /|_/ / _ \\\n" +
" / ___ / /_/ / /_/ / / / / / / __/\n" +
"/_/ |_\\__,_/\\__/_/ /_/_/ /_/\\___/ \n" +
" ");
}
}
// Successful message
//detect server brand with classloader
checkServerType();
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();
}
}
//Migrated
/**
* Load the version and build number of the plugin from the description file.
*
* @param versionRaw the version as given by the plugin description file
*/
private static void loadPluginInfo(String versionRaw) {
int index = versionRaw.lastIndexOf("-");
if (index != -1) {
pluginVersion = versionRaw.substring(0, index);
pluginBuildNumber = versionRaw.substring(index + 1);
if (pluginBuildNumber.startsWith("b")) {
pluginBuildNumber = pluginBuildNumber.substring(1);
}
}
}
/**
* Initialize the plugin and all the services.
*/
private void initialize() {
// Create plugin folder
getDataFolder().mkdir();
// Create injector, provide elements from the Bukkit environment and register providers
injector = new InjectorBuilder()
.addDefaultHandlers("fr.xephi.authme")
.create();
injector.register(AuthMe.class, this);
injector.register(Server.class, getServer());
injector.register(PluginManager.class, getServer().getPluginManager());
injector.register(BukkitScheduler.class, getServer().getScheduler());
injector.provide(DataFolder.class, getDataFolder());
injector.registerProvider(Settings.class, SettingsProvider.class);
injector.registerProvider(DataSource.class, DataSourceProvider.class);
// Get settings and set up logger
settings = injector.getSingleton(Settings.class);
ConsoleLoggerFactory.reloadSettings(settings);
OnStartupTasks.setupConsoleFilter(getLogger());
// Set all service fields on the AuthMe class
instantiateServices(injector);
// Convert deprecated PLAINTEXT hash entries
MigrationService.changePlainTextToSha256(settings, database, new Sha256());
// If the server is empty (fresh start) just set all the players as unlogged
if (bukkitService.getOnlinePlayers().isEmpty()) {
database.purgeLogged();
}
// Register event listeners
registerEventListeners(injector);
// Start Email recall task if needed
OnStartupTasks onStartupTasks = injector.newInstance(OnStartupTasks.class);
onStartupTasks.scheduleRecallEmailTask();
}
/**
* Instantiates all services.
*
* @param injector the injector
*/
void instantiateServices(Injector injector) {
database = injector.getSingleton(DataSource.class);
bukkitService = injector.getSingleton(BukkitService.class);
commandHandler = injector.getSingleton(CommandHandler.class);
emailService = injector.getSingleton(EmailService.class);
backupService = injector.getSingleton(BackupService.class);
// Trigger instantiation (class not used elsewhere)
injector.getSingleton(BungeeReceiver.class);
// Trigger construction of API classes; they will keep track of the singleton
injector.getSingleton(AuthMeApi.class);
}
/**
* Registers all event listeners.
*
* @param injector the injector
*/
void registerEventListeners(Injector injector) {
// Get the plugin manager instance
PluginManager pluginManager = getServer().getPluginManager();
// Register event listeners
pluginManager.registerEvents(injector.getSingleton(PlayerListener.class), this);
pluginManager.registerEvents(injector.getSingleton(BlockListener.class), this);
pluginManager.registerEvents(injector.getSingleton(EntityListener.class), this);
pluginManager.registerEvents(injector.getSingleton(ServerListener.class), this);
// Try to register 1.8+ player listeners
if (isClassLoaded("org.bukkit.event.entity.EntityPickupItemEvent") && isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListenerHigherThan18.class), this);
} else if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this);
}
// Try to register 1.9 player listeners(Moved to else-if)
// if (isClassLoaded("org.bukkit.event.player.PlayerSwapHandItemsEvent")) {
// pluginManager.registerEvents(injector.getSingleton(PlayerListener19.class), this);
// }
// Try to register 1.9 spigot player listeners
if (isClassLoaded("org.spigotmc.event.player.PlayerSpawnLocationEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListener19Spigot.class), this);
}
// Register listener for 1.11 events if available
if (isClassLoaded("org.bukkit.event.entity.EntityAirChangeEvent")) {
pluginManager.registerEvents(injector.getSingleton(PlayerListener111.class), this);
}
//Register 3rd party listeners
if (settings.getProperty(SecuritySettings.GUI_CAPTCHA) && getServer().getPluginManager().getPlugin("ProtocolLib") != null) {
pluginManager.registerEvents(injector.getSingleton(GuiCaptchaHandler.class), this);
logger.info("(Beta)GUICaptcha is enabled successfully!");
logger.info("These features are still in early development, if you encountered any problem, please report.");
} else if (settings.getProperty(SecuritySettings.GUI_CAPTCHA) && getServer().getPluginManager().getPlugin("ProtocolLib") == null) {
logger.warning("ProtocolLib is not loaded, can't enable GUI Captcha.");
}
if (settings.getProperty(SecuritySettings.FORCE_LOGIN_BEDROCK) && settings.getProperty(HooksSettings.HOOK_FLOODGATE_PLAYER) && getServer().getPluginManager().getPlugin("floodgate") != null) {
pluginManager.registerEvents(injector.getSingleton(BedrockAutoLoginListener.class), this);
} else if (settings.getProperty(SecuritySettings.FORCE_LOGIN_BEDROCK) && (!settings.getProperty(HooksSettings.HOOK_FLOODGATE_PLAYER) || getServer().getPluginManager().getPlugin("floodgate") == null)) {
logger.warning("Failed to enable BedrockAutoLogin, ensure hookFloodgate: true and floodgate is loaded.");
}
if (settings.getProperty(SecuritySettings.LOGIN_LOC_FIX_SUB_UNDERGROUND) || settings.getProperty(SecuritySettings.LOGIN_LOC_FIX_SUB_PORTAL)) {
pluginManager.registerEvents(injector.getSingleton(LoginLocationFixListener.class), this);
}
if (settings.getProperty(SecuritySettings.ANTI_GHOST_PLAYERS)) {
pluginManager.registerEvents(injector.getSingleton(DoubleLoginFixListener.class), this);
}
if (settings.getProperty(SecuritySettings.ADVANCED_SHULKER_FIX) && !isClassLoaded("org.bukkit.event.player.PlayerCommandSendEvent")) {
pluginManager.registerEvents(injector.getSingleton(AdvancedShulkerFixListener.class), this);
} else if (settings.getProperty(SecuritySettings.ADVANCED_SHULKER_FIX) && isClassLoaded("org.bukkit.event.player.PlayerCommandSendEvent")) {
logger.warning("You are running an 1.13+ minecraft server, advancedShulkerFix won't enable.");
}
}
/**
* Stops the server or disables the plugin, as defined in the configuration.
*/
public void stopOrUnload() {
if (settings == null || settings.getProperty(SecuritySettings.STOP_SERVER_ON_PROBLEM)) {
getLogger().warning("THE SERVER IS GOING TO SHUT DOWN AS DEFINED IN THE CONFIGURATION!");
setEnabled(false);
getServer().shutdown();
} else {
setEnabled(false);
}
}
@Override
public void onDisable() {
// onDisable is also called when we prematurely abort, so any field may be null
OnShutdownPlayerSaver onShutdownPlayerSaver = injector == null
? null
: injector.createIfHasDependencies(OnShutdownPlayerSaver.class);
if (onShutdownPlayerSaver != null) {
onShutdownPlayerSaver.saveAllPlayers();
}
if (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));
}
// Do backup on stop if enabled
if (backupService != null) {
backupService.doBackup(BackupService.BackupCause.STOP);
}
// Wait for tasks and close data source
new TaskCloser(this, database).run();
// Disabled correctly
Consumer<String> infoLogMethod = logger == null ? getLogger()::info : logger::info;
infoLogMethod.accept("AuthMe " + this.getDescription().getVersion() + " is unloaded successfully!");
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...");
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: github.com/HaHaWTH/AuthMeReReloaded/releases/latest");
} else {
getLogger().log(Level.INFO, "You are running the latest version.");
}
} catch (IOException e) {
String formattedException = ExceptionUtils.formatException(e);
getLogger().log(Level.WARNING, "Error occurred while checking updates from GitHub. Reason: " + formattedException);
}
});
}
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() {
if (isClassLoaded("com.destroystokyo.paper.PaperConfig")) {
logger.info("AuthMeReReloaded is running on Paper");
} else if (isClassLoaded("catserver.server.CatServerConfig")) {
logger.info("AuthMeReReloaded is running on CatServer");
} else if (isClassLoaded("org.spigotmc.SpigotConfig")) {
logger.info("AuthMeReReloaded is running on Spigot");
} else if (isClassLoaded("org.bukkit.craftbukkit.CraftServer")) {
logger.info("AuthMeReReloaded is running on Bukkit");
} else {
logger.info("AuthMeReReloaded is running on Unknown*");
}
}
/**
* Handle Bukkit commands.
*
* @param sender The command sender (Bukkit).
* @param cmd The command (Bukkit).
* @param commandLabel The command label (Bukkit).
* @param args The command arguments (Bukkit).
* @return True if the command was executed, false otherwise.
*/
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd,
@NotNull String commandLabel, String[] args) {
// Make sure the command handler has been initialized
if (commandHandler == null) {
getLogger().severe("AuthMe command handler is not available");
return false;
}
// Handle the command
return commandHandler.processCommand(sender, commandLabel, args);
}
private String getServerNameVersionSafe() {
try {
Server server = getServer();
return server.getName() + " v. " + server.getVersion();
} catch (Throwable ignore) {
return "-";
}
}
}