diff --git a/BuildConfigUpdater/BuildConfigUpdater.iml b/BuildConfigUpdater/BuildConfigUpdater.iml index 04dfdc0..74e6815 100644 --- a/BuildConfigUpdater/BuildConfigUpdater.iml +++ b/BuildConfigUpdater/BuildConfigUpdater.iml @@ -19,6 +19,7 @@ + diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java index 755a32a..6b44848 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTP.java @@ -8,6 +8,8 @@ import org.bukkit.*; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import java.util.logging.Logger; + // @formatter:off @SuppressWarnings("FieldCanBeLocal")@CommandClass public class RandomTP extends TBMCCommandBase @@ -53,15 +55,15 @@ public class RandomTP extends TBMCCommandBase public void onEnable(Component component) { - System.out.println("Adding command"); TBMCChatAPI.AddCommand(component, this); - System.out.println("Getting world"); world = Bukkit.getWorld("World"); - System.out.println("Getting border"); border = world.getWorldBorder(); - System.out.println("Getting new location"); - System.out.println("Success: "+newLocation()); //TODO: It takes 10-30 seconds to find a location (newLocation() was there) + Logger logger = component.getPlugin().getLogger(); + logger.info("Getting new location"); + if(border.getSize() > 100000) + logger.warning("World border is wide, it may take a minute..."); + logger.info("Success: "+newLocation()); } /*================================================================================================*/ diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java index 0f1fb5e..a31bef9 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/randomtp/RandomTPComponent.java @@ -6,6 +6,7 @@ import buttondevteam.lib.architecture.Component; /** * Teleport player to random location within world border. * Every five players teleport to the same general area, and then a new general area is randomly selected for the next five players. + * Author: github.com/iiegit */ public class RandomTPComponent extends Component { @Override diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java index 2dab750..7ad07aa 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/spawn/SpawnComponent.java @@ -20,6 +20,9 @@ import org.bukkit.plugin.messaging.PluginMessageListener; import java.io.*; import java.math.BigDecimal; +/** + * Provides a /spawn command that works with BungeeCord. Make sure to set up on each server. + */ public class SpawnComponent extends Component implements PluginMessageListener { @Override protected void enable() { @@ -72,7 +75,7 @@ public class SpawnComponent extends Component implements PluginMessa } /** - * Set to empty if this server is the target. + * The BungeeCord server that has the spawn. Set to empty if this server is the target. */ private ConfigData targetServer() { return getConfig().getData("targetServer", ""); diff --git a/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java b/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java index 94ffe55..3eff08d 100644 --- a/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java +++ b/ButtonCore/src/main/java/buttondevteam/core/component/votifier/VotifierComponent.java @@ -39,11 +39,11 @@ public class VotifierComponent extends Component { getPlugin().getLogger().info("Vote: " + vote); org.bukkit.OfflinePlayer op = Bukkit.getOfflinePlayer(vote.getUsername()); Player p = Bukkit.getPlayer(vote.getUsername()); - if (op != null) { + /*if (op != null) { economy.depositPlayer(op, rewardAmount().get()); } if (p != null) { p.sendMessage("§bThanks for voting! $50 was added to your account."); - } + }*/ } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java index d22f192..d32fa13 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ButtonPlugin.java @@ -6,12 +6,16 @@ import buttondevteam.lib.chat.Command2MC; import buttondevteam.lib.chat.TBMCChatAPI; import lombok.AccessLevel; import lombok.Getter; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.util.Arrays; import java.util.Optional; import java.util.Stack; @@ -21,14 +25,15 @@ public abstract class ButtonPlugin extends JavaPlugin { private static Command2MC command2MC = new Command2MC(); @Getter(AccessLevel.PROTECTED) private IHaveConfig iConfig; + private CommentedConfiguration yaml; @Getter(AccessLevel.PROTECTED) private IHaveConfig data; //TODO - private boolean loaded = false; /** * Used to unregister components in the right order - and to reload configs */ @Getter private Stack> componentStack = new Stack<>(); + ; protected abstract void pluginEnable(); @@ -56,8 +61,8 @@ public abstract class ButtonPlugin extends JavaPlugin { } private void loadConfig() { - var section = super.getConfig().getConfigurationSection("global"); - if (section == null) section = super.getConfig().createSection("global"); + var section = getConfig().getConfigurationSection("global"); + if (section == null) section = getConfig().createSection("global"); iConfig = new IHaveConfig(section, this::saveConfig); } @@ -88,15 +93,37 @@ public abstract class ButtonPlugin extends JavaPlugin { } public boolean justReload() { - if (loaded && ConfigData.saveNow(getConfig())) { + if (yaml != null && ConfigData.saveNow(getConfig())) { getLogger().warning("Saved pending configuration changes to the file, didn't reload (try again)."); return false; } - super.reloadConfig(); - loaded = true; //Needed because for the first time it uses reloadConfig() to load it + yaml = new CommentedConfiguration(new File(getDataFolder(), "config.yml")); + yaml.load(); + var res = getTextResource("configHelp.yml"); + if (res == null) + return true; + var yc = YamlConfiguration.loadConfiguration(res); + for (var kv : yc.getValues(true).entrySet()) + if (kv.getValue() instanceof String) + yaml.addComment(kv.getKey(), + Arrays.stream(((String) kv.getValue()).split("\n")) + .map(str -> "# " + str.trim()).toArray(String[]::new)); return true; } + @Override + public FileConfiguration getConfig() { + if (yaml == null) + justReload(); + return yaml; + } + + @Override + public void saveConfig() { + if (yaml != null) + yaml.save(); + } + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ConfigOpts { diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/CommentedConfiguration.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/CommentedConfiguration.java new file mode 100644 index 0000000..f7351ff --- /dev/null +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/CommentedConfiguration.java @@ -0,0 +1,227 @@ +package buttondevteam.lib.architecture; + +import com.palmergames.util.FileMgmt; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.file.YamlConstructor; +import org.bukkit.configuration.file.YamlRepresenter; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.representer.Representer; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; + +/** + * A copy of Towny's CommentedConfiguration: https://github.com/TownyAdvanced/Towny/blob/master/src/com/palmergames/bukkit/config/CommentedConfiguration.java + * + * @author dumptruckman & Articdive + */ +public class CommentedConfiguration extends YamlConfiguration { //TODO: Remove FileMgmt dependency + private HashMap comments; + private File file; + + private final DumperOptions yamlOptions = new DumperOptions(); + private final Representer yamlRepresenter = new YamlRepresenter(); + private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions); + + public CommentedConfiguration(File file) { + + super(); + comments = new HashMap<>(); + this.file = file; + } + + public boolean load() { + + boolean loaded = true; + + try { + this.load(file); + } catch (InvalidConfigurationException | IOException e) { + loaded = false; + } + + return loaded; + } + + public void save() { + + boolean saved = true; + + // Save the config just like normal + try { + this.save(file); + + } catch (Exception e) { + saved = false; + } + + // if there's comments to add and it saved fine, we need to add comments + if (!comments.isEmpty() && saved) { + // String array of each line in the config file + String[] yamlContents = FileMgmt.convertFileToString(file).split("[" + System.getProperty("line.separator") + "]"); + + // This will hold the newly formatted line + StringBuilder newContents = new StringBuilder(); + // This holds the current path the lines are at in the config + String currentPath = ""; + // This flags if the line is a node or unknown text. + boolean node; + // The depth of the path. (number of words separated by periods - 1) + int depth = 0; + + // Loop through the config lines + for (String line : yamlContents) { + // If the line is a node (and not something like a list value) + if (line.contains(": ") || (line.length() > 1 && line.charAt(line.length() - 1) == ':')) { + + // This is a node so flag it as one + node = true; + + // Grab the index of the end of the node name + int index; + index = line.indexOf(": "); + if (index < 0) { + index = line.length() - 1; + } + // If currentPath is empty, store the node name as the currentPath. (this is only on the first iteration, i think) + if (currentPath.isEmpty()) { + currentPath = line.substring(0, index); + } else { + // Calculate the whitespace preceding the node name + int whiteSpace = 0; + for (int n = 0; n < line.length(); n++) { + if (line.charAt(n) == ' ') { + whiteSpace++; + } else { + break; + } + } + // Find out if the current depth (whitespace * 2) is greater/lesser/equal to the previous depth + if (whiteSpace / 2 > depth) { + // Path is deeper. Add a . and the node name + currentPath += "." + line.substring(whiteSpace, index); + depth++; + } else if (whiteSpace / 2 < depth) { + // Path is shallower, calculate current depth from whitespace (whitespace / 2) and subtract that many levels from the currentPath + int newDepth = whiteSpace / 2; + for (int i = 0; i < depth - newDepth; i++) { + currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + } + // Grab the index of the final period + int lastIndex = currentPath.lastIndexOf("."); + if (lastIndex < 0) { + // if there isn't a final period, set the current path to nothing because we're at root + currentPath = ""; + } else { + // If there is a final period, replace everything after it with nothing + currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + currentPath += "."; + } + // Add the new node name to the path + currentPath += line.substring(whiteSpace, index); + // Reset the depth + depth = newDepth; + } else { + // Path is same depth, replace the last path node name to the current node name + int lastIndex = currentPath.lastIndexOf("."); + if (lastIndex < 0) { + // if there isn't a final period, set the current path to nothing because we're at root + currentPath = ""; + } else { + // If there is a final period, replace everything after it with nothing + currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + currentPath += "."; + } + //currentPath = currentPath.replace(currentPath.substring(currentPath.lastIndexOf(".")), ""); + currentPath += line.substring(whiteSpace, index); + + } + + } + + } else { + node = false; + } + + if (node) { + // If there's a comment for the current path, retrieve it and flag that path as already commented + String comment = comments.get(currentPath); + + if (comment != null) { + // Add the comment to the beginning of the current line + line = comment + System.getProperty("line.separator") + line + System.getProperty("line.separator"); + } else { + // Add a new line as it is a node, but has no comment + line += System.getProperty("line.separator"); + } + } + // Add the (modified) line to the total config String + if (!node) { + newContents.append(line).append(System.getProperty("line.separator")); + } else { + newContents.append(line); + } + } + + /* + * Due to a Bukkit Bug with the Configuration + * we just need to remove any extra comments at the start of a file. + */ + while (newContents.toString().startsWith(" " + System.getProperty("line.separator"))) { + newContents = new StringBuilder(newContents.toString().replaceFirst(" " + System.getProperty("line.separator"), "")); + } + FileMgmt.stringToFile(newContents.toString(), file); + } + } + + /** + * Adds a comment just before the specified path. The comment can be + * multiple lines. An empty string will indicate a blank line. + * + * @param path Configuration path to add comment. + * @param commentLines Comments to add. One String per line. + */ + public void addComment(String path, String... commentLines) { + + StringBuilder commentstring = new StringBuilder(); + StringBuilder leadingSpaces = new StringBuilder(); + for (int n = 0; n < path.length(); n++) { + if (path.charAt(n) == '.') { + leadingSpaces.append(" "); + } + } + for (String line : commentLines) { + if (!line.isEmpty()) { + line = leadingSpaces + line; + } else { + line = " "; + } + if (commentstring.length() > 0) { + commentstring.append(System.getProperty("line.separator")); + } + commentstring.append(line); + } + comments.put(path, commentstring.toString()); + } + + @Override + public String saveToString() { + yamlOptions.setIndent(options().indent()); + yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + yamlOptions.setWidth(10000); + yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + + String dump = yaml.dump(getValues(false)); + + + if (dump.equals(BLANK_CONFIG)) { + dump = ""; + } + + return dump; + } +} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java index 32221ad..c9f0887 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java @@ -83,8 +83,8 @@ public class ConfigData { Object val; if (config == null || !config.isSet(path)) { //Call set() if config == null val = primitiveDef; - if (def == null && config != null) //In Discord's case def may be null - setInternal(primitiveDef); + if ((def == null || this instanceof ReadOnlyConfigData) && config != null) //In Discord's case def may be null + setInternal(primitiveDef); //If read-only then we still need to save the default value so it can be set else set(def); //Save default value - def is always set } else diff --git a/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java b/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java index c06169a..6d82808 100644 --- a/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java +++ b/ButtonProcessor/src/main/java/buttondevteam/buttonproc/ConfigProcessor.java @@ -1,5 +1,8 @@ package buttondevteam.buttonproc; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; @@ -10,29 +13,35 @@ import javax.lang.model.type.TypeMirror; import javax.tools.FileObject; import javax.tools.StandardLocation; import java.io.File; -import java.io.FileWriter; import java.io.IOException; public class ConfigProcessor { private final ProcessingEnvironment procEnv; - private final FileWriter sw; + private final YamlConfiguration yc = new YamlConfiguration(); + private final FileObject fo; public ConfigProcessor(ProcessingEnvironment procEnv) { + FileObject fo1; this.procEnv = procEnv; - FileWriter sw = null; try { - FileObject file = procEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "configHelp.md"); - sw = new FileWriter(new File(file.toUri())); - System.out.println(file.toUri()); + fo1 = procEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", "configHelp.yml"); } catch (IOException e) { e.printStackTrace(); + fo1 = null; } - this.sw = sw; + this.fo = fo1; } public void process(Element targetcl) { if (targetcl.getModifiers().contains(Modifier.ABSTRACT)) return; final String path = "components." + targetcl.getSimpleName(); + File file = new File(fo.toUri()); + try { + if (file.exists()) + yc.load(file); + } catch (IOException | InvalidConfigurationException e) { + e.printStackTrace(); + } for (Element e : targetcl.getEnclosedElements()) { /*System.out.println("Element: "+e); System.out.println("Type: "+e.getClass()+" - "+e.getKind()); @@ -49,30 +58,18 @@ public class ConfigProcessor { String doc = procEnv.getElementUtils().getDocComment(e); if (doc == null) continue; System.out.println("DOC: " + doc); - try { - sw.append(path).append(".").append(String.valueOf(e.getSimpleName())).append(System.lineSeparator()).append(System.lineSeparator()); - sw.append(doc.trim()).append(System.lineSeparator()).append(System.lineSeparator()); - } catch (IOException e1) { - e1.printStackTrace(); - } + yc.set(path + "." + e.getSimpleName(), doc.trim()); } String javadoc = procEnv.getElementUtils().getDocComment(targetcl); + if (javadoc != null) { + System.out.println("JAVADOC"); + System.out.println(javadoc.trim()); + yc.set(path, javadoc.trim()); + } try { - if (javadoc != null) { - System.out.println("JAVADOC"); - System.out.println(javadoc.trim()); - sw.append(path).append(System.lineSeparator()).append(System.lineSeparator()); - sw.append(javadoc).append(System.lineSeparator()).append(System.lineSeparator()); - } - sw.flush(); + yc.save(file); } catch (IOException e) { e.printStackTrace(); } } - - @Override - protected void finalize() throws Throwable { - sw.close(); - super.finalize(); - } }