mirror of
https://github.com/StarWishsama/Slimefun4.git
synced 2024-09-19 19:25:48 +00:00
Merge pull request #2107 from WalshyDev/feature/metrics-module
Metrics!
This commit is contained in:
commit
d69ef1effb
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#### Additions
|
#### Additions
|
||||||
* Added "Bone Block -> Bone meal" recipe to the Grind Stone
|
* Added "Bone Block -> Bone meal" recipe to the Grind Stone
|
||||||
|
* Added a [Metrics module](https://github.com/Slimefun/MetricsModule) which allows us to deploy changes to metrics (bStats) without another Slimefun build.
|
||||||
|
|
||||||
#### Changes
|
#### Changes
|
||||||
* Refactored and reworked the Generator API
|
* Refactored and reworked the Generator API
|
||||||
|
11
pom.xml
11
pom.xml
@ -327,6 +327,17 @@
|
|||||||
<version>1.7</version>
|
<version>1.7</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.konghq</groupId>
|
||||||
|
<artifactId>unirest-java</artifactId>
|
||||||
|
<version>3.8.06</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.papermc</groupId>
|
<groupId>io.papermc</groupId>
|
||||||
<artifactId>paperlib</artifactId>
|
<artifactId>paperlib</artifactId>
|
||||||
|
@ -2,17 +2,16 @@ package io.github.thebusybiscuit.slimefun4.core.commands.subcommands;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.ChatColor;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.command.ConsoleCommandSender;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
|
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
|
||||||
import io.github.thebusybiscuit.cscorelib2.reflection.ReflectionUtils;
|
import io.github.thebusybiscuit.cscorelib2.reflection.ReflectionUtils;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand;
|
import io.github.thebusybiscuit.slimefun4.core.commands.SlimefunCommand;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand;
|
import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand;
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.command.ConsoleCommandSender;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
|
|
||||||
class VersionsCommand extends SubCommand {
|
class VersionsCommand extends SubCommand {
|
||||||
@ -42,8 +41,11 @@ class VersionsCommand extends SubCommand {
|
|||||||
sender.sendMessage(ChatColors.color("&aCS-CoreLib &2v" + SlimefunPlugin.getCSCoreLibVersion()));
|
sender.sendMessage(ChatColors.color("&aCS-CoreLib &2v" + SlimefunPlugin.getCSCoreLibVersion()));
|
||||||
sender.sendMessage(ChatColors.color("&aSlimefun &2v" + SlimefunPlugin.getVersion()));
|
sender.sendMessage(ChatColors.color("&aSlimefun &2v" + SlimefunPlugin.getVersion()));
|
||||||
|
|
||||||
|
if (SlimefunPlugin.getMetricsService().getVersion() != null)
|
||||||
|
sender.sendMessage(ChatColors.color("&aMetrics: &2#" + SlimefunPlugin.getMetricsService().getVersion() + ')'));
|
||||||
|
|
||||||
if (SlimefunPlugin.getRegistry().isBackwardsCompatible()) {
|
if (SlimefunPlugin.getRegistry().isBackwardsCompatible()) {
|
||||||
sender.sendMessage(ChatColor.YELLOW + "Backwards compatiblity enabled!");
|
sender.sendMessage(ChatColor.YELLOW + "Backwards compatibility enabled!");
|
||||||
}
|
}
|
||||||
|
|
||||||
sender.sendMessage("");
|
sender.sendMessage("");
|
||||||
|
@ -0,0 +1,239 @@
|
|||||||
|
package io.github.thebusybiscuit.slimefun4.core.services;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
||||||
|
import io.github.thebusybiscuit.slimefun4.utils.PatternUtils;
|
||||||
|
import kong.unirest.HttpResponse;
|
||||||
|
import kong.unirest.JsonNode;
|
||||||
|
import kong.unirest.Unirest;
|
||||||
|
import kong.unirest.UnirestException;
|
||||||
|
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This Class represents a Metrics Service that sends data to https://bstats.org/
|
||||||
|
* This data is used to analyse the usage of this {@link Plugin}.
|
||||||
|
* <p>
|
||||||
|
* You can find more info in the README file of this Project on GitHub. <br>
|
||||||
|
* <b>Note:</b> To start the metrics you will need to be calling {@link #start()}
|
||||||
|
*
|
||||||
|
* @author TheBusyBiscuit
|
||||||
|
* @author WalshyDev
|
||||||
|
*/
|
||||||
|
public class MetricsService {
|
||||||
|
|
||||||
|
private static final String REPO_NAME = "MetricsModule";
|
||||||
|
private static final String GH_API = "https://api.github.com/repos/Slimefun/" + REPO_NAME;
|
||||||
|
private static final String GH_REPO_RELEASES = "https://github.com/Slimefun/" + REPO_NAME
|
||||||
|
+ "/releases/download";
|
||||||
|
|
||||||
|
private final SlimefunPlugin plugin;
|
||||||
|
private final File parentFolder;
|
||||||
|
private final File metricFile;
|
||||||
|
|
||||||
|
private URLClassLoader moduleClassLoader;
|
||||||
|
private String metricVersion = null;
|
||||||
|
private boolean newlyDownloaded = false;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Unirest.config()
|
||||||
|
.concurrency(2, 1)
|
||||||
|
.setDefaultHeader("User-Agent", "MetricsModule Auto-Updater")
|
||||||
|
.setDefaultHeader("Accept", "application/vnd.github.v3+json")
|
||||||
|
.enableCookieManagement(false)
|
||||||
|
.cookieSpec("ignoreCookies");
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetricsService(SlimefunPlugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
this.parentFolder = new File(plugin.getDataFolder(), "cache" + File.separatorChar + "modules");
|
||||||
|
if (!parentFolder.exists())
|
||||||
|
parentFolder.mkdirs();
|
||||||
|
|
||||||
|
this.metricFile = new File(parentFolder, REPO_NAME + ".jar");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method loads the metric module and starts the metrics collection.
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
if (!metricFile.exists()) {
|
||||||
|
plugin.getLogger().info(REPO_NAME + " does not exist, downloading...");
|
||||||
|
if (!download(getLatestVersion())) {
|
||||||
|
plugin.getLogger().warning("Failed to start metrics as the file could not be downloaded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Load the jar file into a child class loader using the SF PluginClassLoader
|
||||||
|
// as a parent.
|
||||||
|
moduleClassLoader = URLClassLoader.newInstance(new URL[] { metricFile.toURI().toURL() },
|
||||||
|
plugin.getClass().getClassLoader());
|
||||||
|
Class<?> cl = moduleClassLoader.loadClass("dev.walshy.sfmetrics.MetricsModule");
|
||||||
|
|
||||||
|
metricVersion = cl.getPackage().getImplementationVersion();
|
||||||
|
|
||||||
|
// If it has not been newly downloaded, auto-updates are on AND there's a new version
|
||||||
|
// then cleanup, download and start
|
||||||
|
if (!newlyDownloaded
|
||||||
|
&& hasAutoUpdates()
|
||||||
|
&& checkForUpdate(metricVersion)
|
||||||
|
) {
|
||||||
|
plugin.getLogger().info("Cleaning up and re-loading Metrics.");
|
||||||
|
cleanUp();
|
||||||
|
start();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we're good to start this.
|
||||||
|
Method start = cl.getDeclaredMethod("start");
|
||||||
|
String version = cl.getPackage().getImplementationVersion();
|
||||||
|
|
||||||
|
// This is required to be sync due to bStats.
|
||||||
|
Slimefun.runSync(() -> {
|
||||||
|
try {
|
||||||
|
start.invoke(null);
|
||||||
|
plugin.getLogger().info("Metrics build #" + version + " started.");
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING, "Failed to start metrics.", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING,
|
||||||
|
"Failed to load the metrics module. Maybe the jar is corrupt?", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will close the child classloader and mark all the resources held under this no longer
|
||||||
|
* in use, they will be cleaned up the next GC run.
|
||||||
|
*/
|
||||||
|
public void cleanUp() {
|
||||||
|
try {
|
||||||
|
if (this.moduleClassLoader != null)
|
||||||
|
this.moduleClassLoader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING,
|
||||||
|
"Could not clean up module class loader. Some memory may have been leaked.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for a new update and compares it against the current version.
|
||||||
|
* If there is a new version available then this returns true.
|
||||||
|
*
|
||||||
|
* @param currentVersion The current version which is being used.
|
||||||
|
* @return True if there is an update available.
|
||||||
|
*/
|
||||||
|
public boolean checkForUpdate(String currentVersion) {
|
||||||
|
if (currentVersion == null || !PatternUtils.NUMERIC.matcher(currentVersion).matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int latest = getLatestVersion();
|
||||||
|
if (latest > Integer.parseInt(currentVersion)) {
|
||||||
|
return download(latest);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the latest version available as an int.
|
||||||
|
* This is an internal method used by {@link #checkForUpdate(String)}.
|
||||||
|
* If it cannot get the version for whatever reason this will return 0, effectively always
|
||||||
|
* being behind.
|
||||||
|
*
|
||||||
|
* @return The latest version as an integer or -1 if it failed to fetch.
|
||||||
|
*/
|
||||||
|
private int getLatestVersion() {
|
||||||
|
try {
|
||||||
|
HttpResponse<JsonNode> response = Unirest.get(GH_API + "/releases/latest")
|
||||||
|
.asJson();
|
||||||
|
if (!response.isSuccess()) return -1;
|
||||||
|
|
||||||
|
JsonNode node = response.getBody();
|
||||||
|
|
||||||
|
if (node == null) return -1;
|
||||||
|
|
||||||
|
return node.getObject().getInt("tag_name");
|
||||||
|
} catch (UnirestException e) {
|
||||||
|
plugin.getLogger().log(Level.SEVERE, "Failed to fetch latest builds for SFMetrics");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads the version specified to Slimefun's data folder.
|
||||||
|
*
|
||||||
|
* @param version The version to download.
|
||||||
|
*/
|
||||||
|
private boolean download(int version) {
|
||||||
|
File f = new File(parentFolder, "Metrics-" + version + ".jar");
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin.getLogger().info("# Starting download of MetricsModule build: #" + version);
|
||||||
|
AtomicInteger lastPercentPosted = new AtomicInteger();
|
||||||
|
HttpResponse<File> response = Unirest.get(GH_REPO_RELEASES + "/" + version
|
||||||
|
+ "/" + REPO_NAME + ".jar")
|
||||||
|
.downloadMonitor((b, fileName, bytesWritten, totalBytes) -> {
|
||||||
|
int percent = (int) (20 * (Math.round((((double) bytesWritten / totalBytes) * 100) / 20)));
|
||||||
|
|
||||||
|
if (percent != 0 && percent != lastPercentPosted.get()) {
|
||||||
|
plugin.getLogger().info("# Downloading... " + percent + "% " +
|
||||||
|
"(" + bytesWritten + "/" + totalBytes + " bytes)");
|
||||||
|
lastPercentPosted.set(percent);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.asFile(f.getPath());
|
||||||
|
if (response.isSuccess()) {
|
||||||
|
plugin.getLogger().info("Successfully downloaded " + REPO_NAME + " build: " + version);
|
||||||
|
|
||||||
|
// Replace the metric file with the new one
|
||||||
|
Files.move(f.toPath(), metricFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
metricVersion = String.valueOf(version);
|
||||||
|
newlyDownloaded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (UnirestException e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING, "Failed to fetch the latest jar file from the" +
|
||||||
|
" builds page. Perhaps GitHub is down.");
|
||||||
|
} catch (IOException e) {
|
||||||
|
plugin.getLogger().log(Level.WARNING, "Failed to replace the old metric file with the " +
|
||||||
|
"new one. Please do this manually! Error: {0}", e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the currently downloaded metric version. This CAN change! It may be null or an
|
||||||
|
* older version before it has downloaded a newer one.
|
||||||
|
*
|
||||||
|
* @return The current version or null if not loaded.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getVersion() {
|
||||||
|
return metricVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the current server has metric auto-updates enabled.
|
||||||
|
*
|
||||||
|
* @return True if the current server has metric auto-updates enabled.
|
||||||
|
*/
|
||||||
|
public boolean hasAutoUpdates() {
|
||||||
|
return SlimefunPlugin.instance().getConfig().getBoolean("metrics.auto-update");
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,9 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import kong.unirest.JsonNode;
|
||||||
|
import kong.unirest.json.JSONArray;
|
||||||
|
import kong.unirest.json.JSONObject;
|
||||||
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
||||||
|
|
||||||
class ContributionsConnector extends GitHubConnector {
|
class ContributionsConnector extends GitHubConnector {
|
||||||
@ -57,10 +60,10 @@ class ContributionsConnector extends GitHubConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(JsonElement element) {
|
public void onSuccess(JsonNode element) {
|
||||||
finished = true;
|
finished = true;
|
||||||
if (element.isJsonArray()) {
|
if (element.isArray()) {
|
||||||
computeContributors(element.getAsJsonArray());
|
computeContributors(element.getArray());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Slimefun.getLogger().log(Level.WARNING, "Received an unusual answer from GitHub, possibly a timeout? ({0})", element);
|
Slimefun.getLogger().log(Level.WARNING, "Received an unusual answer from GitHub, possibly a timeout? ({0})", element);
|
||||||
@ -82,13 +85,13 @@ class ContributionsConnector extends GitHubConnector {
|
|||||||
return "/contributors?per_page=100&page=" + page;
|
return "/contributors?per_page=100&page=" + page;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void computeContributors(JsonArray array) {
|
private void computeContributors(JSONArray array) {
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.length(); i++) {
|
||||||
JsonObject object = array.get(i).getAsJsonObject();
|
JSONObject object = array.getJSONObject(i);
|
||||||
|
|
||||||
String name = object.get("login").getAsString();
|
String name = object.getString("login");
|
||||||
int commits = object.get("contributions").getAsInt();
|
int commits = object.getInt("contributions");
|
||||||
String profile = object.get("html_url").getAsString();
|
String profile = object.getString("html_url");
|
||||||
|
|
||||||
if (!blacklist.contains(name)) {
|
if (!blacklist.contains(name)) {
|
||||||
github.addContributor(aliases.getOrDefault(name, name), profile, role, commits);
|
github.addContributor(aliases.getOrDefault(name, name), profile, role, commits);
|
||||||
|
@ -4,19 +4,18 @@ import java.io.BufferedReader;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.channels.Channels;
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import kong.unirest.HttpResponse;
|
||||||
import com.google.gson.JsonParser;
|
import kong.unirest.JsonNode;
|
||||||
|
import kong.unirest.Unirest;
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
import kong.unirest.UnirestException;
|
||||||
|
import kong.unirest.json.JSONException;
|
||||||
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
||||||
|
|
||||||
abstract class GitHubConnector {
|
abstract class GitHubConnector {
|
||||||
@ -34,7 +33,7 @@ abstract class GitHubConnector {
|
|||||||
|
|
||||||
public abstract String getURLSuffix();
|
public abstract String getURLSuffix();
|
||||||
|
|
||||||
public abstract void onSuccess(JsonElement element);
|
public abstract void onSuccess(JsonNode element);
|
||||||
|
|
||||||
public void onFailure() {
|
public void onFailure() {
|
||||||
// Don't do anything by default
|
// Don't do anything by default
|
||||||
@ -48,58 +47,51 @@ abstract class GitHubConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URL website = new URL("https://api.github.com/repos/" + repository + getURLSuffix());
|
HttpResponse<JsonNode> resp = Unirest
|
||||||
|
.get("https://api.github.com/repos/" + repository + getURLSuffix())
|
||||||
|
.header("User-Agent", "Slimefun4 (https://github.com/Slimefun)")
|
||||||
|
.asJson();
|
||||||
|
|
||||||
URLConnection connection = website.openConnection();
|
if (resp.isSuccess()) {
|
||||||
connection.setConnectTimeout(8000);
|
onSuccess(resp.getBody());
|
||||||
connection.addRequestProperty("Accept-Charset", "UTF-8");
|
writeCacheFile(resp.getBody());
|
||||||
connection.addRequestProperty("User-Agent", "Slimefun 4 GitHub Agent (by TheBusyBiscuit)");
|
} else
|
||||||
connection.setDoOutput(true);
|
Slimefun.getLogger().log(Level.WARNING, "Failed to fetch {0}",
|
||||||
|
repository + getURLSuffix());
|
||||||
try (ReadableByteChannel channel = Channels.newChannel(connection.getInputStream())) {
|
} catch (UnirestException e) {
|
||||||
try (FileOutputStream stream = new FileOutputStream(file)) {
|
|
||||||
stream.getChannel().transferFrom(channel, 0, Long.MAX_VALUE);
|
|
||||||
parseData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
if (github.isLoggingEnabled()) {
|
if (github.isLoggingEnabled()) {
|
||||||
Slimefun.getLogger().log(Level.WARNING, "Could not connect to GitHub in time.");
|
Slimefun.getLogger().log(Level.WARNING, "Could not connect to GitHub in time.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasData()) {
|
// It has the cached file, let's just read that then
|
||||||
parseData();
|
if (file.exists()) {
|
||||||
}
|
JsonNode cache = readCacheFile();
|
||||||
else {
|
if (cache != null) {
|
||||||
onFailure();
|
onSuccess(cache);
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasData() {
|
|
||||||
return getFile().exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void parseData() {
|
|
||||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(getFile()), StandardCharsets.UTF_8))) {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
builder.append(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonElement element = new JsonParser().parse(builder.toString());
|
// If the request failed and it failed to read the cache then call onFailure.
|
||||||
onSuccess(element);
|
|
||||||
}
|
|
||||||
catch (IOException x) {
|
|
||||||
Slimefun.getLogger().log(Level.SEVERE, x, () -> "An Error occurred while parsing GitHub-Data for Slimefun " + SlimefunPlugin.getVersion());
|
|
||||||
onFailure();
|
onFailure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JsonNode readCacheFile() {
|
||||||
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||||
|
return new JsonNode(br.readLine());
|
||||||
|
} catch (IOException | JSONException e) {
|
||||||
|
Slimefun.getLogger().log(Level.WARNING, "Failed to read Github cache file: {0}",
|
||||||
|
file.getName());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCacheFile(JsonNode node) {
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
fos.write(node.toString().getBytes(StandardCharsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Slimefun.getLogger().log(Level.WARNING, "Failed to populate GitHub cache");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import kong.unirest.JsonNode;
|
||||||
|
import kong.unirest.json.JSONArray;
|
||||||
|
import kong.unirest.json.JSONElement;
|
||||||
|
import kong.unirest.json.JSONObject;
|
||||||
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
import me.mrCookieSlime.Slimefun.api.Slimefun;
|
||||||
|
|
||||||
class GitHubIssuesTracker extends GitHubConnector {
|
class GitHubIssuesTracker extends GitHubConnector {
|
||||||
@ -25,15 +29,15 @@ class GitHubIssuesTracker extends GitHubConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(JsonElement element) {
|
public void onSuccess(JsonNode element) {
|
||||||
if (element.isJsonArray()) {
|
if (element.isArray()) {
|
||||||
JsonArray array = element.getAsJsonArray();
|
JSONArray array = element.getArray();
|
||||||
|
|
||||||
int issues = 0;
|
int issues = 0;
|
||||||
int pullRequests = 0;
|
int pullRequests = 0;
|
||||||
|
|
||||||
for (JsonElement elem : array) {
|
for (int i = 0; i < array.length(); i++) {
|
||||||
JsonObject obj = elem.getAsJsonObject();
|
JSONObject obj = array.getJSONObject(i);
|
||||||
|
|
||||||
if (obj.has("pull_request")) {
|
if (obj.has("pull_request")) {
|
||||||
pullRequests++;
|
pullRequests++;
|
||||||
|
@ -17,6 +17,8 @@ import io.github.thebusybiscuit.slimefun4.core.services.localization.Translators
|
|||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
||||||
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
|
import io.github.thebusybiscuit.slimefun4.utils.HeadTexture;
|
||||||
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
|
import io.github.thebusybiscuit.slimefun4.utils.NumberUtils;
|
||||||
|
import kong.unirest.JsonNode;
|
||||||
|
import kong.unirest.json.JSONObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Service is responsible for grabbing every {@link Contributor} to this project
|
* This Service is responsible for grabbing every {@link Contributor} to this project
|
||||||
@ -108,11 +110,11 @@ public class GitHubService {
|
|||||||
connectors.add(new GitHubConnector(this, repository) {
|
connectors.add(new GitHubConnector(this, repository) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(JsonElement element) {
|
public void onSuccess(JsonNode element) {
|
||||||
JsonObject object = element.getAsJsonObject();
|
JSONObject object = element.getObject();
|
||||||
forks = object.get("forks").getAsInt();
|
forks = object.getInt("forks");
|
||||||
stars = object.get("stargazers_count").getAsInt();
|
stars = object.getInt("stargazers_count");
|
||||||
lastUpdate = NumberUtils.parseGitHubDate(object.get("pushed_at").getAsString());
|
lastUpdate = NumberUtils.parseGitHubDate(object.getString("pushed_at"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.AdvancedPie;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class AddonsChart extends AdvancedPie {
|
|
||||||
|
|
||||||
AddonsChart() {
|
|
||||||
super("installed_addons", () -> {
|
|
||||||
Map<String, Integer> addons = new HashMap<>();
|
|
||||||
|
|
||||||
for (Plugin plugin : SlimefunPlugin.getInstalledAddons()) {
|
|
||||||
if (plugin.isEnabled()) {
|
|
||||||
addons.put(plugin.getName(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addons;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class AutoUpdaterChart extends SimplePie {
|
|
||||||
|
|
||||||
AutoUpdaterChart() {
|
|
||||||
super("auto_updates", () -> {
|
|
||||||
boolean enabled = SlimefunPlugin.getUpdater().isEnabled();
|
|
||||||
return enabled ? "enabled" : "disabled";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.AdvancedPie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.core.commands.SubCommand;
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class CommandChart extends AdvancedPie {
|
|
||||||
|
|
||||||
CommandChart() {
|
|
||||||
super("commands_ran", () -> {
|
|
||||||
Map<String, Integer> commands = new HashMap<>();
|
|
||||||
|
|
||||||
for (Map.Entry<SubCommand, Integer> entry : SlimefunPlugin.getCommand().getCommandUsage().entrySet()) {
|
|
||||||
commands.put("/sf " + entry.getKey().getName(), entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
return commands;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class CompatibilityModeChart extends SimplePie {
|
|
||||||
|
|
||||||
CompatibilityModeChart() {
|
|
||||||
super("compatibility_mode", () -> {
|
|
||||||
boolean enabled = SlimefunPlugin.getRegistry().isBackwardsCompatible();
|
|
||||||
return enabled ? "enabled" : "disabled";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class GuideLayoutChart extends SimplePie {
|
|
||||||
|
|
||||||
GuideLayoutChart() {
|
|
||||||
super("guide_layout", () -> {
|
|
||||||
boolean book = SlimefunPlugin.getCfg().getBoolean("guide.default-view-book");
|
|
||||||
|
|
||||||
return book ? "Book" : "Chest GUI";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This Class represents a Metrics Service that sends data to https://bstats.org/
|
|
||||||
* This data is used to analyse the usage of this {@link Plugin}.
|
|
||||||
*
|
|
||||||
* You can find more info in the README file of this Project on GitHub.
|
|
||||||
*
|
|
||||||
* @author TheBusyBiscuit
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MetricsService {
|
|
||||||
|
|
||||||
private final SlimefunPlugin plugin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This creates a new {@link MetricsService}. The constructor does not set up
|
|
||||||
* anything related to bStats yet, that happens in the {@link MetricsService#start()} method.
|
|
||||||
*
|
|
||||||
* @param plugin
|
|
||||||
* The instance of our {@link SlimefunPlugin}
|
|
||||||
*/
|
|
||||||
public MetricsService(SlimefunPlugin plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method intializes and starts the metrics collection.
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
Metrics metrics = new Metrics(plugin, 4574);
|
|
||||||
|
|
||||||
if (SlimefunPlugin.getUpdater().getBranch().isOfficial()) {
|
|
||||||
// We really do not need this data if it is an unofficially modified build...
|
|
||||||
metrics.addCustomChart(new AutoUpdaterChart());
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics.addCustomChart(new ResourcePackChart());
|
|
||||||
metrics.addCustomChart(new SlimefunVersionChart());
|
|
||||||
metrics.addCustomChart(new ServerLanguageChart());
|
|
||||||
metrics.addCustomChart(new PlayerLanguageChart());
|
|
||||||
metrics.addCustomChart(new ResearchesEnabledChart());
|
|
||||||
metrics.addCustomChart(new GuideLayoutChart());
|
|
||||||
metrics.addCustomChart(new AddonsChart());
|
|
||||||
metrics.addCustomChart(new CommandChart());
|
|
||||||
metrics.addCustomChart(new ServerSizeChart());
|
|
||||||
metrics.addCustomChart(new CompatibilityModeChart());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.AdvancedPie;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class PlayerLanguageChart extends AdvancedPie {
|
|
||||||
|
|
||||||
PlayerLanguageChart() {
|
|
||||||
super("player_languages", () -> {
|
|
||||||
Map<String, Integer> languages = new HashMap<>();
|
|
||||||
|
|
||||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
||||||
Language language = SlimefunPlugin.getLocalization().getLanguage(p);
|
|
||||||
boolean supported = SlimefunPlugin.getLocalization().isLanguageLoaded(language.getId());
|
|
||||||
|
|
||||||
String lang = supported ? language.getId() : "Unsupported Language";
|
|
||||||
languages.merge(lang, 1, Integer::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
return languages;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class ResearchesEnabledChart extends SimplePie {
|
|
||||||
|
|
||||||
ResearchesEnabledChart() {
|
|
||||||
super("servers_with_researches_enabled", () -> {
|
|
||||||
boolean enabled = SlimefunPlugin.getRegistry().isFreeCreativeResearchingEnabled();
|
|
||||||
return enabled ? "enabled" : "disabled";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class ResourcePackChart extends SimplePie {
|
|
||||||
|
|
||||||
ResourcePackChart() {
|
|
||||||
super("resourcepack", () -> {
|
|
||||||
String version = SlimefunPlugin.getItemTextureService().getVersion();
|
|
||||||
|
|
||||||
if (version != null && version.startsWith("v")) {
|
|
||||||
return version + " (Official)";
|
|
||||||
}
|
|
||||||
else if (SlimefunPlugin.getItemTextureService().isActive()) {
|
|
||||||
return "Custom / Modified";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "None";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.localization.Language;
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class ServerLanguageChart extends SimplePie {
|
|
||||||
|
|
||||||
ServerLanguageChart() {
|
|
||||||
super("language", () -> {
|
|
||||||
Language language = SlimefunPlugin.getLocalization().getDefaultLanguage();
|
|
||||||
boolean supported = SlimefunPlugin.getLocalization().isLanguageLoaded(language.getId());
|
|
||||||
return supported ? language.getId() : "Unsupported Language";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.SimplePie;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
|
|
||||||
class ServerSizeChart extends SimplePie {
|
|
||||||
|
|
||||||
ServerSizeChart() {
|
|
||||||
super("server_size", () -> {
|
|
||||||
int players = Bukkit.getOnlinePlayers().size();
|
|
||||||
|
|
||||||
if (players < 10) {
|
|
||||||
return "0-10";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players < 25) {
|
|
||||||
return "10-25";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players < 50) {
|
|
||||||
return "25-50";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players < 100) {
|
|
||||||
return "50-100";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players < 200) {
|
|
||||||
return "100-200";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (players < 300) {
|
|
||||||
return "200-300";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "300+";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.bstats.bukkit.Metrics.DrilldownPie;
|
|
||||||
|
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
|
|
||||||
|
|
||||||
class SlimefunVersionChart extends DrilldownPie {
|
|
||||||
|
|
||||||
SlimefunVersionChart() {
|
|
||||||
super("slimefun_version", () -> {
|
|
||||||
Map<String, Map<String, Integer>> outerMap = new HashMap<>();
|
|
||||||
Map<String, Integer> innerMap = new HashMap<>();
|
|
||||||
|
|
||||||
innerMap.put(SlimefunPlugin.getVersion(), 1);
|
|
||||||
outerMap.put(SlimefunPlugin.getUpdater().getBranch().getName(), innerMap);
|
|
||||||
|
|
||||||
return outerMap;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
/**
|
|
||||||
* This package contains everything related to bStats Metrics
|
|
||||||
*/
|
|
||||||
package io.github.thebusybiscuit.slimefun4.core.services.metrics;
|
|
@ -41,7 +41,7 @@ import io.github.thebusybiscuit.slimefun4.core.services.PerWorldSettingsService;
|
|||||||
import io.github.thebusybiscuit.slimefun4.core.services.PermissionsService;
|
import io.github.thebusybiscuit.slimefun4.core.services.PermissionsService;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.UpdaterService;
|
import io.github.thebusybiscuit.slimefun4.core.services.UpdaterService;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.github.GitHubService;
|
import io.github.thebusybiscuit.slimefun4.core.services.github.GitHubService;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.metrics.MetricsService;
|
import io.github.thebusybiscuit.slimefun4.core.services.MetricsService;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.plugins.ThirdPartyPluginService;
|
import io.github.thebusybiscuit.slimefun4.core.services.plugins.ThirdPartyPluginService;
|
||||||
import io.github.thebusybiscuit.slimefun4.core.services.profiler.SlimefunProfiler;
|
import io.github.thebusybiscuit.slimefun4.core.services.profiler.SlimefunProfiler;
|
||||||
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar;
|
import io.github.thebusybiscuit.slimefun4.implementation.items.altar.AncientAltar;
|
||||||
@ -196,7 +196,7 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
|
|||||||
networkManager = new NetworkManager(networkSize);
|
networkManager = new NetworkManager(networkSize);
|
||||||
|
|
||||||
// Setting up bStats
|
// Setting up bStats
|
||||||
metricsService.start();
|
new Thread(metricsService::start).start();
|
||||||
|
|
||||||
// Starting the Auto-Updater
|
// Starting the Auto-Updater
|
||||||
if (config.getBoolean("options.auto-update")) {
|
if (config.getBoolean("options.auto-update")) {
|
||||||
@ -367,6 +367,8 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
|
|||||||
// Create a new backup zip
|
// Create a new backup zip
|
||||||
backupService.run();
|
backupService.run();
|
||||||
|
|
||||||
|
metricsService.cleanUp();
|
||||||
|
|
||||||
// Prevent Memory Leaks
|
// Prevent Memory Leaks
|
||||||
// These static Maps should be removed at some point...
|
// These static Maps should be removed at some point...
|
||||||
AContainer.processing = null;
|
AContainer.processing = null;
|
||||||
@ -564,6 +566,16 @@ public final class SlimefunPlugin extends JavaPlugin implements SlimefunAddon {
|
|||||||
return instance.updaterService;
|
return instance.updaterService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the {@link MetricsService} of Slimefun.
|
||||||
|
* It is used to handle sending metric information to bStats.
|
||||||
|
*
|
||||||
|
* @return The {@link MetricsService} for Slimefun
|
||||||
|
*/
|
||||||
|
public static MetricsService getMetricsService() {
|
||||||
|
return instance.metricsService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns the {@link GitHubService} of Slimefun.
|
* This method returns the {@link GitHubService} of Slimefun.
|
||||||
* It is used to retrieve data from GitHub repositories.
|
* It is used to retrieve data from GitHub repositories.
|
||||||
|
@ -6,7 +6,7 @@ options:
|
|||||||
|
|
||||||
auto-update: true
|
auto-update: true
|
||||||
backwards-compatibility: true
|
backwards-compatibility: true
|
||||||
chat-prefix: '&a&lSlimefun 4 &7>'
|
chat-prefix: '&a&lSlimefun 4&7> '
|
||||||
armor-update-interval: 10
|
armor-update-interval: 10
|
||||||
enable-armor-effects: true
|
enable-armor-effects: true
|
||||||
auto-save-delay-in-minutes: 10
|
auto-save-delay-in-minutes: 10
|
||||||
@ -40,6 +40,9 @@ items:
|
|||||||
backpacks: true
|
backpacks: true
|
||||||
soulbound: true
|
soulbound: true
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
auto-update: true
|
||||||
|
|
||||||
research-ranks:
|
research-ranks:
|
||||||
- Chicken
|
- Chicken
|
||||||
- Cow
|
- Cow
|
||||||
|
Loading…
Reference in New Issue
Block a user