All source projects
MrUltimateLibrary Source
Standalone source project / 68 files
MrUltimateLibrary is the backbone of all my plugins; it offers features that facilitate plugin creation, maintenance, and updates.
java
MrUltimateLibrary/src/main/java/eu/mrneznamy/mrultimatelibrary/config/DevMrNeznamyConfigs.java
JAVA · Read-only source
package eu.mrneznamy.mrultimatelibrary.config;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import eu.mrneznamy.mrultimatelibrary.stats.MrNeznamyStats;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* Generalized dynamic template retrieval mechanism.
*/
public class DevMrNeznamyConfigs {
private static final String BASE_API_URL = "";
private static final String MRSELLCHESTS_GITHUB_URL =
"https://raw.githubusercontent.com/MrNeznamy/my-plugin-configs/main/MrSellChests/";
private static final int TIMEOUT = 10000;
/**
* Safely queries API directories and triggers incremental additions.
*/
public static void checkAndDownloadConfigs(JavaPlugin plugin, String slug) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
checkAndDownloadConfigsSync(plugin, slug);
});
}
/**
* Synchronously queries API directories and triggers incremental additions.
*/
public static void checkAndDownloadConfigsSync(JavaPlugin plugin, String slug) {
try {
File engineFile = new File(plugin.getDataFolder(), "engine.yml");
if (!engineFile.exists()) return;
YamlConfiguration engine = YamlConfiguration.loadConfiguration(engineFile);
String mainKey = plugin.getName();
if (!engine.contains(mainKey)) {
for (String key : engine.getKeys(false)) {
if (engine.isConfigurationSection(key)) {
mainKey = key;
break;
}
}
}
boolean allowAutoEdit = true;
if (engine.contains(mainKey + ".Engine.ConfigManager.AllowAutoEditConfigs")) {
allowAutoEdit = engine.getBoolean(mainKey + ".Engine.ConfigManager.AllowAutoEditConfigs");
} else if (engine.contains("Engine.ConfigManager.AllowAutoEditConfigs")) {
allowAutoEdit = engine.getBoolean("Engine.ConfigManager.AllowAutoEditConfigs");
}
List<String> manifest = engine.getStringList(mainKey + ".Engine.ConfigManager.Files");
if (manifest == null || manifest.isEmpty()) {
manifest = engine.getStringList("Engine.ConfigManager.Files");
}
if (manifest == null) {
manifest = new ArrayList<>();
}
boolean manifestChanged = false;
List<RemoteConfigInfo> remoteFiles = getRemoteFiles(slug);
if (remoteFiles.isEmpty()) {
plugin.getLogger().warning("No remote configuration files found for '" + slug + "'.");
return;
}
for (RemoteConfigInfo remote : remoteFiles) {
String filename = remote.getFilename();
String remoteVersion = remote.getVersion();
String remoteLang = remote.getLanguage();
if (filename.equals("data.yml") || (filename.equals("boosters.yml") && !slug.equalsIgnoreCase("MrUltimateFishing") && !slug.equalsIgnoreCase("MrSellChests"))) {
continue;
}
String manifestEntry = null;
int manifestIndex = -1;
for (int i = 0; i < manifest.size(); i++) {
String entry = manifest.get(i);
if (entry.startsWith(filename + ";")) {
manifestEntry = entry;
manifestIndex = i;
break;
}
}
File localFile = new File(plugin.getDataFolder(), filename);
if (manifestEntry != null) {
String[] parts = manifestEntry.split(";");
String localVer = parts[1];
String lang = parts.length > 2 ? parts[2] : "global";
if (lang.equalsIgnoreCase("onetime")) {
continue;
}
if (!localFile.exists()) {
if (allowAutoEdit) {
plugin.getLogger().info("Downloading missing remote asset: " + filename);
if (downloadDirect(plugin, slug, filename)) {
manifest.set(manifestIndex, filename + ";" + remoteVersion + ";" + lang);
manifestChanged = true;
}
}
} else if (allowAutoEdit && isNewerVersion(remoteVersion, localVer)) {
Bukkit.getLogger().info("[" + plugin.getName() + "] Updating remote asset: " + filename + " (" + localVer + " -> " + remoteVersion + ")");
if (downloadAndMerge(plugin, slug, filename)) {
manifest.set(manifestIndex, filename + ";" + remoteVersion + ";" + lang);
manifestChanged = true;
}
}
} else {
if (allowAutoEdit) {
if (localFile.exists()) {
manifest.add(filename + ";" + remoteVersion + ";" + remoteLang);
manifestChanged = true;
} else {
plugin.getLogger().info("Downloading missing remote asset: " + filename);
if (downloadDirect(plugin, slug, filename)) {
manifest.add(filename + ";" + remoteVersion + ";" + remoteLang);
manifestChanged = true;
}
}
}
}
}
if (manifestChanged) {
if (engine.contains(mainKey + ".Engine.ConfigManager.Files")) {
engine.set(mainKey + ".Engine.ConfigManager.Files", manifest);
} else {
engine.set("Engine.ConfigManager.Files", manifest);
}
engine.save(engineFile);
}
} catch (Exception e) {
}
}
public static List<RemoteConfigInfo> getRemoteFiles(String slug) {
List<RemoteConfigInfo> list = new ArrayList<>();
if (slug.equalsIgnoreCase("mrsellchests")) {
return getMrSellChestsGitHubFiles();
}
return list;
/*
try {
URL url = new URL(BASE_API_URL + slug.toLowerCase() + "/files");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("X-Api-Key", MrNeznamyStats.getApiKey() != null ? MrNeznamyStats.getApiKey() : "");
con.setConnectTimeout(TIMEOUT);
con.setReadTimeout(TIMEOUT);
if (con.getResponseCode() == 200) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder content = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
content.append(line);
}
JsonObject json = JsonParser.parseString(content.toString()).getAsJsonObject();
if (json.has("files")) {
JsonArray filesArray = json.getAsJsonArray("files");
for (int i = 0; i < filesArray.size(); i++) {
JsonObject obj = filesArray.get(i).getAsJsonObject();
String filename = obj.get("filename").getAsString();
String version = obj.get("version").getAsString();
String language = "global";
if (obj.has("lang") && !obj.get("lang").isJsonNull()) {
String langVal = obj.get("lang").getAsString();
if (langVal != null && !langVal.equalsIgnoreCase("null") && !langVal.isEmpty()) {
language = langVal;
}
}
list.add(new RemoteConfigInfo(filename, version, language));
}
}
}
}
} catch (Exception e) {
Bukkit.getLogger().warning("[" + slug + "] Failed to read remote configuration manifest: " + e.getMessage());
}
return list;
*/
}
private static List<RemoteConfigInfo> getMrSellChestsGitHubFiles() {
List<RemoteConfigInfo> list = new ArrayList<>();
try {
URL url = new URL(MRSELLCHESTS_GITHUB_URL + "versions.json");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(TIMEOUT);
con.setReadTimeout(TIMEOUT);
if (con.getResponseCode() == 200) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), StandardCharsets.UTF_8))) {
JsonObject versions = JsonParser.parseReader(in).getAsJsonObject();
if (versions.has("configs") && versions.get("configs").isJsonObject()) {
versions = versions.getAsJsonObject("configs");
}
for (String filename : versions.keySet()) {
JsonElement version = versions.get(filename);
if (version.isJsonObject() && version.getAsJsonObject().has("version")) {
version = version.getAsJsonObject().get("version");
}
list.add(new RemoteConfigInfo(filename, version.getAsString(), "global"));
}
}
}
} catch (Exception e) {
Bukkit.getLogger().warning("[MrSellChests] Failed to read GitHub versions.json: " + e.getMessage());
}
return list;
}
public static String getLocalConfigVersion(File file) {
if (!file.exists()) return "0.0.0";
try {
FileConfiguration cfg = YamlConfiguration.loadConfiguration(file);
if (cfg.contains("MrUltimateLibrary-Configs-Version")) {
return cfg.getString("MrUltimateLibrary-Configs-Version", "0.0.0");
}
} catch (Exception ignored) {}
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
int lineCount = 0;
while ((line = br.readLine()) != null && lineCount < 10) {
lineCount++;
if (line.contains("MrUltimateLibrary-Configs-Version:")) {
int idx = line.indexOf(":");
String ver = line.substring(idx + 1).replace("\"", "").replace("'", "").trim();
if (!ver.isEmpty()) {
return ver;
}
}
}
} catch (Exception ignored) {}
return "0.0.0";
}
private static boolean isNewerVersion(String remote, String local) {
try {
String[] remoteParts = remote.split("\\.");
String[] localParts = local.split("\\.");
int length = Math.max(remoteParts.length, localParts.length);
for (int i = 0; i < length; i++) {
int r = i < remoteParts.length ? Integer.parseInt(remoteParts[i]) : 0;
int l = i < localParts.length ? Integer.parseInt(localParts[i]) : 0;
if (r > l) return true;
if (r < l) return false;
}
} catch (Exception ignored) {}
return false;
}
private static boolean downloadDirect(JavaPlugin plugin, String slug, String filename) {
try {
String content = fetchTemplate(slug, filename);
if (content == null || content.trim().isEmpty()) return false;
File localFile = new File(plugin.getDataFolder(), filename);
if (localFile.getParentFile() != null) {
localFile.getParentFile().mkdirs();
}
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(localFile), StandardCharsets.UTF_8))) {
writer.write(content);
}
return true;
} catch (Exception e) {
plugin.getLogger().warning("Failed to download remote asset '" + filename + "': " + e.getMessage());
return false;
}
}
private static final java.util.regex.Pattern KEY_PATTERN = java.util.regex.Pattern.compile("^(\\s*)([\"']?)([a-zA-Z0-9_.-]+)\\2\\s*:(.*)$");
private static boolean downloadAndMerge(JavaPlugin plugin, String slug, String filename) {
try {
String remoteContent = fetchTemplate(slug, filename);
if (remoteContent == null || remoteContent.trim().isEmpty()) return false;
File localFile = new File(plugin.getDataFolder(), filename);
YamlConfiguration localConfig = YamlConfiguration.loadConfiguration(localFile);
YamlConfiguration remoteConfig = new YamlConfiguration();
remoteConfig.loadFromString(remoteContent);
List<String> missingKeys = new ArrayList<>();
for (String key : remoteConfig.getKeys(true)) {
if (!remoteConfig.isConfigurationSection(key) && !localConfig.contains(key)) {
missingKeys.add(key);
}
}
if (missingKeys.isEmpty()) {
if (remoteConfig.contains("MrUltimateLibrary-Configs-Version")) {
String localVer = getLocalConfigVersion(localFile);
String remoteVer = remoteConfig.getString("MrUltimateLibrary-Configs-Version");
if (isNewerVersion(remoteVer, localVer)) {
MrLibYamlUpdater.setKeyValue(localFile, "MrUltimateLibrary-Configs-Version", remoteVer);
}
}
removeLegacyKeys(localFile, filename);
return true;
}
List<String> remoteLines = java.util.Arrays.asList(remoteContent.split("\\r?\\n"));
for (String key : missingKeys) {
int keyLineIdx = -1;
String[] parts = key.split("\\.");
String leafKey = parts[parts.length - 1];
for (int i = 0; i < remoteLines.size(); i++) {
String line = remoteLines.get(i);
String trimmed = line.trim();
if (trimmed.startsWith(leafKey + ":") || trimmed.startsWith("\"" + leafKey + "\":") || trimmed.startsWith("'" + leafKey + "':")) {
if (matchPath(remoteLines, i, key)) {
keyLineIdx = i;
break;
}
}
}
if (keyLineIdx != -1) {
List<String> block = new ArrayList<>();
int startIdx = keyLineIdx;
while (startIdx > 0) {
String prev = remoteLines.get(startIdx - 1).trim();
if (prev.startsWith("#") || prev.isEmpty()) {
startIdx--;
} else {
break;
}
}
int endIdx = keyLineIdx;
int keyIndent = getIndent(remoteLines.get(keyLineIdx));
while (endIdx + 1 < remoteLines.size()) {
String nextLine = remoteLines.get(endIdx + 1);
String trimmedNext = nextLine.trim();
if (trimmedNext.isEmpty() || trimmedNext.startsWith("#")) {
endIdx++;
} else {
int nextIndent = getIndent(nextLine);
if (nextIndent > keyIndent) {
endIdx++;
} else {
break;
}
}
}
for (int i = startIdx; i <= endIdx; i++) {
block.add(remoteLines.get(i));
}
insertBlock(localFile, key, block, plugin.getName());
}
}
if (remoteConfig.contains("MrUltimateLibrary-Configs-Version")) {
String remoteVer = remoteConfig.getString("MrUltimateLibrary-Configs-Version");
MrLibYamlUpdater.setKeyValue(localFile, "MrUltimateLibrary-Configs-Version", remoteVer);
}
removeLegacyKeys(localFile, filename);
return true;
} catch (Exception e) {
return false;
}
}
private static boolean matchPath(List<String> lines, int lineIdx, String targetPath) {
List<String> pathParts = new ArrayList<>();
int currentIndent = getIndent(lines.get(lineIdx));
pathParts.add(getKeyName(lines.get(lineIdx)));
for (int i = lineIdx - 1; i >= 0; i--) {
String line = lines.get(i);
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) continue;
int indent = getIndent(line);
if (indent < currentIndent) {
pathParts.add(0, getKeyName(line));
currentIndent = indent;
}
}
String path = String.join(".", pathParts);
return path.equals(targetPath);
}
private static int getIndent(String line) {
int count = 0;
while (count < line.length() && line.charAt(count) == ' ') {
count++;
}
return count;
}
private static String getKeyName(String line) {
String trimmed = line.trim();
int colon = trimmed.indexOf(':');
if (colon == -1) return trimmed;
String key = trimmed.substring(0, colon).trim();
if ((key.startsWith("\"") && key.endsWith("\"")) || (key.startsWith("'") && key.endsWith("'"))) {
key = key.substring(1, key.length() - 1);
}
return key;
}
private static void insertBlock(File file, String key, List<String> block, String pluginName) throws IOException {
List<String> lines = java.nio.file.Files.readAllLines(file.toPath(), java.nio.charset.StandardCharsets.UTF_8);
List<String> newLines = new ArrayList<>();
int lastDot = key.lastIndexOf('.');
if (lastDot == -1) {
newLines.addAll(lines);
if (!newLines.isEmpty() && !newLines.get(newLines.size() - 1).trim().isEmpty()) {
newLines.add("");
}
newLines.addAll(block);
} else {
String parentPath = key.substring(0, lastDot);
int parentLineIdx = -1;
List<PathSegment> pathStack = new ArrayList<>();
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) continue;
java.util.regex.Matcher matcher = KEY_PATTERN.matcher(line);
if (matcher.find()) {
int indent = matcher.group(1).length();
String keyName = matcher.group(3);
pathStack.removeIf(segment -> segment.indent >= indent);
pathStack.add(new PathSegment(keyName, indent));
String currentPath = getFullPath(pathStack);
if (currentPath.equals(parentPath)) {
parentLineIdx = i;
}
}
}
if (parentLineIdx != -1) {
int parentIndent = getIndent(lines.get(parentLineIdx));
int insertIdx = parentLineIdx + 1;
for (int i = parentLineIdx + 1; i < lines.size(); i++) {
String line = lines.get(i);
String trimmed = line.trim();
if (trimmed.isEmpty() || trimmed.startsWith("#")) continue;
int indent = getIndent(line);
if (indent <= parentIndent) {
break;
}
insertIdx = i + 1;
}
for (int i = 0; i < lines.size(); i++) {
if (i == insertIdx) {
newLines.addAll(block);
}
newLines.add(lines.get(i));
}
if (insertIdx == lines.size()) {
newLines.addAll(block);
}
} else {
newLines.addAll(lines);
if (!newLines.isEmpty() && !newLines.get(newLines.size() - 1).trim().isEmpty()) {
newLines.add("");
}
newLines.addAll(block);
}
}
java.nio.file.Files.write(file.toPath(), newLines, java.nio.charset.StandardCharsets.UTF_8);
}
private static String getFullPath(List<PathSegment> pathStack) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pathStack.size(); i++) {
sb.append(pathStack.get(i).key);
if (i < pathStack.size() - 1) {
sb.append(".");
}
}
return sb.toString();
}
private static void removeLegacyKeys(File file, String filename) {
try {
List<String> lines = java.nio.file.Files.readAllLines(file.toPath(), java.nio.charset.StandardCharsets.UTF_8);
List<String> newLines = new ArrayList<>();
boolean changed = false;
String oldKey = filename.replace(".yml", "").replace("/", "_").replace("\\", "_") + "-version";
for (String line : lines) {
String trimmed = line.trim();
if (trimmed.startsWith("config-version:") || trimmed.startsWith("verze:") || trimmed.startsWith(oldKey + ":")) {
changed = true;
continue;
}
newLines.add(line);
}
if (changed) {
java.nio.file.Files.write(file.toPath(), newLines, java.nio.charset.StandardCharsets.UTF_8);
}
} catch (Exception ignored) {}
}
private static String fetchTemplate(String slug, String filename) {
if (!slug.equalsIgnoreCase("mrsellchests")) return null;
/*
try {
String webPath = filename.replace("\\", "/");
URL url;
if (slug.equalsIgnoreCase("mrsellchests")) {
url = new URL(MRSELLCHESTS_GITHUB_URL + webPath);
} else {
url = new URL(BASE_API_URL + slug.toLowerCase() + "/config?filename="
+ URLEncoder.encode(webPath, "UTF-8"));
}
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
if (!slug.equalsIgnoreCase("mrsellchests")) {
con.setRequestProperty("X-Api-Key", MrNeznamyStats.getApiKey() != null
? MrNeznamyStats.getApiKey() : "");
}
con.setConnectTimeout(TIMEOUT);
con.setReadTimeout(TIMEOUT);
if (con.getResponseCode() == 200) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder content = new StringBuilder();
String line;
while ((line = in.readLine()) != null) {
content.append(line).append("\n");
}
return content.toString();
}
}
} catch (Exception ignored) {}
return null;
*/
return null;
}
public static class RemoteConfigInfo {
private final String filename;
private final String version;
private final String language;
public RemoteConfigInfo(String filename, String version, String language) {
this.filename = filename;
this.version = version;
this.language = language;
}
public String getFilename() { return filename; }
public String getVersion() { return version; }
public String getLanguage() { return language; }
}
private static class PathSegment {
String key;
int indent;
PathSegment(String key, int indent) {
this.key = key;
this.indent = indent;
}
}
}
616 linesGNU GENERAL PUBLIC LICENSE · Version 3, 29 June 2007 · UTF-8 · Read only