511 lines
21 KiB
Java
511 lines
21 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 java.io.File;
|
||
import java.io.IOException;
|
||
import java.net.HttpURLConnection;
|
||
|
||
import fr.xephi.authme.initialization.SettingsProvider;
|
||
import fr.xephi.authme.initialization.TaskCloser;
|
||
import fr.xephi.authme.listener.BlockListener;
|
||
import fr.xephi.authme.listener.EntityListener;
|
||
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.PlayerQuitListener;
|
||
import fr.xephi.authme.listener.GuiCaptchaHandler;
|
||
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.SecuritySettings;
|
||
import fr.xephi.authme.task.CleanupTask;
|
||
import fr.xephi.authme.task.purge.PurgeService;
|
||
import fr.xephi.authme.util.ExceptionUtils;
|
||
import org.bukkit.Bukkit;
|
||
import org.bukkit.Server;
|
||
import org.bukkit.command.Command;
|
||
import org.bukkit.command.CommandSender;
|
||
import org.bukkit.plugin.Plugin;
|
||
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.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 = "22";
|
||
protected final Boolean SHAEnabled = false;
|
||
// 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;
|
||
private ConsoleLogger logger;
|
||
/**
|
||
* Constructor.
|
||
*/
|
||
public AuthMe() {
|
||
}
|
||
|
||
|
||
/**
|
||
* 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("你正在运行不受支持的服务器版本 (" + getServerNameVersionSafe() + "). "
|
||
+ "AuthMe 仅支持Spigot 1.9及之后的版本!");
|
||
stopOrUnload();
|
||
return;
|
||
}
|
||
// Prevent running AuthMeBridge due to major exploit issues
|
||
if (getServer().getPluginManager().isPluginEnabled("AuthMeBridge")) {
|
||
logger.warning("检测到 AuthMeBridge被加载, 对AuthMeBridge的支持已经停止 "
|
||
+ "且可能会造成严重漏洞! 已中止加载!");
|
||
stopOrUnload();
|
||
return;
|
||
}
|
||
|
||
// Initialize the plugin
|
||
try {
|
||
initialize();
|
||
} catch (Throwable th) {
|
||
YamlParseException yamlParseException = ExceptionUtils.findThrowableInCause(YamlParseException.class, th);
|
||
if (yamlParseException == null) {
|
||
logger.logException("已中止AuthMeReReloaded的初始化,原因:", th);
|
||
th.printStackTrace();
|
||
} else {
|
||
logger.logException("文件 '" + yamlParseException.getFile() + "' 包含YAML语法错误. "
|
||
+ "请尝试在 https://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);
|
||
// 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();
|
||
// 注册玩家退出事件监听
|
||
if (settings.getProperty(SecuritySettings.ANTI_GHOST_PLAYERS) || settings.getProperty(SecuritySettings.SMART_ASYNC_TELEPORT)/* || settings.getProperty(SecuritySettings.GUI_CAPTCHA)*/) {
|
||
if (settings.getProperty(SecuritySettings.ANTI_GHOST_PLAYERS)) {
|
||
getServer().getPluginManager().registerEvents(new PlayerQuitListener((Plugin) this), this);
|
||
}
|
||
if (settings.getProperty(SecuritySettings.GUI_CAPTCHA) && getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
||
getServer().getPluginManager().registerEvents(new GuiCaptchaHandler((Plugin) this), this);
|
||
logger.info("(Alpha3)GUICaptcha Feature is enabled successfully!");
|
||
logger.info("These features are still in development, if you encountered any problem, please report.");
|
||
|
||
} else if (settings.getProperty(SecuritySettings.GUI_CAPTCHA) && !getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
||
logger.warning("ProtocolLib is not loaded, we can't enable GUI Captcha.");
|
||
}
|
||
//logger.info("以上功能尚在测试中,如有问题请反馈,如需关闭请前往config.yml修改");
|
||
logger.info("GitHub Issue: github.com/HaHaWTH/AuthMeReReloaded/issues");
|
||
}
|
||
if (settings.getProperty(SecuritySettings.CHECK_FOR_UPDATES)) {
|
||
checkForUpdates();
|
||
}
|
||
|
||
if (SHAEnabled){
|
||
//shaChecker();
|
||
}
|
||
}
|
||
public File pluginfile = getFile();
|
||
/**
|
||
* 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.9 player listeners
|
||
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);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 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...");
|
||
Bukkit.getScheduler().runTaskAsynchronously(this, () -> {
|
||
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 ((pluginBuild + pluginBuildNumber).equals(latestVersion)) {
|
||
getLogger().log(Level.INFO,"You are running the latest version.");
|
||
}
|
||
if (!(pluginBuild + pluginBuildNumber).equals(latestVersion)) {
|
||
// Display update message
|
||
String message = "New version available! Latest:" + latestVersion + " Current:" + pluginBuild + pluginBuildNumber;
|
||
getLogger().log(Level.INFO, message);
|
||
getLogger().log(Level.INFO,"Download from here:github.com/HaHaWTH/AuthMeReReloaded/releases/latest");
|
||
}
|
||
}catch (IOException e) {
|
||
getLogger().log(Level.WARNING,"Error occurred while checking updates from GitHub. Reason: " + e.getMessage());
|
||
}
|
||
});
|
||
}
|
||
|
||
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*");
|
||
}
|
||
}
|
||
// 其他方法和事件处理
|
||
|
||
|
||
// 其他方法和事件处理
|
||
private static final String SHA_URL = "https://raw.githubusercontent.com/"+ owner +"/"+ repo + "/master/"+pluginBuild +pluginBuildNumber+ ".sha";
|
||
private static final String ALGORITHM = "SHA-256";
|
||
private static final String PROXY_URL = "https://ghproxy.com/";
|
||
private static final String SHA_URL_GITEE = "https://gitee.com/"+ owner_gitee +"/"+ repo + "/raw/master/"+pluginBuild+pluginBuildNumber+ ".sha";
|
||
|
||
// public void shaChecker() {
|
||
// // 请求SHA文件
|
||
//
|
||
// String actualSha;
|
||
// try {
|
||
// URL url;
|
||
// if(settings.getProperty(SecuritySettings.SHA_CHECK_METHOD).equals("github")) {
|
||
// url = new URL(SHA_URL);
|
||
// logger.info("正在检查文件完整性...(GitHub)");
|
||
// } else if(settings.getProperty(SecuritySettings.SHA_CHECK_METHOD).equals("ghproxy")) {
|
||
// url = new URL(PROXY_URL + SHA_URL);
|
||
// logger.info("正在检查文件完整性...(GhProxy)");
|
||
// } else if (settings.getProperty(SecuritySettings.SHA_CHECK_METHOD).equals("gitee")) {
|
||
// url = new URL(SHA_URL_GITEE);
|
||
// logger.info("正在检查文件完整性...(Gitee)");
|
||
// }else {
|
||
// logger.warning("未知的SHA检查方法,将从GitHub获取SHA文件");
|
||
// url = new URL(SHA_URL);
|
||
// }
|
||
//
|
||
//
|
||
// HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||
// conn.setConnectTimeout(10000);
|
||
// conn.setReadTimeout(9000);
|
||
// conn.setRequestMethod("GET");
|
||
// InputStream stream = conn.getInputStream();
|
||
// ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||
// byte[] buffer = new byte[1024];
|
||
// int length;
|
||
// while ((length = stream.read(buffer)) != -1) {
|
||
// result.write(buffer, 0, length);
|
||
// }
|
||
// String expectedSha = result.toString().trim();
|
||
// // 计算插件文件的SHA值
|
||
// MessageDigest md = MessageDigest.getInstance(ALGORITHM);
|
||
// byte[] fileBytes = Files.readAllBytes(pluginfile.toPath());
|
||
// byte[] hashBytes = md.digest(fileBytes);
|
||
// StringBuilder sb = new StringBuilder();
|
||
// for (byte b : hashBytes) {
|
||
// sb.append(String.format("%02x", b));
|
||
// }
|
||
// actualSha = sb.toString();
|
||
//
|
||
// // 比较SHA值并加载插件
|
||
// if (expectedSha.equals(actualSha)) {
|
||
// logger.info("SHA联网安全校验完毕");
|
||
// } else {
|
||
// // SHA值不匹配,插件可能被篡改
|
||
// logger.warning("SHA值不匹配,插件被篡改");
|
||
// stopOrUnload();
|
||
// }
|
||
// }catch (NoSuchAlgorithmException | IOException e){
|
||
// logger.warning("SHA校验失败,请尝试切换校验API");
|
||
// logger.warning("您当前请求的API为:" + settings.getProperty(SecuritySettings.SHA_CHECK_METHOD));
|
||
// stopOrUnload();
|
||
// }
|
||
// }
|
||
|
||
|
||
/**
|
||
* 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 "-";
|
||
}
|
||
}
|
||
}
|