Skip to content

Instantly share code, notes, and snippets.

@Magnum97
Last active March 9, 2024 16:02
Show Gist options
  • Save Magnum97/65aa169a3073d43b4aa3a3c326eade79 to your computer and use it in GitHub Desktop.
Save Magnum97/65aa169a3073d43b4aa3a3c326eade79 to your computer and use it in GitHub Desktop.
Config manager for Bukkit / Spigot Minecraft plugins. Allows easy & attractive header, comments add & preserve on change and automatically updated the config with resource file if key does not exist.
/*
* Base with comment and header features created by Log-out
* https://bukkit.org/threads/tut-custom-yaml-configurations-with-comments.142592/
* Updated by Magnum1997 to auto update config files from resources
*/
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import org.mineacademy.fo.Common;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
public class SimpleConfig extends YamlConfiguration {
private JavaPlugin plugin;
private int comments;
private SimpleConfigManager manager;
private File file;
private FileConfiguration config;
private boolean useDefaults;
private YamlConfiguration defaults;
public SimpleConfig (InputStreamReader configStream, File configFile, int comments, boolean useDefaults, JavaPlugin plugin) {
this.plugin = plugin;
this.comments = comments;
this.manager = new SimpleConfigManager(plugin);
this.file = configFile;
this.config = YamlConfiguration.loadConfiguration(configStream);
this.useDefaults = useDefaults;
if (useDefaults) {
this.defaults = YamlConfiguration.loadConfiguration(new InputStreamReader(SimpleConfig.class.getResourceAsStream("/" + file.getName()), StandardCharsets.UTF_8));
Objects.requireNonNull(defaults, "Could not find the default " + file.getName() + " in jar file.");
}
else
this.defaults = null;
this.file = extract(file.getName());
}
// Extract the file from your jar to the plugins/YourPlugin folder.
// Does nothing if the file exists
private File extract (String fileName) {
File file = new File(plugin.getDataFolder(), fileName);
if (file.exists())
return file;
createPath(fileName);
if (defaults != null)
try (InputStream inputStream = plugin.getResource(fileName)) {
Objects.requireNonNull(inputStream, "File not found in archive: +" + fileName);
Files.copy(inputStream, Paths.get(file.toURI()), StandardCopyOption.REPLACE_EXISTING);
}
catch (final IOException e) {
e.printStackTrace();
}
return file;
}
private File createPath (String fileName) {
final File datafolder = plugin.getDataFolder();
final int lastIndex = fileName.lastIndexOf("/");
final File directory = new File(datafolder, fileName.substring(0, Math.max(lastIndex, 0)));
directory.mkdirs();
final File destination = new File(datafolder, fileName);
try {
destination.createNewFile();
}
catch (IOException e) {
e.printStackTrace();
System.out.println("File creation failed: " + fileName);
}
return destination;
}
public Object get (String path) {
return get(path, null);
}
/**
* Gets an unspecified value from your file, so you must cast it to your desired value (example: (boolean) get("disable.this.feature", true))
* The "def" is the default value, must be null since we use default values from your file in your .jar.
*/
@Override
public Object get (String path, Object def) {
if (defaults != null) {
if (def != null && ! def.getClass().isPrimitive() && ! PrimitiveWrapper.isWrapperType(def.getClass()))
throw new IllegalArgumentException("The default value must be null since we use defaults from file inside of the plugin! Path: " + path + ", default called: " + def);
if (super.get(path, null) == null) {
final Object defaultValue = defaults.get(path);
Objects.requireNonNull(defaultValue, "Default " + file.getName() + " in your .jar lacks a key at '" + path + "' path");
Common.log("&fUpdating &a" + file.getName() + "&f. Set '&b" + path + "&f' to '" + defaultValue + "'");
set(path, defaultValue);
saveConfig();
reloadConfig();
}
}
/*
// prevent infinite loop due to how get works in the parent class
final String m = new Throwable().getStackTrace()[1].getMethodName();
// Add path prefix, but only when the default file doesn't exist
if (defaults == null && pathPrefix != null && !m.equals("getConfigurationSection") && !m.equals("get"))
path = pathPrefix + "." + path;
*/
// return super.get(path, null);
return this.config.get(path, null);
}
public String getString (String path) {
return this.config.getString(path);
}
public String getString (String path, String def) {
return this.config.getString(path, def);
}
public int getInt (String path) {
return this.config.getInt(path);
}
public int getInt (String path, int def) {
return this.config.getInt(path, def);
}
public boolean getBoolean (String path) {
return this.config.getBoolean(path);
}
public boolean getBoolean (String path, boolean def) {
return this.config.getBoolean(path, def);
}
/*
public void createSection (String path) {
this.config.createSection(path);
}
*/
public ConfigurationSection getConfigurationSection (String path) {
return this.config.getConfigurationSection(path);
}
public double getDouble (String path) {
return this.config.getDouble(path);
}
public double getDouble (String path, double def) {
return this.config.getDouble(path, def);
}
public List <?> getList (String path) {
return this.config.getList(path);
}
public List <?> getList (String path, List <?> def) {
return this.config.getList(path, def);
}
public boolean contains (String path) {
return this.config.contains(path);
}
public void removeKey (String path) {
this.config.set(path, null);
}
public void set (String path, Object value) {
this.config.set(path, value);
}
public void set (String path, Object value, String comment) {
if (! this.config.contains(path)) {
this.config.set(manager.getPluginName() + "_COMMENT_" + comments, " " + comment);
comments++;
}
this.config.set(path, value);
}
public void set (String path, Object value, String[] comment) {
for (String comm : comment) {
if (! this.config.contains(path)) {
this.config.set(manager.getPluginName() + "_COMMENT_" + comments, " " + comm);
comments++;
}
}
this.config.set(path, value);
}
public void setHeader (String[] header) {
manager.setHeader(this.file, header);
this.comments = header.length + 2;
this.reloadConfig();
}
public void reloadConfig () {
this.config = YamlConfiguration.loadConfiguration(manager.getConfigContent(file));
}
public void saveConfig () {
String config = this.config.saveToString();
manager.saveConfig(config, this.file);
}
public Set <String> getKeys () {
return this.config.getKeys(false);
}
// A helper class
private static final class PrimitiveWrapper {
private static final Set <Class <?>> WRAPPER_TYPES = getWrapperTypes();
private static boolean isWrapperType (Class <?> clazz) {
return WRAPPER_TYPES.contains(clazz);
}
private static Set <Class <?>> getWrapperTypes () {
final Set <Class <?>> ret = new HashSet <>();
ret.add(Boolean.class);
ret.add(Character.class);
ret.add(Byte.class);
ret.add(Short.class);
ret.add(Integer.class);
ret.add(Long.class);
ret.add(Float.class);
ret.add(Double.class);
ret.add(Void.class);
return ret;
}
}
}
/*
* Base with comment and header features created by Log-out
* https://bukkit.org/threads/tut-custom-yaml-configurations-with-comments.142592/
* Updated by Magnum1997 to auto update config files from resources
*/
import org.bukkit.plugin.java.JavaPlugin;
import java.io.*;
import java.nio.charset.Charset;
public class SimpleConfigManager {
private JavaPlugin plugin;
/**
* Manage custom configurations and files
*/
public SimpleConfigManager (JavaPlugin plugin) {
this.plugin = plugin;
}
/**
* Get new configuration with header
*
* @param filePath - Path to file
* @return - New SimpleConfig
*/
public SimpleConfig getNewConfig (String filePath, String[] header,boolean useDefaults) {
File file = this.getConfigFile(filePath);
if (! file.exists()) {
this.prepareFile(filePath);
if (header != null && header.length != 0) {
this.setHeader(file, header);
}
}
SimpleConfig config = new SimpleConfig(this.getConfigContent(filePath), file, this.getCommentsNum(file), useDefaults,plugin);
return config;
}
/**
* Get new configuration.
* <p>It will be blank unless (@link useDefaults) is true
* <b>and</b> include it in resources.</p>
*
* @param filePath - Path to file
* @return - New SimpleConfig
*/
public SimpleConfig getNewConfig (String filePath) {
return this.getNewConfig(filePath, null,false);
}
/**
* Get configuration file from string
*
* @param file - File path
* @return - New file object
*/
private File getConfigFile (String file) {
if (file == null || file.isEmpty()) {
return null;
}
File configFile;
if (file.contains("/")) {
if (file.startsWith("/")) {
configFile = new File(plugin.getDataFolder() + file.replace("/", File.separator));
}
else {
configFile = new File(plugin.getDataFolder() + File.separator + file.replace("/", File.separator));
}
}
else {
configFile = new File(plugin.getDataFolder(), file);
}
return configFile;
}
/**
* Create new file for config and copy resource into it
*
* @param filePath - Path to file
* @param resource - Resource to copy
*/
public void prepareFile (String filePath, String resource) {
File file = this.getConfigFile(filePath);
if (file.exists()) {
return;
}
try {
file.getParentFile().mkdirs();
file.createNewFile();
if (resource != null && ! resource.isEmpty()) {
this.copyResource(plugin.getResource(resource), file);
}
}
catch (IOException e) {
e.printStackTrace();
}
}
/**
* Create new file for config without resource
*
* @param filePath - File to create
*/
public void prepareFile (String filePath) {
this.prepareFile(filePath, null);
}
/**
* Adds header block to config
*
* @param file - Config file
* @param header - Header lines
*/
public void setHeader (File file, String[] header) {
if (! file.exists()) {
return;
}
try {
String currentLine;
StringBuilder config = new StringBuilder("");
BufferedReader reader = new BufferedReader(new FileReader(file));
while ((currentLine = reader.readLine()) != null) {
config.append(currentLine + "\n");
}
reader.close();
config.append("# +----------------------------------------------------+ #\n");
for (String line : header) {
if (line.length() > 50) {
continue;
}
int lenght = (50 - line.length()) / 2;
StringBuilder finalLine = new StringBuilder(line);
for (int i = 0; i < lenght; i++) {
finalLine.append(" ");
finalLine.reverse();
finalLine.append(" ");
finalLine.reverse();
}
if (line.length() % 2 != 0) {
finalLine.append(" ");
}
config.append("# < " + finalLine.toString() + " > #\n");
}
config.append("# +----------------------------------------------------+ #");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write(this.prepareConfigString(config.toString()));
writer.flush();
writer.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
/**
* Read file and make comments SnakeYAML friendly
*
* @param file - Path to file
* @return - File as Input Stream
*/
public InputStreamReader getConfigContent (File file) {
if (! file.exists()) {
return null;
}
try {
int commentNum = 0;
String addLine;
String currentLine;
String pluginName = this.getPluginName();
StringBuilder whole = new StringBuilder("");
BufferedReader reader = new BufferedReader(new FileReader(file));
while ((currentLine = reader.readLine()) != null) {
if (currentLine.startsWith("#")) {
addLine = currentLine.replaceFirst("#", pluginName + "_COMMENT_" + commentNum + ":");
whole.append(addLine + "\n");
commentNum++;
}
else {
whole.append(currentLine + "\n");
}
}
String config = whole.toString();
InputStream configStream = new ByteArrayInputStream(config.getBytes(Charset.forName("UTF-8")));
InputStreamReader configStreamReader = new InputStreamReader(configStream);
reader.close();
return configStreamReader;
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Get comments from file
*
* @param file - File
* @return - Comments number
*/
private int getCommentsNum (File file) {
if (! file.exists()) {
return 0;
}
try {
int comments = 0;
String currentLine;
BufferedReader reader = new BufferedReader(new FileReader(file));
while ((currentLine = reader.readLine()) != null) {
if (currentLine.startsWith("#")) {
comments++;
}
}
reader.close();
return comments;
}
catch (IOException e) {
e.printStackTrace();
return 0;
}
}
/**
* Get config content from file
*
* @param filePath - Path to file
* @return - readied file
*/
public InputStreamReader getConfigContent (String filePath) {
return this.getConfigContent(this.getConfigFile(filePath));
}
private String prepareConfigString (String configString) {
int lastLine = 0;
int headerLine = 0;
String[] lines = configString.split("\n");
StringBuilder config = new StringBuilder("");
for (String line : lines) {
if (line.startsWith(this.getPluginName() + "_COMMENT")) {
String comment = "#" + line.trim().substring(line.indexOf(":") + 1);
if (comment.startsWith("# +-")) {
/*
* If header line = 0 then it is
* header start, if it's equal
* to 1 it's the end of header
*/
if (headerLine == 0) {
config.append(comment + "\n");
lastLine = 0;
headerLine = 1;
}
else if (headerLine == 1) {
config.append(comment + "\n\n");
lastLine = 0;
headerLine = 0;
}
}
else {
/*
* Last line = 0 - Comment
* Last line = 1 - Normal path
*/
String normalComment;
if (comment.startsWith("# ' ")) {
normalComment = comment.substring(0, comment.length() - 1).replaceFirst("# ' ", "# ");
}
else {
normalComment = comment;
}
if (lastLine == 0) {
config.append(normalComment + "\n");
}
else if (lastLine == 1) {
config.append("\n" + normalComment + "\n");
}
lastLine = 0;
}
}
else {
config.append(line + "\n");
lastLine = 1;
}
}
return config.toString();
}
/**
* Saves configuration to file
*
* @param configString - Config string
* @param file - Config file
*/
public void saveConfig (String configString, File file) {
String configuration = this.prepareConfigString(configString);
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
writer.write(configuration);
writer.flush();
writer.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public String getPluginName () {
return plugin.getDescription().getName();
}
/**
* Copy resource from Input Stream to file
*
* @param resource - Resource from .jar
* @param file - File to write
*/
private void copyResource (InputStream resource, File file) {
try {
OutputStream out = new FileOutputStream(file);
int lenght;
byte[] buf = new byte[1024];
while ((lenght = resource.read(buf)) > 0) {
out.write(buf, 0, lenght);
}
out.close();
resource.close();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment