Repackage utils

This commit is contained in:
Daniel Mills
2021-07-16 02:11:37 -04:00
parent b9b30f9f53
commit da53a7d469
471 changed files with 1043 additions and 601 deletions

View File

@@ -0,0 +1,23 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
public interface CancellableTask {
void cancel();
}

View File

@@ -0,0 +1,31 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(FIELD)
public @interface Command {
String value() default "";
}

View File

@@ -0,0 +1,31 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(FIELD)
public @interface Control {
}

View File

@@ -0,0 +1,78 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.volmit.iris.Iris;
public abstract class Controller implements IController {
private int tickRate;
private final String name;
public Controller() {
name = getClass().getSimpleName().replaceAll("Controller", "") + " Controller";
tickRate = -1;
}
protected void setTickRate(@SuppressWarnings("SameParameterValue") int rate) {
this.tickRate = rate;
}
protected void disableTicking() {
setTickRate(-1);
}
@Override
public void l(Object l) {
Iris.info("[" + getName() + "]: " + l);
}
@Override
public void w(Object l) {
Iris.warn("[" + getName() + "]: " + l);
}
@Override
public void f(Object l) {
Iris.error("[" + getName() + "]: " + l);
}
@Override
public void v(Object l) {
Iris.verbose("[" + getName() + "]: " + l);
}
@Override
public String getName() {
return name;
}
@Override
public abstract void start();
@Override
public abstract void stop();
@Override
public abstract void tick();
@Override
public int getTickInterval() {
return tickRate;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
public interface IActivator {
}

View File

@@ -0,0 +1,70 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.volmit.iris.util.plugin.MortarSender;
/**
* Represents a pawn command
*
* @author cyberpwn
*/
public interface ICommand {
KList<String> getRequiredPermissions();
/**
* Get the name of this command (node)
*
* @return the node
*/
String getNode();
/**
* Get all (realized) nodes of this command
*
* @return the nodes
*/
KList<String> getNodes();
/**
* Get all (every) node in this command
*
* @return all nodes
*/
KList<String> getAllNodes();
/**
* Add a node to this command
*
* @param node the node
*/
void addNode(String node);
/**
* Handle a command. If this is a subcommand, parameters after the subcommand
* will be adapted in args for you
*
* @param sender the volume sender (pre-tagged)
* @param args the arguments after this command node
* @return return true to mark it as handled
*/
boolean handle(MortarSender sender, String[] args);
KList<String> handleTab(MortarSender sender, String[] args);
}

View File

@@ -0,0 +1,42 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import org.bukkit.event.Listener;
@SuppressWarnings("EmptyMethod")
public interface IController extends Listener {
String getName();
void start();
void stop();
void tick();
int getTickInterval();
void l(Object l);
void w(Object l);
void f(Object l);
void v(Object l);
}

View File

@@ -0,0 +1,31 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(FIELD)
public @interface Instance {
}

View File

@@ -0,0 +1,747 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.volmit.iris.Iris;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings({"WeakerAccess", "unused"})
public class Metrics {
static {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
final String defaultPackage = new String(
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private final boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private final Plugin plugin;
// The plugin id
private final int pluginId;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
* @param pluginId The id of the plugin.
* It can be found at <a href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
*/
public Metrics(Plugin plugin, int pluginId) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
this.pluginId = pluginId;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header(
"""
bStats collects some data for plugin authors like how many servers are using their plugins.
To honor their work, you should not disable it.
This has nearly no effect on the server performance!
Check out https://bStats.org/ to learn more :)"""
).copyDefaults(true);
try {
config.save(configFile);
} catch (IOException e) {
Iris.reportError(e);
}
}
// Load the data
enabled = true;
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException e) {Iris.reportError(e);
}
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Adds a custom chart.
*
* @param chart The chart to add.
*/
public void addCustomChart(CustomChart chart) {
if (chart == null) {
throw new IllegalArgumentException("Chart cannot be null!");
}
charts.add(chart);
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("id", pluginId); // Append the id of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
JsonArray customCharts = new JsonArray();
for (CustomChart customChart : charts) {
// Add the data of the custom charts
JsonObject chart = customChart.getRequestJsonObject();
if (chart == null) { // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
data.add("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {Iris.reportError(e);
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {Iris.reportError(e);
// minecraft version 1.14+
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e);
}
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
Iris.reportError(ignored);
}
}
} catch (NoSuchFieldException e) {Iris.reportError(e);
}
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(() -> {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {Iris.reportError(e);
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data);
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
/**
* Represents a custom chart.
*/
public static abstract class CustomChart {
// The id of the chart
final String chartId;
/**
* Class constructor.
*
* @param chartId The id of the chart.
*/
CustomChart(String chartId) {
if (chartId == null || chartId.isEmpty()) {
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
private JsonObject getRequestJsonObject() {
JsonObject chart = new JsonObject();
chart.addProperty("chartId", chartId);
try {
JsonObject data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
chart.add("data", data);
} catch (Throwable t) {Iris.reportError(t);
if (logFailedRequests) {
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return chart;
}
protected abstract JsonObject getChartData() throws Exception;
}
/**
* Represents a custom simple pie.
*/
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
data.addProperty("value", value);
return data;
}
}
/**
* Represents a custom advanced pie.
*/
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.addProperty(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom drilldown pie.
*/
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JsonObject value = new JsonObject();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
value.addProperty(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
values.add(entryValues.getKey(), value);
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom single line chart.
*/
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
data.addProperty("value", value);
return data;
}
}
/**
* Represents a custom multi line chart.
*/
public static class MultiLineChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.addProperty(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom simple bar chart.
*/
public static class SimpleBarChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
JsonArray categoryValues = new JsonArray();
categoryValues.add(new JsonPrimitive(entry.getValue()));
values.add(entry.getKey(), categoryValues);
}
data.add("values", values);
return data;
}
}
/**
* Represents a custom advanced bar chart.
*/
public static class AdvancedBarChart extends CustomChart {
private final Callable<Map<String, int[]>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JsonObject getChartData() throws Exception {
JsonObject data = new JsonObject();
JsonObject values = new JsonObject();
Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) {
continue; // Skip this invalid
}
allSkipped = false;
JsonArray categoryValues = new JsonArray();
for (int categoryValue : entry.getValue()) {
categoryValues.add(new JsonPrimitive(categoryValue));
}
values.add(entry.getKey(), categoryValues);
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.add("values", values);
return data;
}
}
}

View File

@@ -0,0 +1,394 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.volmit.iris.Iris;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*/
public class MetricsLite {
static {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this
// little "trick" ... :D
final String defaultPackage = new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong
// package names
if (MetricsLite.class.getPackage().getName().equals(defaultPackage) || MetricsLite.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private final boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private final Plugin plugin;
// The plugin id
private final int pluginId;
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
* @param pluginId The id of the plugin. It can be found at
* <a href="https://bstats.org/what-is-my-plugin-id">What is my
* plugin id?</a>
*/
public MetricsLite(Plugin plugin, int pluginId) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
this.pluginId = pluginId;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header("""
bStats collects some data for plugin authors like how many servers are using their plugins.
To honor their work, you should not disable it.
This has nearly no effect on the server performance!
Check out https://bStats.org/ to learn more :)""").copyDefaults(true);
try {
config.save(configFile);
} catch (IOException e) {
Iris.reportError(e);
}
}
// Load the data
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
enabled = config.getBoolean("enabled", true);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException e) {Iris.reportError(e);
}
}
// Register our service
Bukkit.getServicesManager().register(MetricsLite.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to
// use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the
// stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other
// plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be
// blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data. This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JsonObject getPluginData() {
JsonObject data = new JsonObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("id", pluginId); // Append the id of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
data.add("customCharts", new JsonArray());
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JsonObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError:
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) ? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size() : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {Iris.reportError(e);
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
String bukkitName = Bukkit.getName();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JsonObject data = new JsonObject();
data.addProperty("serverUUID", serverUUID);
data.addProperty("playerAmount", playerAmount);
data.addProperty("onlineMode", onlineMode);
data.addProperty("bukkitVersion", bukkitVersion);
data.addProperty("bukkitName", bukkitName);
data.addProperty("javaVersion", javaVersion);
data.addProperty("osName", osName);
data.addProperty("osArch", osArch);
data.addProperty("osVersion", osVersion);
data.addProperty("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JsonObject data = getServerData();
JsonArray pluginData = new JsonArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
if (plugin instanceof JsonObject) {
pluginData.add((JsonObject) plugin);
} else { // old bstats version compatibility
try {
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
jsonStringGetter.setAccessible(true);
String jsonString = (String) jsonStringGetter.invoke(plugin);
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
pluginData.add(object);
}
} catch (ClassNotFoundException e) {Iris.reportError(e);
// minecraft version 1.14+
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e);
}
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
Iris.reportError(ignored);
}
}
} catch (NoSuchFieldException e) {Iris.reportError(e);
}
}
data.add("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(() ->
{
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {Iris.reportError(e);
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JsonObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data);
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
}

View File

@@ -0,0 +1,213 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import org.bukkit.Sound;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* Represents a pawn command
*
* @author cyberpwn
*/
public abstract class MortarCommand implements ICommand {
private final KList<MortarCommand> children;
private final KList<String> nodes;
private final KList<String> requiredPermissions;
private final String node;
private String category;
private String description;
/**
* Override this with a super constructor as most commands shouldn't change
* these parameters
*
* @param node the node (primary node) i.e. volume
* @param nodes the aliases. i.e. v, vol, bile
*/
public MortarCommand(String node, String... nodes) {
category = "";
this.node = node;
this.nodes = new KList<>(nodes);
requiredPermissions = new KList<>();
children = buildChildren();
description = "No Description";
}
@Override
public KList<String> handleTab(MortarSender sender, String[] args) {
KList<String> v = new KList<>();
if (args.length == 0) {
for (MortarCommand i : getChildren()) {
v.add(i.getNode());
}
}
addTabOptions(sender, args, v);
if (v.isEmpty()) {
return null;
}
if (sender.isPlayer() && IrisSettings.get().getGeneral().isCommandSounds()) {
sender.player().getWorld().playSound(sender.player().getLocation(), Sound.ENTITY_ITEM_FRAME_ROTATE_ITEM, 0.25f, 1.7f);
}
return v;
}
public abstract void addTabOptions(MortarSender sender, String[] args, KList<String> list);
public void printHelp(MortarSender sender) {
boolean b = false;
for (MortarCommand i : getChildren()) {
for (String j : i.getRequiredPermissions()) {
if (!sender.hasPermission(j)) {
}
}
b = true;
sender.sendMessage(C.GREEN + i.getNode() + " " + C.WHITE + i.getArgsUsage() + C.GRAY + " - " + i.getDescription());
}
if (!b) {
sender.sendMessage("There are either no sub-commands or you do not have permission to use them.");
}
if (sender.isPlayer() && IrisSettings.get().getGeneral().isCommandSounds()) {
sender.player().getWorld().playSound(sender.player().getLocation(), Sound.ITEM_BOOK_PAGE_TURN, 0.28f, 1.4f);
sender.player().getWorld().playSound(sender.player().getLocation(), Sound.ITEM_AXE_STRIP, 0.35f, 1.7f);
}
}
protected abstract String getArgsUsage();
public String getDescription() {
return description;
}
protected void setDescription(String description) {
this.description = description;
}
protected void requiresPermission(MortarPermission node) {
if (node == null) {
return;
}
requiresPermission(node.toString());
}
protected void requiresPermission(String node) {
if (node == null) {
return;
}
requiredPermissions.add(node);
}
public void rejectAny(int past, MortarSender sender, String[] a) {
if (a.length > past) {
int p = past;
StringBuilder m = new StringBuilder();
for (String i : a) {
p--;
if (p < 0) {
m.append(i).append(", ");
}
}
if (!m.toString().trim().isEmpty()) {
sender.sendMessage("Parameters Ignored: " + m);
}
}
}
@Override
public String getNode() {
return node;
}
@Override
public KList<String> getNodes() {
return nodes;
}
@Override
public KList<String> getAllNodes() {
return getNodes().copy().qadd(getNode());
}
@Override
public void addNode(String node) {
getNodes().add(node);
}
public KList<MortarCommand> getChildren() {
return children;
}
private KList<MortarCommand> buildChildren() {
KList<MortarCommand> p = new KList<>();
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Command.class)) {
try {
i.setAccessible(true);
MortarCommand pc = (MortarCommand) i.getType().getConstructor().newInstance();
Command c = i.getAnnotation(Command.class);
if (!c.value().trim().isEmpty()) {
pc.setCategory(c.value().trim());
} else {
pc.setCategory(getCategory());
}
p.add(pc);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
Iris.reportError(e);
e.printStackTrace();
}
}
}
return p;
}
@Override
public KList<String> getRequiredPermissions() {
return requiredPermissions;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}

View File

@@ -0,0 +1,97 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.volmit.iris.Iris;
import org.bukkit.command.CommandSender;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
public abstract class MortarPermission {
private MortarPermission parent;
public MortarPermission() {
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Permission.class)) {
try {
MortarPermission px = (MortarPermission) i.getType().getConstructor().newInstance();
px.setParent(this);
i.set(Modifier.isStatic(i.getModifiers()) ? null : this, px);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
e.printStackTrace();
Iris.reportError(e);
}
}
}
}
public KList<MortarPermission> getChildren() {
KList<MortarPermission> p = new KList<>();
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Permission.class)) {
try {
p.add((MortarPermission) i.get(Modifier.isStatic(i.getModifiers()) ? null : this));
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
e.printStackTrace();
Iris.reportError(e);
}
}
}
return p;
}
public String getFullNode() {
if (hasParent()) {
return getParent().getFullNode() + "." + getNode();
}
return getNode();
}
protected abstract String getNode();
public abstract String getDescription();
public abstract boolean isDefault();
@Override
public String toString() {
return getFullNode();
}
public boolean hasParent() {
return getParent() != null;
}
public MortarPermission getParent() {
return parent;
}
public void setParent(MortarPermission parent) {
this.parent = parent;
}
public boolean has(CommandSender sender) {
return sender.hasPermission(getFullNode());
}
}

View File

@@ -0,0 +1,219 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.UUID;
/**
* Represents a volume sender. A command sender with extra crap in it
*
* @author cyberpwn
*/
public class MortarSender implements CommandSender {
private final CommandSender s;
private String tag;
@Getter
@Setter
private String command;
/**
* Wrap a command sender
*
* @param s the command sender
*/
public MortarSender(CommandSender s) {
tag = "";
this.s = s;
}
public MortarSender(CommandSender s, String tag) {
this.tag = tag;
this.s = s;
}
/**
* Set a command tag (prefix for sendMessage)
*
* @param tag the tag
*/
public void setTag(String tag) {
this.tag = tag;
}
/**
* Get the command tag
*
* @return the command tag
*/
public String getTag() {
return tag;
}
/**
* Is this sender a player?
*
* @return true if it is
*/
public boolean isPlayer() {
return getS() instanceof Player;
}
/**
* Force cast to player (be sure to check first)
*
* @return a casted player
*/
public Player player() {
return (Player) getS();
}
/**
* Get the origin sender this object is wrapping
*
* @return the command sender
*/
public CommandSender getS() {
return s;
}
@Override
public boolean isPermissionSet(@NotNull String name) {
return s.isPermissionSet(name);
}
@Override
public boolean isPermissionSet(@NotNull Permission perm) {
return s.isPermissionSet(perm);
}
@Override
public boolean hasPermission(@NotNull String name) {
return s.hasPermission(name);
}
@Override
public boolean hasPermission(@NotNull Permission perm) {
return s.hasPermission(perm);
}
@NotNull
@Override
public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value) {
return s.addAttachment(plugin, name, value);
}
@NotNull
@Override
public PermissionAttachment addAttachment(@NotNull Plugin plugin) {
return s.addAttachment(plugin);
}
@Override
public PermissionAttachment addAttachment(@NotNull Plugin plugin, @NotNull String name, boolean value, int ticks) {
return s.addAttachment(plugin, name, value, ticks);
}
@Override
public PermissionAttachment addAttachment(@NotNull Plugin plugin, int ticks) {
return s.addAttachment(plugin, ticks);
}
@Override
public void removeAttachment(@NotNull PermissionAttachment attachment) {
s.removeAttachment(attachment);
}
@Override
public void recalculatePermissions() {
s.recalculatePermissions();
}
@NotNull
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
return s.getEffectivePermissions();
}
@Override
public boolean isOp() {
return s.isOp();
}
@Override
public void setOp(boolean value) {
s.setOp(value);
}
public void hr() {
s.sendMessage("========================================================");
}
@Override
public void sendMessage(@NotNull String message) {
s.sendMessage(C.translateAlternateColorCodes('&', getTag()) + message);
}
@Override
public void sendMessage(String[] messages) {
for (String str : messages)
s.sendMessage(C.translateAlternateColorCodes('&', getTag() + str));
}
@Override
public void sendMessage(@Nullable UUID uuid, @NotNull String message) {
sendMessage(message);
}
@Override
public void sendMessage(@Nullable UUID uuid, @NotNull String[] messages) {
sendMessage(messages);
}
@NotNull
@Override
public Server getServer() {
return s.getServer();
}
@NotNull
@Override
public String getName() {
return s.getName();
}
@NotNull
@Override
public Spigot spigot() {
return s.spigot();
}
}

View File

@@ -0,0 +1,31 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(FIELD)
public @interface Permission {
}

View File

@@ -0,0 +1,65 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
/**
* Assistive command router
*
* @author cyberpwn
*/
public class RouterCommand extends org.bukkit.command.Command {
private final CommandExecutor ex;
private String usage;
/**
* The router command routes commands to bukkit executors
*
* @param realCommand the real command
* @param ex the executor
*/
public RouterCommand(ICommand realCommand, CommandExecutor ex) {
super(realCommand.getNode().toLowerCase());
setAliases(realCommand.getNodes());
this.ex = ex;
}
@NotNull
@Override
public Command setUsage(@NotNull String u) {
this.usage = u;
return this;
}
@NotNull
@Override
public String getUsage() {
return usage;
}
@Override
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
return ex.onCommand(sender, this, commandLabel, args);
}
}

View File

@@ -0,0 +1,182 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.plugin.MortarSender;
import com.volmit.iris.util.reflect.V;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.command.CommandSender;
import java.lang.reflect.Field;
/**
* Represents a virtual command. A chain of iterative processing through
* subcommands.
*
* @author cyberpwn
*/
public class VirtualCommand {
private final ICommand command;
private final String tag;
private final KMap<KList<String>, VirtualCommand> children;
public VirtualCommand(ICommand command) {
this(command, "");
}
public VirtualCommand(ICommand command, String tag) {
this.command = command;
children = new KMap<>();
this.tag = tag;
for (Field i : command.getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Command.class)) {
try {
Command cc = i.getAnnotation(Command.class);
ICommand cmd = (ICommand) i.getType().getConstructor().newInstance();
new V(command, true, true).set(i.getName(), cmd);
children.put(cmd.getAllNodes(), new VirtualCommand(cmd, cc.value().trim().isEmpty() ? tag : cc.value().trim()));
} catch (Exception e) {Iris.reportError(e);
e.printStackTrace();
}
}
}
}
public String getTag() {
return tag;
}
public ICommand getCommand() {
return command;
}
public KMap<KList<String>, VirtualCommand> getChildren() {
return children;
}
public boolean hit(CommandSender sender, KList<String> chain) {
return hit(sender, chain, null);
}
public boolean hit(CommandSender sender, KList<String> chain, String label) {
MortarSender vs = new MortarSender(sender);
vs.setTag(tag);
if (label != null) {
vs.setCommand(label);
}
if (chain.isEmpty()) {
if (!checkPermissions(sender, command)) {
return true;
}
return command.handle(vs, new String[0]);
}
String nl = chain.get(0);
for (KList<String> i : children.k()) {
for (String j : i) {
if (j.equalsIgnoreCase(nl)) {
vs.setCommand(chain.get(0));
VirtualCommand cmd = children.get(i);
KList<String> c = chain.copy();
c.remove(0);
if (cmd.hit(sender, c, vs.getCommand())) {
if (vs.isPlayer() && IrisSettings.get().getGeneral().isCommandSounds()) {
vs.player().getWorld().playSound(vs.player().getLocation(), Sound.ITEM_AXE_STRIP, 0.35f, 1.8f);
}
return true;
}
}
}
}
if (!checkPermissions(sender, command)) {
return true;
}
return command.handle(vs, chain.toArray(new String[0]));
}
public KList<String> hitTab(CommandSender sender, KList<String> chain, String label) {
MortarSender vs = new MortarSender(sender);
vs.setTag(tag);
if (label != null)
vs.setCommand(label);
if (chain.isEmpty()) {
if (!checkPermissions(sender, command)) {
return null;
}
return command.handleTab(vs, new String[0]);
}
String nl = chain.get(0);
for (KList<String> i : children.k()) {
for (String j : i) {
if (j.equalsIgnoreCase(nl)) {
vs.setCommand(chain.get(0));
VirtualCommand cmd = children.get(i);
KList<String> c = chain.copy();
c.remove(0);
KList<String> v = cmd.hitTab(sender, c, vs.getCommand());
if (v != null) {
return v;
}
}
}
}
if (!checkPermissions(sender, command)) {
return null;
}
return command.handleTab(vs, chain.toArray(new String[0]));
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean checkPermissions(CommandSender sender, ICommand command2) {
boolean failed = false;
for (String i : command.getRequiredPermissions()) {
if (!sender.hasPermission(i)) {
failed = true;
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> sender.sendMessage("- " + C.WHITE + i), 0);
}
}
if (failed) {
sender.sendMessage("Insufficient Permissions");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,702 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util;
import com.volmit.iris.Iris;
import com.volmit.iris.util.math.M;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.*;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredListener;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
@SuppressWarnings("EmptyMethod")
public abstract class VolmitPlugin extends JavaPlugin implements Listener {
public static final boolean bad = false;
private KMap<KList<String>, VirtualCommand> commands;
private KList<MortarCommand> commandCache;
private KList<MortarPermission> permissionCache;
private KMap<String, IController> controllers;
private KList<IController> cachedControllers;
private KMap<Class<? extends IController>, IController> cachedClassControllers;
public void selfDistruct() {
HandlerList.unregisterAll((Plugin) this);
getServer().getScheduler().cancelTasks(this);
File me = getFile();
Plugin plugin = this;
String name = plugin.getName();
SimpleCommandMap commandMap = null;
List<Plugin> plugins = null;
Map<String, Plugin> names = null;
Map<String, Command> commands = null;
Map<Event, SortedSet<RegisteredListener>> listeners = null;
try {
Field pluginsField = Bukkit.getPluginManager().getClass().getDeclaredField("plugins");
pluginsField.setAccessible(true);
//noinspection unchecked
plugins = (List<Plugin>) pluginsField.get(getServer().getPluginManager());
Field lookupNamesField = Bukkit.getPluginManager().getClass().getDeclaredField("lookupNames");
lookupNamesField.setAccessible(true);
//noinspection unchecked
names = (Map<String, Plugin>) lookupNamesField.get(getServer().getPluginManager());
try {
Field listenersField = Bukkit.getPluginManager().getClass().getDeclaredField("listeners");
listenersField.setAccessible(true);
//noinspection unchecked
listeners = (Map<Event, SortedSet<RegisteredListener>>) listenersField.get(getServer().getPluginManager());
} catch (Throwable e) {Iris.reportError(e);
}
Field commandMapField = Bukkit.getPluginManager().getClass().getDeclaredField("commandMap");
commandMapField.setAccessible(true);
commandMap = (SimpleCommandMap) commandMapField.get(getServer().getPluginManager());
Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
knownCommandsField.setAccessible(true);
//noinspection unchecked
commands = (Map<String, Command>) knownCommandsField.get(commandMap);
} catch (Throwable e) {Iris.reportError(e);
}
getServer().getPluginManager().disablePlugin(plugin);
plugins.remove(plugin);
names.remove(name);
if (listeners != null) {
for (SortedSet<RegisteredListener> set : listeners.values()) {
set.removeIf(value -> value.getPlugin().equals(plugin));
}
}
for (Iterator<Map.Entry<String, Command>> it = commands.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Command> entry = it.next();
if (entry.getValue() instanceof PluginCommand c) {
if (c.getPlugin() == plugin) {
c.unregister(commandMap);
it.remove();
}
}
}
new Thread(() -> {
// Attempt to close the classloader to unlock any handles on the
// plugin's
// jar file.
ClassLoader cl = plugin.getClass().getClassLoader();
if (cl instanceof URLClassLoader) {
try {
((URLClassLoader) cl).close();
} catch (IOException e) {Iris.reportError(e);
}
}
// Will not work on processes started with the -XX:+DisableExplicitGC
// flag,
// but lets try it anyway. This tries to get around the issue where
// Windows
// refuses to unlock jar files that were previously loaded into the JVM.
System.gc();
if (!me.delete()) {
me.deleteOnExit();
}
}).start();
}
public void l(Object l) {
Iris.info("[" + getName() + "]: " + l);
}
public void w(Object l) {
Iris.warn("[" + getName() + "]: " + l);
}
public void f(Object l) {
Iris.error("[" + getName() + "]: " + l);
}
public void v(Object l) {
Iris.verbose("[" + getName() + "]: " + l);
}
public void onEnable() {
registerInstance();
registerPermissions();
registerCommands();
registerControllers();
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, this::tickControllers, 0, 0);
J.a(this::outputInfo);
registerListener(this);
start();
}
public void unregisterAll() {
stopControllers();
unregisterListeners();
unregisterCommands();
unregisterPermissions();
unregisterInstance();
}
private void outputInfo() {
try {
IO.delete(getDataFolder("info"));
getDataFolder("info").mkdirs();
outputPluginInfo();
outputCommandInfo();
outputPermissionInfo();
} catch (Throwable e) {Iris.reportError(e);
}
}
private void outputPermissionInfo() throws IOException {
FileConfiguration fc = new YamlConfiguration();
for (MortarPermission i : permissionCache) {
chain(i, fc);
}
fc.save(getDataFile("info", "permissions.yml"));
}
private void chain(MortarPermission i, FileConfiguration fc) {
KList<String> ff = new KList<>();
for (MortarPermission j : i.getChildren()) {
ff.add(j.getFullNode());
}
fc.set(i.getFullNode().replaceAll("\\Q.\\E", ",") + "." + "description", i.getDescription());
fc.set(i.getFullNode().replaceAll("\\Q.\\E", ",") + "." + "default", i.isDefault());
fc.set(i.getFullNode().replaceAll("\\Q.\\E", ",") + "." + "children", ff);
for (MortarPermission j : i.getChildren()) {
chain(j, fc);
}
}
private void outputCommandInfo() throws IOException {
FileConfiguration fc = new YamlConfiguration();
for (MortarCommand i : commandCache) {
chain(i, "/", fc);
}
fc.save(getDataFile("info", "commands.yml"));
}
private void chain(MortarCommand i, String c, FileConfiguration fc) {
String n = c + (c.length() == 1 ? "" : " ") + i.getNode();
fc.set(n + "." + "description", i.getDescription());
fc.set(n + "." + "required-permissions", i.getRequiredPermissions());
fc.set(n + "." + "aliases", i.getAllNodes());
for (MortarCommand j : i.getChildren()) {
chain(j, n, fc);
}
}
private void outputPluginInfo() throws IOException {
FileConfiguration fc = new YamlConfiguration();
fc.set("version", getDescription().getVersion());
fc.set("name", getDescription().getName());
fc.save(getDataFile("info", "plugin.yml"));
}
private void registerPermissions() {
permissionCache = new KList<>();
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Permission.class)) {
try {
i.setAccessible(true);
MortarPermission pc = (MortarPermission) i.getType().getConstructor().newInstance();
i.set(Modifier.isStatic(i.getModifiers()) ? null : this, pc);
registerPermission(pc);
permissionCache.add(pc);
v("Registered Permissions " + pc.getFullNode() + " (" + i.getName() + ")");
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
Iris.reportError(e);
w("Failed to register permission (field " + i.getName() + ")");
e.printStackTrace();
}
}
}
for (org.bukkit.permissions.Permission i : computePermissions()) {
try {
Bukkit.getPluginManager().addPermission(i);
} catch (Throwable e) {Iris.reportError(e);
}
}
}
private KList<org.bukkit.permissions.Permission> computePermissions() {
KList<org.bukkit.permissions.Permission> g = new KList<>();
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Permission.class)) {
try {
MortarPermission x = (MortarPermission) i.get(Modifier.isStatic(i.getModifiers()) ? null : this);
g.add(toPermission(x));
g.addAll(computePermissions(x));
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
Iris.reportError(e);e.printStackTrace();
}
}
}
return g.removeDuplicates();
}
private KList<org.bukkit.permissions.Permission> computePermissions(MortarPermission p) {
KList<org.bukkit.permissions.Permission> g = new KList<>();
if (p == null) {
return g;
}
for (MortarPermission i : p.getChildren()) {
if (i == null) {
continue;
}
g.add(toPermission(i));
g.addAll(computePermissions(i));
}
return g;
}
private org.bukkit.permissions.Permission toPermission(MortarPermission p) {
if (p == null) {
return null;
}
org.bukkit.permissions.Permission perm = new org.bukkit.permissions.Permission(p.getFullNode() + (p.hasParent() ? "" : ".*"));
perm.setDescription(p.getDescription() == null ? "" : p.getDescription());
perm.setDefault(p.isDefault() ? PermissionDefault.TRUE : PermissionDefault.OP);
for (MortarPermission i : p.getChildren()) {
perm.getChildren().put(i.getFullNode(), true);
}
return perm;
}
private void registerPermission(MortarPermission pc) {
}
@Override
public void onDisable() {
stop();
Bukkit.getScheduler().cancelTasks(this);
unregisterListener(this);
unregisterAll();
}
private void tickControllers() {
if (bad) {
return;
}
for (IController i : getControllers()) {
tickController(i);
}
}
private void tickController(IController i) {
if (bad) {
return;
}
if (i.getTickInterval() < 0) {
return;
}
M.tick++;
if (M.interval(i.getTickInterval())) {
try {
i.tick();
} catch (Throwable e) {
w("Failed to tick controller " + i.getName());
e.printStackTrace();Iris.reportError(e);
}
}
}
public KList<IController> getControllers() {
if (bad) {
return new KList<>();
}
return cachedControllers;
}
private void registerControllers() {
if (bad) {
return;
}
controllers = new KMap<>();
cachedClassControllers = new KMap<>();
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Control.class)) {
try {
i.setAccessible(true);
IController pc = (IController) i.getType().getConstructor().newInstance();
registerController(pc);
i.set(this, pc);
v("Registered " + pc.getName() + " (" + i.getName() + ")");
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
w("Failed to register controller (field " + i.getName() + ")");
e.printStackTrace();Iris.reportError(e);
}
}
}
cachedControllers = controllers.v();
}
public IController getController(Class<? extends IController> c) {
return cachedClassControllers.get(c);
}
private void registerController(IController pc) {
if (bad) {
return;
}
controllers.put(pc.getName(), pc);
cachedClassControllers.put(pc.getClass(), pc);
registerListener(pc);
try {
pc.start();
v("Started " + pc.getName());
} catch (Throwable e) {
w("Failed to start controller " + pc.getName());
e.printStackTrace();Iris.reportError(e);
}
}
private void registerInstance() {
if (bad) {
return;
}
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Instance.class)) {
try {
i.setAccessible(true);
i.set(Modifier.isStatic(i.getModifiers()) ? null : this, this);
v("Registered Instance " + i.getName());
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
w("Failed to register instance (field " + i.getName() + ")");
e.printStackTrace();Iris.reportError(e);
}
}
}
}
private void unregisterInstance() {
if (bad) {
return;
}
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(Instance.class)) {
try {
i.setAccessible(true);
i.set(Modifier.isStatic(i.getModifiers()) ? null : this, null);
v("Unregistered Instance " + i.getName());
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
w("Failed to unregister instance (field " + i.getName() + ")");
e.printStackTrace();Iris.reportError(e);
}
}
}
}
private void registerCommands() {
if (bad) {
return;
}
commands = new KMap<>();
commandCache = new KList<>();
for (Field i : getClass().getDeclaredFields()) {
if (i.isAnnotationPresent(com.volmit.iris.util.Command.class)) {
try {
i.setAccessible(true);
MortarCommand pc = (MortarCommand) i.getType().getConstructor().newInstance();
com.volmit.iris.util.Command c = i.getAnnotation(com.volmit.iris.util.Command.class);
registerCommand(pc, c.value());
commandCache.add(pc);
v("Registered Commands /" + pc.getNode() + " (" + i.getName() + ")");
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
w("Failed to register command (field " + i.getName() + ")");
e.printStackTrace();Iris.reportError(e);
}
}
}
}
@Nullable
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String alias, @NotNull String[] args) {
KList<String> chain = new KList<>();
for (String i : args) {
if (i.trim().isEmpty()) {
continue;
}
chain.add(i.trim());
}
for (KList<String> i : commands.k()) {
for (String j : i) {
if (j.equalsIgnoreCase(alias)) {
VirtualCommand cmd = commands.get(i);
List<String> v = cmd.hitTab(sender, chain.copy(), alias);
if (v != null) {
return v;
}
}
}
}
return super.onTabComplete(sender, command, alias, args);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull org.bukkit.command.Command command, @NotNull String label, @NotNull String[] args) {
if (bad) {
return false;
}
KList<String> chain = new KList<>();
chain.add(args);
for (KList<String> i : commands.k()) {
for (String j : i) {
if (j.equalsIgnoreCase(label)) {
VirtualCommand cmd = commands.get(i);
if (cmd.hit(sender, chain.copy(), label)) {
return true;
}
}
}
}
return false;
}
public void registerCommand(ICommand cmd) {
registerCommand(cmd, "");
}
public void registerCommand(ICommand cmd, String subTag) {
if (bad) {
return;
}
commands.put(cmd.getAllNodes(), new VirtualCommand(cmd, subTag.trim().isEmpty() ? getTag() : getTag(subTag.trim())));
PluginCommand cc = getCommand(cmd.getNode().toLowerCase());
if (cc != null) {
cc.setExecutor(this);
cc.setUsage(getName() + ":" + getClass().toString() + ":" + cmd.getNode());
} else {
RouterCommand r = new RouterCommand(cmd, this);
r.setUsage(getName() + ":" + getClass().toString());
((CommandMap) new com.volmit.iris.util.V(Bukkit.getServer()).get("commandMap")).register("", r);
}
}
public void unregisterCommand(ICommand cmd) {
if (bad) {
return;
}
try {
SimpleCommandMap m = new com.volmit.iris.util.V(Bukkit.getServer()).get("commandMap");
Map<String, Command> k = new com.volmit.iris.util.V(m).get("knownCommands");
for (Iterator<Map.Entry<String, Command>> it = k.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Command> entry = it.next();
if (entry.getValue() instanceof Command) {
org.bukkit.command.Command c = entry.getValue();
String u = c.getUsage();
if (u != null && u.equals(getName() + ":" + getClass().toString() + ":" + cmd.getNode())) {
if (c.unregister(m)) {
it.remove();
v("Unregistered Command /" + cmd.getNode());
} else {
Bukkit.getConsoleSender().sendMessage(getTag() + "Failed to unregister command " + c.getName());
}
}
}
}
} catch (Throwable e) {
e.printStackTrace();Iris.reportError(e);
}
}
public String getTag() {
if (bad) {
return "";
}
return getTag("");
}
public void registerListener(Listener l) {
if (bad) {
return;
}
Bukkit.getPluginManager().registerEvents(l, this);
}
public void unregisterListener(Listener l) {
if (bad) {
return;
}
HandlerList.unregisterAll(l);
}
public void unregisterListeners() {
if (bad) {
return;
}
HandlerList.unregisterAll((Listener) this);
}
public void unregisterCommands() {
if (bad) {
return;
}
for (VirtualCommand i : commands.v()) {
try {
unregisterCommand(i.getCommand());
} catch (Throwable e) {Iris.reportError(e);
}
}
}
private void unregisterPermissions() {
if (bad) {
return;
}
for (org.bukkit.permissions.Permission i : computePermissions()) {
Bukkit.getPluginManager().removePermission(i);
v("Unregistered Permission " + i.getName());
}
}
private void stopControllers() {
if (bad) {
return;
}
for (IController i : controllers.v()) {
try {
unregisterListener(i);
i.stop();
v("Stopped " + i.getName());
} catch (Throwable e) {Iris.reportError(e);
w("Failed to stop controller " + i.getName());
e.printStackTrace();
}
}
}
public File getDataFile(String... strings) {
File f = new File(getDataFolder(), new KList<>(strings).toString(File.separator));
f.getParentFile().mkdirs();
return f;
}
public File getDataFileList(String pre, String[] strings) {
KList<String> v = new KList<>(strings);
v.add(0, pre);
File f = new File(getDataFolder(), v.toString(File.separator));
f.getParentFile().mkdirs();
return f;
}
public File getDataFolder(String... strings) {
if (strings.length == 0) {
return super.getDataFolder();
}
File f = new File(getDataFolder(), new KList<>(strings).toString(File.separator));
f.mkdirs();
return f;
}
public File getDataFolderList(String pre, String[] strings) {
KList<String> v = new KList<>(strings);
v.add(0, pre);
if (v.size() == 0) {
return super.getDataFolder();
}
File f = new File(getDataFolder(), v.toString(File.separator));
f.mkdirs();
return f;
}
public abstract void start();
public abstract void stop();
public abstract String getTag(String subTag);
}