diff --git a/src/main/java/dev/plex/Plex.java b/src/main/java/dev/plex/Plex.java index 923bf52..078772b 100644 --- a/src/main/java/dev/plex/Plex.java +++ b/src/main/java/dev/plex/Plex.java @@ -1,6 +1,5 @@ package dev.plex; -import com.google.gson.Gson; import dev.plex.admin.Admin; import dev.plex.admin.AdminList; import dev.plex.cache.DataUtils; @@ -10,6 +9,7 @@ import dev.plex.cache.SQLPlayerData; import dev.plex.config.Config; import dev.plex.handlers.CommandHandler; import dev.plex.handlers.ListenerHandler; +import dev.plex.module.ModuleManager; import dev.plex.player.PlexPlayer; import dev.plex.player.PunishedPlayer; import dev.plex.punishment.PunishmentManager; @@ -23,21 +23,25 @@ import dev.plex.util.PlexLog; import dev.plex.util.PlexUtils; import dev.plex.util.UpdateChecker; import dev.plex.world.CustomWorld; -import java.util.UUID; import lombok.Getter; import lombok.Setter; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; +import java.util.UUID; + @Getter @Setter -public class Plex extends JavaPlugin -{ +public class Plex extends JavaPlugin { private static Plex plugin; public Config config; public Config messages; public Config indefBans; + + public File modulesFolder; + private StorageType storageType = StorageType.SQLITE; private SQLConnection sqlConnection; @@ -47,6 +51,7 @@ public class Plex extends JavaPlugin private MongoPlayerData mongoPlayerData; private SQLPlayerData sqlPlayerData; + private ModuleManager moduleManager; private RankManager rankManager; private ServiceManager serviceManager; @@ -58,39 +63,43 @@ public class Plex extends JavaPlugin private String system; - public static Plex get() - { + public static Plex get() { return plugin; } @Override - public void onLoad() - { + public void onLoad() { plugin = this; config = new Config(this, "config.yml"); messages = new Config(this, "messages.yml"); indefBans = new Config(this, "indefbans.yml"); + + modulesFolder = new File(this.getDataFolder() + File.separator + "modules"); + if (!modulesFolder.exists()) modulesFolder.mkdir(); + sqlConnection = new SQLConnection(); mongoConnection = new MongoConnection(); redisConnection = new RedisConnection(); + + moduleManager = new ModuleManager(); + moduleManager.loadAllModules(); + moduleManager.loadModules(); } @Override - public void onEnable() - { + public void onEnable() { config.load(); messages.load(); indefBans.load(); + moduleManager.enableModules(); + system = config.getString("commands.permissions"); - try - { + try { PlexUtils.testConnections(); PlexLog.log("Connected to " + storageType.name().toUpperCase()); - } - catch (Exception e) - { + } catch (Exception e) { PlexLog.error("Failed to connect to " + storageType.name().toUpperCase()); e.printStackTrace(); } @@ -102,22 +111,16 @@ public class Plex extends JavaPlugin Metrics metrics = new Metrics(this, 14143); PlexLog.log("Enabled Metrics"); - if (redisConnection.isEnabled()) - { + if (redisConnection.isEnabled()) { redisConnection.getJedis(); PlexLog.log("Connected to Redis!"); - } - else - { + } else { PlexLog.log("Redis is disabled in the configuration file, not connecting."); } - if (storageType == StorageType.MONGODB) - { + if (storageType == StorageType.MONGODB) { mongoPlayerData = new MongoPlayerData(); - } - else - { + } else { sqlPlayerData = new SQLPlayerData(); } @@ -145,52 +148,46 @@ public class Plex extends JavaPlugin } @Override - public void onDisable() - { + public void onDisable() { Bukkit.getOnlinePlayers().forEach(player -> { PlexPlayer plexPlayer = PlayerCache.getPlexPlayerMap().get(player.getUniqueId()); //get the player because it's literally impossible for them to not have an object - if (plugin.getRankManager().isAdmin(plexPlayer)) - { + if (plugin.getRankManager().isAdmin(plexPlayer)) { plugin.getAdminList().removeFromCache(UUID.fromString(plexPlayer.getUuid())); } if (mongoPlayerData != null) //back to mongo checking { mongoPlayerData.update(plexPlayer); //update the player's document - } - else if (sqlPlayerData != null) //sql checking + } else if (sqlPlayerData != null) //sql checking { sqlPlayerData.update(plexPlayer); } }); - if (redisConnection.isEnabled() && redisConnection.getJedis().isConnected()) - { + if (redisConnection.isEnabled() && redisConnection.getJedis().isConnected()) { PlexLog.log("Disabling Redis/Jedis. No memory leaks in this Anarchy server!"); redisConnection.getJedis().close(); } + + moduleManager.disableModules(); } - private void generateWorlds() - { + private void generateWorlds() { PlexLog.log("Generating any worlds if needed..."); - for (String key : config.getConfigurationSection("worlds").getKeys(false)) - { + for (String key : config.getConfigurationSection("worlds").getKeys(false)) { CustomWorld.generateConfigFlatWorld(key); } PlexLog.log("Finished with world generation!"); } - private void reloadPlayers() - { + private void reloadPlayers() { Bukkit.getOnlinePlayers().forEach(player -> { PlexPlayer plexPlayer = DataUtils.getPlayer(player.getUniqueId()); PlayerCache.getPlexPlayerMap().put(player.getUniqueId(), plexPlayer); //put them into the cache PlayerCache.getPunishedPlayerMap().put(player.getUniqueId(), new PunishedPlayer(player.getUniqueId())); - if (plugin.getRankManager().isAdmin(plexPlayer)) - { + if (plugin.getRankManager().isAdmin(plexPlayer)) { Admin admin = new Admin(UUID.fromString(plexPlayer.getUuid())); admin.setRank(plexPlayer.getRankFromString()); diff --git a/src/main/java/dev/plex/command/impl/PlexCMD.java b/src/main/java/dev/plex/command/impl/PlexCMD.java index d04f050..e89010f 100644 --- a/src/main/java/dev/plex/command/impl/PlexCMD.java +++ b/src/main/java/dev/plex/command/impl/PlexCMD.java @@ -1,37 +1,38 @@ package dev.plex.command.impl; import com.google.common.collect.ImmutableList; -import dev.plex.Plex; import dev.plex.command.PlexCommand; import dev.plex.command.annotation.CommandParameters; import dev.plex.command.annotation.CommandPermissions; import dev.plex.command.exception.CommandFailException; import dev.plex.command.source.RequiredCommandSource; +import dev.plex.module.PlexModule; +import dev.plex.module.PlexModuleFile; import dev.plex.rank.enums.Rank; -import java.util.List; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.apache.commons.lang.StringUtils; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; +import java.util.stream.Collectors; + @CommandPermissions(level = Rank.OP, permission = "plex.plex", source = RequiredCommandSource.ANY) -@CommandParameters(name = "plex", usage = "/ [reload | redis]", aliases = "plexhelp", description = "Show information about Plex or reload it") -public class PlexCMD extends PlexCommand -{ +@CommandParameters(name = "plex", usage = "/ [reload | redis | modules] [reload]", aliases = "plexhelp", description = "Show information about Plex or reload it") +public class PlexCMD extends PlexCommand { // Don't modify this command @Override - protected Component execute(@NotNull CommandSender sender, @Nullable Player playerSender, String[] args) - { - if (args.length == 0) - { + protected Component execute(@NotNull CommandSender sender, @Nullable Player playerSender, String[] args) { + if (args.length == 0) { send(sender, ChatColor.LIGHT_PURPLE + "Plex - A new freedom plugin."); send(sender, ChatColor.LIGHT_PURPLE + "Plugin version: " + plugin.getDescription().getVersion()); return componentFromString(ChatColor.LIGHT_PURPLE + "Authors: " + ChatColor.GOLD + "Telesphoreo, Taahh"); } - if (args[0].equalsIgnoreCase("reload")) - { + if (args[0].equalsIgnoreCase("reload")) { checkRank(sender, Rank.SENIOR_ADMIN, "plex.reload"); plugin.config.load(); send(sender, "Reloaded config file"); @@ -43,12 +44,9 @@ public class PlexCMD extends PlexCommand plugin.getRankManager().importDefaultRanks(); send(sender, "Imported ranks"); send(sender, "Plex successfully reloaded."); - } - else if (args[0].equalsIgnoreCase("redis")) - { + } else if (args[0].equalsIgnoreCase("redis")) { checkRank(sender, Rank.SENIOR_ADMIN, "plex.redis"); - if (!plugin.getRedisConnection().isEnabled()) - { + if (!plugin.getRedisConnection().isEnabled()) { throw new CommandFailException("&cRedis is not enabled."); } plugin.getRedisConnection().getJedis().set("test", "123"); @@ -56,16 +54,25 @@ public class PlexCMD extends PlexCommand send(sender, plugin.getRedisConnection().getJedis().get("test")); plugin.getRedisConnection().getJedis().close(); } - else - { + if (args[0].equalsIgnoreCase("modules")) { + if (args.length == 1) { + return MiniMessage.miniMessage().deserialize("Modules (" + plugin.getModuleManager().getModules().size() + "): " + StringUtils.join(plugin.getModuleManager().getModules().stream().map(PlexModule::getPlexModuleFile).map(PlexModuleFile::getName).collect(Collectors.toList()), ", ")); + } + if (args[1].equalsIgnoreCase("reload")) { + plugin.getModuleManager().unloadModules(); + plugin.getModuleManager().loadAllModules(); + plugin.getModuleManager().loadModules(); + plugin.getModuleManager().enableModules(); + plugin.getModuleManager().disableModules(); + } + } else { return usage(); } return null; } @Override - public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException - { + public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { return ImmutableList.of("reload", "redis"); } } \ No newline at end of file diff --git a/src/main/java/dev/plex/module/ModuleManager.java b/src/main/java/dev/plex/module/ModuleManager.java new file mode 100644 index 0000000..5b21cec --- /dev/null +++ b/src/main/java/dev/plex/module/ModuleManager.java @@ -0,0 +1,106 @@ +package dev.plex.module; + +import com.google.common.collect.Lists; +import dev.plex.Plex; +import dev.plex.module.exception.ModuleLoadException; +import dev.plex.util.PlexLog; +import lombok.Getter; +import org.apache.logging.log4j.LogManager; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +@Getter +public class ModuleManager { + + private final List modules = Lists.newArrayList(); + + public void loadAllModules() { + this.modules.clear(); + PlexLog.debug(String.valueOf(Plex.get().getModulesFolder().listFiles().length)); + Arrays.stream(Plex.get().getModulesFolder().listFiles()).forEach(file -> { + if (file.getName().endsWith(".jar")) { + try { + URLClassLoader loader = new URLClassLoader( + new URL[]{file.toURI().toURL()}, + Plex.class.getClassLoader() + ); + + InputStreamReader internalModuleFile = new InputStreamReader(loader.getResourceAsStream("module.yml"), StandardCharsets.UTF_8); + YamlConfiguration internalModuleConfig = YamlConfiguration.loadConfiguration(internalModuleFile); + + String name = internalModuleConfig.getString("name"); + if (name == null) + { + throw new ModuleLoadException("Plex module name can't be null!"); + } + + String main = internalModuleConfig.getString("main"); + if (main == null) + { + throw new ModuleLoadException("Plex module main class can't be null!"); + } + + String description = internalModuleConfig.getString("description", "A plex module"); + String version = internalModuleConfig.getString("version", "0.1"); + + PlexModuleFile plexModuleFile = new PlexModuleFile(name, main, description, version); + Class module = (Class) Class.forName(main, true, loader); + + PlexModule plexModule = module.getConstructor().newInstance(); + plexModule.setPlex(Plex.get()); + plexModule.setPlexModuleFile(plexModuleFile); + + plexModule.setDataFolder(new File(Plex.get().getModulesFolder() + File.separator + plexModuleFile.getName())); + if (!plexModule.getDataFolder().exists()) plexModule.getDataFolder().mkdir(); + + plexModule.setLogger(LogManager.getLogger(plexModuleFile.getName())); + modules.add(plexModule); + } catch (MalformedURLException | ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + }); + } + + public void loadModules() { + this.modules.forEach(module -> { + PlexLog.log("Loading module " + module.getPlexModuleFile().getName() + " with version " + module.getPlexModuleFile().getVersion()); + module.load(); + }); + } + + public void enableModules() { + this.modules.forEach(module -> { + PlexLog.log("Enabling module " + module.getPlexModuleFile().getName() + " with version " + module.getPlexModuleFile().getVersion()); + module.enable(); + }); + } + + public void disableModules() { + this.modules.forEach(module -> { + PlexLog.log("Disabling module " + module.getPlexModuleFile().getName() + " with version " + module.getPlexModuleFile().getVersion()); + module.disable(); + }); + } + + public void unloadModules() { + this.modules.forEach(module -> { + try { + ((URLClassLoader)module.getClass().getClassLoader()).close(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + +} diff --git a/src/main/java/dev/plex/module/PlexModule.java b/src/main/java/dev/plex/module/PlexModule.java new file mode 100644 index 0000000..12f2730 --- /dev/null +++ b/src/main/java/dev/plex/module/PlexModule.java @@ -0,0 +1,26 @@ +package dev.plex.module; + +import dev.plex.Plex; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; + +@Getter +@Setter(AccessLevel.MODULE) +public abstract class PlexModule +{ + private Plex plex; + private PlexModuleFile plexModuleFile; + private File dataFolder; + private Logger logger; + + public void load() {} + + public void enable() {} + + public void disable() {} +} diff --git a/src/main/java/dev/plex/module/PlexModuleFile.java b/src/main/java/dev/plex/module/PlexModuleFile.java new file mode 100644 index 0000000..3d4ba39 --- /dev/null +++ b/src/main/java/dev/plex/module/PlexModuleFile.java @@ -0,0 +1,12 @@ +package dev.plex.module; + +import lombok.Data; + +@Data +public class PlexModuleFile +{ + private final String name; + private final String main; + private final String description; + private final String version; +} diff --git a/src/main/java/dev/plex/module/exception/ModuleLoadException.java b/src/main/java/dev/plex/module/exception/ModuleLoadException.java new file mode 100644 index 0000000..0a46640 --- /dev/null +++ b/src/main/java/dev/plex/module/exception/ModuleLoadException.java @@ -0,0 +1,10 @@ +package dev.plex.module.exception; + +public class ModuleLoadException extends RuntimeException +{ + public ModuleLoadException(String s) + { + super(s); + } + +}