1
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
package me.earthme.luminol.commands;
|
||||
|
||||
import me.earthme.luminol.config.LuminolConfig;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LuminolConfigCommand extends Command {
|
||||
public LuminolConfigCommand(){
|
||||
super("luminolconfig");
|
||||
this.setPermission("luminol.commands.luminolconfig");
|
||||
this.setDescription("Manage config file");
|
||||
this.setUsage("/luminolconfig");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args, @Nullable Location location) throws IllegalArgumentException {
|
||||
final List<String> result = new ArrayList<>();
|
||||
|
||||
if (args.length == 1){
|
||||
result.add("reload");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
||||
if (!this.testPermission(sender)){
|
||||
sender.sendMessage(Component
|
||||
.text("No permission to execute this command!")
|
||||
.color(TextColor.color(255,0,0))
|
||||
);
|
||||
}
|
||||
|
||||
if (args.length < 1){
|
||||
sender.sendMessage(
|
||||
Component
|
||||
.text("Wrong use!\n")
|
||||
.color(TextColor.color(255,0,0))
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (args[0]){
|
||||
case "reload" -> {
|
||||
LuminolConfig.reloadAsync().thenAccept(nullValue -> sender.sendMessage(
|
||||
Component
|
||||
.text("Reloaded config file!")
|
||||
.color(TextColor.color(0,255,0))
|
||||
));
|
||||
}
|
||||
|
||||
default -> sender.sendMessage(
|
||||
Component
|
||||
.text("Unknown action!\n")
|
||||
.color(TextColor.color(255,0,0))
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package me.earthme.luminol.config;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigInfo {
|
||||
String baseName();
|
||||
|
||||
String comments() default "";
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package me.earthme.luminol.config;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DoNotLoad {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.earthme.luminol.config;
|
||||
|
||||
public enum EnumConfigCategory {
|
||||
OPTIMIZATIONS("optimizations"),
|
||||
FIXES("fixes"),
|
||||
MISC("misc"),
|
||||
GAMEPLAY("gameplay"),
|
||||
EXPERIMENT("experiment");
|
||||
|
||||
private final String baseKeyName;
|
||||
|
||||
EnumConfigCategory(String baseKeyName) {
|
||||
this.baseKeyName = baseKeyName;
|
||||
}
|
||||
|
||||
public String getBaseKeyName() {
|
||||
return this.baseKeyName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package me.earthme.luminol.config;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface HotReloadUnsupported {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.earthme.luminol.config;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface IConfigModule {
|
||||
|
||||
EnumConfigCategory getCategory();
|
||||
|
||||
String getBaseName();
|
||||
|
||||
default void onLoaded(CommentedFileConfig configInstance) {}
|
||||
|
||||
default <T> T get(String keyName, T defaultValue, @NotNull CommentedFileConfig config){
|
||||
if (!config.contains(keyName)){
|
||||
config.set(keyName,defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return config.get(keyName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package me.earthme.luminol.config;
|
||||
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import io.papermc.paper.threadedregions.RegionizedServer;
|
||||
import me.earthme.luminol.commands.LuminolConfigCommand;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class LuminolConfig {
|
||||
public static final Logger logger = LogManager.getLogger();
|
||||
private static final File baseConfigFolder = new File("luminol_config");
|
||||
private static final File baseConfigFile = new File(baseConfigFolder,"luminol_global_config.toml");
|
||||
private static final Set<IConfigModule> allInstanced = new HashSet<>();
|
||||
private static CommentedFileConfig configFileInstance;
|
||||
public static boolean alreadyInited = false;
|
||||
|
||||
public static void setupLatch(){
|
||||
Bukkit.getCommandMap().register("luminolconfig","luminol",new LuminolConfigCommand());
|
||||
alreadyInited = true;
|
||||
}
|
||||
|
||||
public static void reload(){
|
||||
RegionizedServer.ensureGlobalTickThread("Reload luminol config off global region thread!");
|
||||
|
||||
dropAllInstanced();
|
||||
try {
|
||||
preLoadConfig();
|
||||
finalizeLoadConfig();
|
||||
}catch (Exception e){
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(" -> new")
|
||||
public static @NotNull CompletableFuture<Void> reloadAsync(){
|
||||
return CompletableFuture.runAsync(LuminolConfig::reload,task -> RegionizedServer.getInstance().addTask(() -> {
|
||||
try{
|
||||
task.run();
|
||||
}catch (Exception e){
|
||||
logger.error(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static void dropAllInstanced(){
|
||||
allInstanced.clear();
|
||||
}
|
||||
|
||||
public static void finalizeLoadConfig() {
|
||||
for (IConfigModule module : allInstanced) {
|
||||
module.onLoaded(configFileInstance);
|
||||
}
|
||||
}
|
||||
|
||||
public static void preLoadConfig() throws IOException {
|
||||
baseConfigFolder.mkdirs();
|
||||
|
||||
if (!baseConfigFile.exists()){
|
||||
baseConfigFile.createNewFile();
|
||||
}
|
||||
|
||||
configFileInstance = CommentedFileConfig.ofConcurrent(baseConfigFile);
|
||||
|
||||
configFileInstance.load();
|
||||
|
||||
try {
|
||||
instanceAllModule();
|
||||
loadAllModules();
|
||||
}catch (Exception e){
|
||||
logger.error("Failed to load config modules!",e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
configFileInstance.save();
|
||||
}
|
||||
|
||||
private static void loadAllModules() throws IllegalAccessException {
|
||||
for (IConfigModule instanced : allInstanced){
|
||||
loadForSingle(instanced);
|
||||
}
|
||||
}
|
||||
|
||||
private static void instanceAllModule() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||
for (Class<?> clazz : getClasses("me.earthme.luminol.config.modules")){
|
||||
if (IConfigModule.class.isAssignableFrom(clazz)){
|
||||
allInstanced.add((IConfigModule) clazz.getConstructor().newInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadForSingle(@NotNull IConfigModule singleConfigModule) throws IllegalAccessException {
|
||||
final EnumConfigCategory category = singleConfigModule.getCategory();
|
||||
|
||||
Field[] fields = singleConfigModule.getClass().getDeclaredFields();
|
||||
|
||||
for (Field field : fields) {
|
||||
int modifiers = field.getModifiers();
|
||||
if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
|
||||
boolean skipLoad = field.getAnnotation(DoNotLoad.class) != null || (alreadyInited && field.getAnnotation(HotReloadUnsupported.class) != null);
|
||||
ConfigInfo configInfo = field.getAnnotation(ConfigInfo.class);
|
||||
|
||||
if (skipLoad || configInfo == null){
|
||||
continue;
|
||||
}
|
||||
|
||||
final String fullConfigKeyName = category.getBaseKeyName() + "." + singleConfigModule.getBaseName() + "." + configInfo.baseName();
|
||||
|
||||
field.setAccessible(true);
|
||||
final Object currentValue = field.get(null);
|
||||
|
||||
if (!configFileInstance.contains(fullConfigKeyName)){
|
||||
if (currentValue == null){
|
||||
throw new UnsupportedOperationException("Config " + singleConfigModule.getBaseName() + "tried to add an null default value!");
|
||||
}
|
||||
|
||||
final String comments = configInfo.comments();
|
||||
|
||||
if (!comments.isBlank()){
|
||||
configFileInstance.setComment(fullConfigKeyName,comments);
|
||||
}
|
||||
|
||||
configFileInstance.add(fullConfigKeyName,currentValue);
|
||||
continue;
|
||||
}
|
||||
|
||||
final Object actuallyValue = configFileInstance.get(fullConfigKeyName);
|
||||
field.set(null,actuallyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Set<Class<?>> getClasses(String pack) {
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
String packageDirName = pack.replace('.', '/');
|
||||
Enumeration<URL> dirs;
|
||||
|
||||
try {
|
||||
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
|
||||
while (dirs.hasMoreElements()) {
|
||||
URL url = dirs.nextElement();
|
||||
String protocol = url.getProtocol();
|
||||
if ("file".equals(protocol)) {
|
||||
String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
|
||||
findClassesInPackageByFile(pack, filePath, classes);
|
||||
} else if ("jar".equals(protocol)) {
|
||||
JarFile jar;
|
||||
try {
|
||||
jar = ((JarURLConnection) url.openConnection()).getJarFile();
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
findClassesInPackageByJar(pack, entries, packageDirName, classes);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static void findClassesInPackageByFile(String packageName, String packagePath, Set<Class<?>> classes) {
|
||||
File dir = new File(packagePath);
|
||||
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File[] dirfiles = dir.listFiles((file) -> file.isDirectory() || file.getName().endsWith(".class"));
|
||||
if (dirfiles != null) {
|
||||
for (File file : dirfiles) {
|
||||
if (file.isDirectory()) {
|
||||
findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classes);
|
||||
} else {
|
||||
String className = file.getName().substring(0, file.getName().length() - 6);
|
||||
try {
|
||||
classes.add(Class.forName(packageName + '.' + className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, Set<Class<?>> classes) {
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
if (name.charAt(0) == '/') {
|
||||
name = name.substring(1);
|
||||
}
|
||||
if (name.startsWith(packageDirName)) {
|
||||
int idx = name.lastIndexOf('/');
|
||||
if (idx != -1) {
|
||||
packageName = name.substring(0, idx).replace('/', '.');
|
||||
}
|
||||
if (name.endsWith(".class") && !entry.isDirectory()) {
|
||||
String className = name.substring(packageName.length() + 1, name.length() - 6);
|
||||
try {
|
||||
classes.add(Class.forName(packageName + '.' + className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package me.earthme.luminol.utils;
|
||||
|
||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.plugin.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class NullPlugin extends PluginBase {
|
||||
private boolean enabled = true;
|
||||
|
||||
private final String pluginName;
|
||||
private PluginDescriptionFile pdf;
|
||||
|
||||
public NullPlugin() {
|
||||
this.pluginName = "Minecraft";
|
||||
pdf = new PluginDescriptionFile(pluginName, "1.0", "nms");
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginDescriptionFile getDescription() {
|
||||
return pdf;
|
||||
}
|
||||
// Paper start
|
||||
@Override
|
||||
public io.papermc.paper.plugin.configuration.PluginMeta getPluginMeta() {
|
||||
return pdf;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Override
|
||||
public FileConfiguration getConfig() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResource(String filename) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfig() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveDefaultConfig() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveResource(String resourcePath, boolean replace) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLogger getLogger() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginLoader getPluginLoader() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Server getServer() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNaggable() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNaggable(boolean canNag) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BiomeProvider getDefaultBiomeProvider(@NotNull String worldName, @Nullable String id) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
|
||||
// Paper start - lifecycle events
|
||||
@Override
|
||||
public @NotNull io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager<org.bukkit.plugin.Plugin> getLifecycleManager() {
|
||||
throw new UnsupportedOperationException("Not supported.");
|
||||
}
|
||||
// Paper end - lifecycle events
|
||||
}
|
||||
Reference in New Issue
Block a user