forked from circlecloud/MiaoChat
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d9322cbac4 | |||
| 95591ec5ff | |||
| 11fb155a37 |
BIN
MiaoChat.jar
BIN
MiaoChat.jar
Binary file not shown.
120
pom.xml
120
pom.xml
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>MiaoChat</artifactId>
|
<artifactId>MiaoChat</artifactId>
|
||||||
<version>2.0.0</version>
|
<version>2.2.3</version>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>pw.yumc</groupId>
|
<groupId>pw.yumc</groupId>
|
||||||
@@ -13,13 +13,20 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<update.description>§a正式版本 §bv2.0.0 §b全新版本(号) 配合龙核食用</update.description>
|
<update.description>§a正式版本 §bv2.2.3 §b默认支持RGB色彩</update.description>
|
||||||
<update.changes>
|
<update.changes>
|
||||||
§621-12-18 §a增强: 全新版本(号) 新增 itemTip 配合龙核 实现神奇功能;
|
§622-11-23 §a增强: 默认支持RGB色彩;
|
||||||
§621-12-06 §a增强: 兼容 1.18 版本 兼容新版PAPI;
|
§622-11-21 §a增强: 兼容 1.19.2(1.19.x部分版本存在BUG);
|
||||||
§621-06-20 §a增强: 兼容 1.17 版本
|
§622-07-05 §a增强: 兼容 Paper 1.19;
|
||||||
|
§622-06-17 §a增强: 兼容 1.19 修复 快捷指令错误;
|
||||||
|
§622-04-13 §c修复: 部分版本JDK导致启动错误
|
||||||
</update.changes>
|
</update.changes>
|
||||||
<update.changelog>
|
<update.changelog>
|
||||||
|
§622-04-12 §a增强: 兼容 1.18.2 修复 PAPI 注销报错;
|
||||||
|
§622-02-25 §a增强: 新增 %a-z 自定义解析 实现神奇功能 兼容 1.18.1#133+;
|
||||||
|
§621-12-18 §a增强: 全新版本(号) 新增 itemTip 配合龙核 实现神奇功能;
|
||||||
|
§621-12-06 §a增强: 兼容 1.18 版本 兼容新版PAPI;
|
||||||
|
§621-06-20 §a增强: 兼容 1.17 版本;
|
||||||
§620-10-10 §c修复: 1.16.3聊天包格式调整的问题;
|
§620-10-10 §c修复: 1.16.3聊天包格式调整的问题;
|
||||||
§620-04-10 §c修复: L10N 本地化组件报错的问题;
|
§620-04-10 §c修复: L10N 本地化组件报错的问题;
|
||||||
§620-04-09 §c修复: 1.13-1.15.2新增物品不兼容的问题;
|
§620-04-09 §c修复: 1.13-1.15.2新增物品不兼容的问题;
|
||||||
@@ -35,109 +42,6 @@
|
|||||||
</update.changelog>
|
</update.changelog>
|
||||||
<env.GIT_COMMIT>DEV</env.GIT_COMMIT>
|
<env.GIT_COMMIT>DEV</env.GIT_COMMIT>
|
||||||
</properties>
|
</properties>
|
||||||
<repositories>
|
|
||||||
<repository>
|
|
||||||
<id>yumc-repo</id>
|
|
||||||
<url>https://repo.yumc.pw/repository/maven-public/</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
|
||||||
<id>placeholderapi</id>
|
|
||||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
|
||||||
<id>spigot-repo</id>
|
|
||||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.spigotmc</groupId>
|
|
||||||
<artifactId>spigot-api</artifactId>
|
|
||||||
<version>1.16.5-R0.1-SNAPSHOT</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.md-5</groupId>
|
|
||||||
<artifactId>bungeecord-api</artifactId>
|
|
||||||
<version>1.12-SNAPSHOT</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<artifactId>snakeyaml</artifactId>
|
|
||||||
<groupId>org.yaml</groupId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>me.clip</groupId>
|
|
||||||
<artifactId>placeholderapi</artifactId>
|
|
||||||
<version>2.10.9</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<artifactId>spigot-api</artifactId>
|
|
||||||
<groupId>org.spigotmc</groupId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.black_ixx</groupId>
|
|
||||||
<artifactId>PlayerPoints</artifactId>
|
|
||||||
<version>2.1.3</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.milkbowl.vault</groupId>
|
|
||||||
<artifactId>Vault</artifactId>
|
|
||||||
<version>1.7.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>javax.mail</groupId>
|
|
||||||
<artifactId>mail</artifactId>
|
|
||||||
<version>1.4.7</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.bukkit</groupId>
|
|
||||||
<artifactId>craftbukkit</artifactId>
|
|
||||||
<version>1.7.10</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.comphenix.protocol</groupId>
|
|
||||||
<artifactId>ProtocolLib</artifactId>
|
|
||||||
<version>4.5.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.sk89q.worldedit</groupId>
|
|
||||||
<artifactId>worldedit-bukkit</artifactId>
|
|
||||||
<version>6.1.5</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.puharesource.mc</groupId>
|
|
||||||
<artifactId>TitleManager</artifactId>
|
|
||||||
<version>2.0.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.alibaba</groupId>
|
|
||||||
<artifactId>fastjson</artifactId>
|
|
||||||
<version>1.2.28</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.projectlombok</groupId>
|
|
||||||
<artifactId>lombok</artifactId>
|
|
||||||
<version>1.18.22</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>junit</groupId>
|
|
||||||
<artifactId>junit</artifactId>
|
|
||||||
<version>4.12</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
@@ -1,842 +0,0 @@
|
|||||||
package org.bstats.bukkit;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
|
||||||
import java.io.*;
|
|
||||||
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.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
public class Metrics {
|
|
||||||
|
|
||||||
private final Plugin plugin;
|
|
||||||
|
|
||||||
private final MetricsBase metricsBase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Metrics instance.
|
|
||||||
*
|
|
||||||
* @param plugin Your plugin instance.
|
|
||||||
* @param serviceId The id of the service. It can be found at <a
|
|
||||||
* href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
|
|
||||||
*/
|
|
||||||
public Metrics(JavaPlugin plugin, int serviceId) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
// Get the config file
|
|
||||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
|
||||||
File configFile = new File(bStatsFolder, "config.yml");
|
|
||||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
|
||||||
if (!config.isSet("serverUuid")) {
|
|
||||||
config.addDefault("enabled", true);
|
|
||||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
|
||||||
config.addDefault("logFailedRequests", false);
|
|
||||||
config.addDefault("logSentData", false);
|
|
||||||
config.addDefault("logResponseStatusText", false);
|
|
||||||
// Inform the server owners about bStats
|
|
||||||
config
|
|
||||||
.options()
|
|
||||||
.header(
|
|
||||||
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n"
|
|
||||||
+ "many people use their plugin and their total player count. It's recommended to keep bStats\n"
|
|
||||||
+ "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n"
|
|
||||||
+ "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n"
|
|
||||||
+ "anonymous.")
|
|
||||||
.copyDefaults(true);
|
|
||||||
try {
|
|
||||||
config.save(configFile);
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Load the data
|
|
||||||
boolean enabled = config.getBoolean("enabled", true);
|
|
||||||
String serverUUID = config.getString("serverUuid");
|
|
||||||
boolean logErrors = config.getBoolean("logFailedRequests", false);
|
|
||||||
boolean logSentData = config.getBoolean("logSentData", false);
|
|
||||||
boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
|
||||||
metricsBase =
|
|
||||||
new MetricsBase(
|
|
||||||
"bukkit",
|
|
||||||
serverUUID,
|
|
||||||
serviceId,
|
|
||||||
enabled,
|
|
||||||
this::appendPlatformData,
|
|
||||||
this::appendServiceData,
|
|
||||||
submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask),
|
|
||||||
plugin::isEnabled,
|
|
||||||
(message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error),
|
|
||||||
(message) -> this.plugin.getLogger().log(Level.INFO, message),
|
|
||||||
logErrors,
|
|
||||||
logSentData,
|
|
||||||
logResponseStatusText);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a custom chart.
|
|
||||||
*
|
|
||||||
* @param chart The chart to add.
|
|
||||||
*/
|
|
||||||
public void addCustomChart(CustomChart chart) {
|
|
||||||
metricsBase.addCustomChart(chart);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendPlatformData(JsonObjectBuilder builder) {
|
|
||||||
builder.appendField("playerAmount", getPlayerAmount());
|
|
||||||
builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0);
|
|
||||||
builder.appendField("bukkitVersion", Bukkit.getVersion());
|
|
||||||
builder.appendField("bukkitName", Bukkit.getName());
|
|
||||||
builder.appendField("javaVersion", System.getProperty("java.version"));
|
|
||||||
builder.appendField("osName", System.getProperty("os.name"));
|
|
||||||
builder.appendField("osArch", System.getProperty("os.arch"));
|
|
||||||
builder.appendField("osVersion", System.getProperty("os.version"));
|
|
||||||
builder.appendField("coreCount", Runtime.getRuntime().availableProcessors());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void appendServiceData(JsonObjectBuilder builder) {
|
|
||||||
builder.appendField("pluginVersion", plugin.getDescription().getVersion());
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getPlayerAmount() {
|
|
||||||
try {
|
|
||||||
// Around MC 1.8 the return type was changed from an array to a collection,
|
|
||||||
// This fixes java.lang.NoSuchMethodError:
|
|
||||||
// org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
|
||||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
|
||||||
return onlinePlayersMethod.getReturnType().equals(Collection.class)
|
|
||||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
|
||||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Just use the new method if the reflection failed
|
|
||||||
return Bukkit.getOnlinePlayers().size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MetricsBase {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The version of the Metrics class.
|
|
||||||
*/
|
|
||||||
public static final String METRICS_VERSION = "2.2.1";
|
|
||||||
|
|
||||||
private static final ScheduledExecutorService scheduler =
|
|
||||||
Executors.newScheduledThreadPool(1, task -> new Thread(task, "bStats-Metrics"));
|
|
||||||
|
|
||||||
private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s";
|
|
||||||
|
|
||||||
private final String platform;
|
|
||||||
|
|
||||||
private final String serverUuid;
|
|
||||||
|
|
||||||
private final int serviceId;
|
|
||||||
|
|
||||||
private final Consumer<JsonObjectBuilder> appendPlatformDataConsumer;
|
|
||||||
|
|
||||||
private final Consumer<JsonObjectBuilder> appendServiceDataConsumer;
|
|
||||||
|
|
||||||
private final Consumer<Runnable> submitTaskConsumer;
|
|
||||||
|
|
||||||
private final Supplier<Boolean> checkServiceEnabledSupplier;
|
|
||||||
|
|
||||||
private final BiConsumer<String, Throwable> errorLogger;
|
|
||||||
|
|
||||||
private final Consumer<String> infoLogger;
|
|
||||||
|
|
||||||
private final boolean logErrors;
|
|
||||||
|
|
||||||
private final boolean logSentData;
|
|
||||||
|
|
||||||
private final boolean logResponseStatusText;
|
|
||||||
|
|
||||||
private final Set<CustomChart> customCharts = new HashSet<>();
|
|
||||||
|
|
||||||
private final boolean enabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new MetricsBase class instance.
|
|
||||||
*
|
|
||||||
* @param platform The platform of the service.
|
|
||||||
* @param serviceId The id of the service.
|
|
||||||
* @param serverUuid The server uuid.
|
|
||||||
* @param enabled Whether or not data sending is enabled.
|
|
||||||
* @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
|
||||||
* appends all platform-specific data.
|
|
||||||
* @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and
|
|
||||||
* appends all service-specific data.
|
|
||||||
* @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be
|
|
||||||
* used to delegate the data collection to a another thread to prevent errors caused by
|
|
||||||
* concurrency. Can be {@code null}.
|
|
||||||
* @param checkServiceEnabledSupplier A supplier to check if the service is still enabled.
|
|
||||||
* @param errorLogger A consumer that accepts log message and an error.
|
|
||||||
* @param infoLogger A consumer that accepts info log messages.
|
|
||||||
* @param logErrors Whether or not errors should be logged.
|
|
||||||
* @param logSentData Whether or not the sent data should be logged.
|
|
||||||
* @param logResponseStatusText Whether or not the response status text should be logged.
|
|
||||||
*/
|
|
||||||
public MetricsBase(
|
|
||||||
String platform,
|
|
||||||
String serverUuid,
|
|
||||||
int serviceId,
|
|
||||||
boolean enabled,
|
|
||||||
Consumer<JsonObjectBuilder> appendPlatformDataConsumer,
|
|
||||||
Consumer<JsonObjectBuilder> appendServiceDataConsumer,
|
|
||||||
Consumer<Runnable> submitTaskConsumer,
|
|
||||||
Supplier<Boolean> checkServiceEnabledSupplier,
|
|
||||||
BiConsumer<String, Throwable> errorLogger,
|
|
||||||
Consumer<String> infoLogger,
|
|
||||||
boolean logErrors,
|
|
||||||
boolean logSentData,
|
|
||||||
boolean logResponseStatusText) {
|
|
||||||
this.platform = platform;
|
|
||||||
this.serverUuid = serverUuid;
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.enabled = enabled;
|
|
||||||
this.appendPlatformDataConsumer = appendPlatformDataConsumer;
|
|
||||||
this.appendServiceDataConsumer = appendServiceDataConsumer;
|
|
||||||
this.submitTaskConsumer = submitTaskConsumer;
|
|
||||||
this.checkServiceEnabledSupplier = checkServiceEnabledSupplier;
|
|
||||||
this.errorLogger = errorLogger;
|
|
||||||
this.infoLogger = infoLogger;
|
|
||||||
this.logErrors = logErrors;
|
|
||||||
this.logSentData = logSentData;
|
|
||||||
this.logResponseStatusText = logResponseStatusText;
|
|
||||||
checkRelocation();
|
|
||||||
if (enabled) {
|
|
||||||
startSubmitting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCustomChart(CustomChart chart) {
|
|
||||||
this.customCharts.add(chart);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startSubmitting() {
|
|
||||||
final Runnable submitTask =
|
|
||||||
() -> {
|
|
||||||
if (!enabled || !checkServiceEnabledSupplier.get()) {
|
|
||||||
// Submitting data or service is disabled
|
|
||||||
scheduler.shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (submitTaskConsumer != null) {
|
|
||||||
submitTaskConsumer.accept(this::submitData);
|
|
||||||
} else {
|
|
||||||
this.submitData();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution
|
|
||||||
// of requests on the
|
|
||||||
// bStats backend. To circumvent this problem, we introduce some randomness into the initial
|
|
||||||
// and second delay.
|
|
||||||
// WARNING: You must not modify and part of this Metrics class, including the submit delay or
|
|
||||||
// frequency!
|
|
||||||
// WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it!
|
|
||||||
long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
|
||||||
long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
|
||||||
scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
|
||||||
scheduler.scheduleAtFixedRate(
|
|
||||||
submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void submitData() {
|
|
||||||
final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder();
|
|
||||||
appendPlatformDataConsumer.accept(baseJsonBuilder);
|
|
||||||
final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder();
|
|
||||||
appendServiceDataConsumer.accept(serviceJsonBuilder);
|
|
||||||
JsonObjectBuilder.JsonObject[] chartData =
|
|
||||||
customCharts.stream()
|
|
||||||
.map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors))
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toArray(JsonObjectBuilder.JsonObject[]::new);
|
|
||||||
serviceJsonBuilder.appendField("id", serviceId);
|
|
||||||
serviceJsonBuilder.appendField("customCharts", chartData);
|
|
||||||
baseJsonBuilder.appendField("service", serviceJsonBuilder.build());
|
|
||||||
baseJsonBuilder.appendField("serverUUID", serverUuid);
|
|
||||||
baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION);
|
|
||||||
JsonObjectBuilder.JsonObject data = baseJsonBuilder.build();
|
|
||||||
scheduler.execute(
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
// Send the data
|
|
||||||
sendData(data);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Something went wrong! :(
|
|
||||||
if (logErrors) {
|
|
||||||
errorLogger.accept("Could not submit bStats metrics data", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendData(JsonObjectBuilder.JsonObject data) throws Exception {
|
|
||||||
if (logSentData) {
|
|
||||||
infoLogger.accept("Sent bStats metrics data: " + data.toString());
|
|
||||||
}
|
|
||||||
String url = String.format(REPORT_URL, platform);
|
|
||||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
|
||||||
// Compress the data to save bandwidth
|
|
||||||
byte[] compressedData = compress(data.toString());
|
|
||||||
connection.setRequestMethod("POST");
|
|
||||||
connection.addRequestProperty("Accept", "application/json");
|
|
||||||
connection.addRequestProperty("Connection", "close");
|
|
||||||
connection.addRequestProperty("Content-Encoding", "gzip");
|
|
||||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
|
||||||
connection.setRequestProperty("Content-Type", "application/json");
|
|
||||||
connection.setRequestProperty("User-Agent", "Metrics-Service/1");
|
|
||||||
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) {
|
|
||||||
infoLogger.accept("Sent data to bStats and received response: " + builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that the class was properly relocated.
|
|
||||||
*/
|
|
||||||
private void checkRelocation() {
|
|
||||||
// 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'});
|
|
||||||
final String examplePackage =
|
|
||||||
new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
|
||||||
// We want to make sure no one just copy & pastes the example and uses the wrong package
|
|
||||||
// names
|
|
||||||
if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage)
|
|
||||||
|| MetricsBase.class.getPackage().getName().startsWith(examplePackage)) {
|
|
||||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gzips the given string.
|
|
||||||
*
|
|
||||||
* @param str The string to gzip.
|
|
||||||
* @return The gzipped string.
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
|
||||||
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) {
|
|
||||||
// Skip this invalid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
allSkipped = false;
|
|
||||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
if (allSkipped) {
|
|
||||||
// Null = skip the chart
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
|
||||||
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()) {
|
|
||||||
valuesBuilder.appendField(entry.getKey(), new int[]{entry.getValue()});
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
|
||||||
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) {
|
|
||||||
// Skip this invalid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
allSkipped = false;
|
|
||||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
if (allSkipped) {
|
|
||||||
// Null = skip the chart
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
|
||||||
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) {
|
|
||||||
// Skip this invalid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
allSkipped = false;
|
|
||||||
valuesBuilder.appendField(entry.getKey(), entry.getValue());
|
|
||||||
}
|
|
||||||
if (allSkipped) {
|
|
||||||
// Null = skip the chart
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract static class CustomChart {
|
|
||||||
|
|
||||||
private final String chartId;
|
|
||||||
|
|
||||||
protected CustomChart(String chartId) {
|
|
||||||
if (chartId == null) {
|
|
||||||
throw new IllegalArgumentException("chartId must not be null");
|
|
||||||
}
|
|
||||||
this.chartId = chartId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonObjectBuilder.JsonObject getRequestJsonObject(
|
|
||||||
BiConsumer<String, Throwable> errorLogger, boolean logErrors) {
|
|
||||||
JsonObjectBuilder builder = new JsonObjectBuilder();
|
|
||||||
builder.appendField("chartId", chartId);
|
|
||||||
try {
|
|
||||||
JsonObjectBuilder.JsonObject data = getChartData();
|
|
||||||
if (data == null) {
|
|
||||||
// If the data is null we don't send the chart.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
builder.appendField("data", data);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
if (logErrors) {
|
|
||||||
errorLogger.accept("Failed to get data for custom chart with id " + chartId, t);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
int value = callable.call();
|
|
||||||
if (value == 0) {
|
|
||||||
// Null = skip the chart
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("value", value).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
String value = callable.call();
|
|
||||||
if (value == null || value.isEmpty()) {
|
|
||||||
// Null = skip the chart
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("value", value).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 JsonObjectBuilder.JsonObject getChartData() throws Exception {
|
|
||||||
JsonObjectBuilder valuesBuilder = new JsonObjectBuilder();
|
|
||||||
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()) {
|
|
||||||
JsonObjectBuilder valueBuilder = new JsonObjectBuilder();
|
|
||||||
boolean allSkipped = true;
|
|
||||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
|
||||||
valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue());
|
|
||||||
allSkipped = false;
|
|
||||||
}
|
|
||||||
if (!allSkipped) {
|
|
||||||
reallyAllSkipped = false;
|
|
||||||
valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (reallyAllSkipped) {
|
|
||||||
// Null = skip the chart
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An extremely simple JSON builder.
|
|
||||||
*
|
|
||||||
* <p>While this class is neither feature-rich nor the most performant one, it's sufficient enough
|
|
||||||
* for its use-case.
|
|
||||||
*/
|
|
||||||
public static class JsonObjectBuilder {
|
|
||||||
|
|
||||||
private StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
private boolean hasAtLeastOneField = false;
|
|
||||||
|
|
||||||
public JsonObjectBuilder() {
|
|
||||||
builder.append("{");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a null field to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendNull(String key) {
|
|
||||||
appendFieldUnescaped(key, "null");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a string field to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param value The value of the field.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendField(String key, String value) {
|
|
||||||
if (value == null) {
|
|
||||||
throw new IllegalArgumentException("JSON value must not be null");
|
|
||||||
}
|
|
||||||
appendFieldUnescaped(key, "\"" + escape(value) + "\"");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends an integer field to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param value The value of the field.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendField(String key, int value) {
|
|
||||||
appendFieldUnescaped(key, String.valueOf(value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends an object to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param object The object.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendField(String key, JsonObject object) {
|
|
||||||
if (object == null) {
|
|
||||||
throw new IllegalArgumentException("JSON object must not be null");
|
|
||||||
}
|
|
||||||
appendFieldUnescaped(key, object.toString());
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a string array to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param values The string array.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendField(String key, String[] values) {
|
|
||||||
if (values == null) {
|
|
||||||
throw new IllegalArgumentException("JSON values must not be null");
|
|
||||||
}
|
|
||||||
String escapedValues =
|
|
||||||
Arrays.stream(values)
|
|
||||||
.map(value -> "\"" + escape(value) + "\"")
|
|
||||||
.collect(Collectors.joining(","));
|
|
||||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends an integer array to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param values The integer array.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendField(String key, int[] values) {
|
|
||||||
if (values == null) {
|
|
||||||
throw new IllegalArgumentException("JSON values must not be null");
|
|
||||||
}
|
|
||||||
String escapedValues =
|
|
||||||
Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
|
|
||||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends an object array to the JSON.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param values The integer array.
|
|
||||||
* @return A reference to this object.
|
|
||||||
*/
|
|
||||||
public JsonObjectBuilder appendField(String key, JsonObject[] values) {
|
|
||||||
if (values == null) {
|
|
||||||
throw new IllegalArgumentException("JSON values must not be null");
|
|
||||||
}
|
|
||||||
String escapedValues =
|
|
||||||
Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
|
|
||||||
appendFieldUnescaped(key, "[" + escapedValues + "]");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Appends a field to the object.
|
|
||||||
*
|
|
||||||
* @param key The key of the field.
|
|
||||||
* @param escapedValue The escaped value of the field.
|
|
||||||
*/
|
|
||||||
private void appendFieldUnescaped(String key, String escapedValue) {
|
|
||||||
if (builder == null) {
|
|
||||||
throw new IllegalStateException("JSON has already been built");
|
|
||||||
}
|
|
||||||
if (key == null) {
|
|
||||||
throw new IllegalArgumentException("JSON key must not be null");
|
|
||||||
}
|
|
||||||
if (hasAtLeastOneField) {
|
|
||||||
builder.append(",");
|
|
||||||
}
|
|
||||||
builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
|
|
||||||
hasAtLeastOneField = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds the JSON string and invalidates this builder.
|
|
||||||
*
|
|
||||||
* @return The built JSON string.
|
|
||||||
*/
|
|
||||||
public JsonObject build() {
|
|
||||||
if (builder == null) {
|
|
||||||
throw new IllegalStateException("JSON has already been built");
|
|
||||||
}
|
|
||||||
JsonObject object = new JsonObject(builder.append("}").toString());
|
|
||||||
builder = null;
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
|
|
||||||
*
|
|
||||||
* <p>This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
|
|
||||||
* Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
|
|
||||||
*
|
|
||||||
* @param value The value to escape.
|
|
||||||
* @return The escaped value.
|
|
||||||
*/
|
|
||||||
private static String escape(String value) {
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
for (int i = 0; i < value.length(); i++) {
|
|
||||||
char c = value.charAt(i);
|
|
||||||
if (c == '"') {
|
|
||||||
builder.append("\\\"");
|
|
||||||
} else if (c == '\\') {
|
|
||||||
builder.append("\\\\");
|
|
||||||
} else if (c <= '\u000F') {
|
|
||||||
builder.append("\\u000").append(Integer.toHexString(c));
|
|
||||||
} else if (c <= '\u001F') {
|
|
||||||
builder.append("\\u00").append(Integer.toHexString(c));
|
|
||||||
} else {
|
|
||||||
builder.append(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A super simple representation of a JSON object.
|
|
||||||
*
|
|
||||||
* <p>This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
|
|
||||||
* allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
|
|
||||||
* JsonObject)}.
|
|
||||||
*/
|
|
||||||
public static class JsonObject {
|
|
||||||
|
|
||||||
private final String value;
|
|
||||||
|
|
||||||
private JsonObject(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
//
|
|
||||||
// Source code recreated from a .class file by IntelliJ IDEA
|
|
||||||
// (powered by FernFlower decompiler)
|
|
||||||
//
|
|
||||||
|
|
||||||
package org.bukkit.configuration.file;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.Validate;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.configuration.Configuration;
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
|
||||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
import org.yaml.snakeyaml.error.YAMLException;
|
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class YamlConfiguration extends FileConfiguration {
|
|
||||||
protected static final String COMMENT_PREFIX = "# ";
|
|
||||||
protected static final String BLANK_CONFIG = "{}\n";
|
|
||||||
private final DumperOptions yamlOptions = new DumperOptions();
|
|
||||||
private final Representer yamlRepresenter = new YamlRepresenter();
|
|
||||||
private final Yaml yaml;
|
|
||||||
|
|
||||||
public YamlConfiguration() {
|
|
||||||
this.yaml = new Yaml(new YamlConstructor(), this.yamlRepresenter, this.yamlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String saveToString() {
|
|
||||||
this.yamlOptions.setIndent(this.options().indent());
|
|
||||||
this.yamlOptions.setDefaultFlowStyle(FlowStyle.BLOCK);
|
|
||||||
this.yamlRepresenter.setDefaultFlowStyle(FlowStyle.BLOCK);
|
|
||||||
String header = this.buildHeader();
|
|
||||||
String dump = this.yaml.dump(this.getValues(false));
|
|
||||||
if (dump.equals("{}\n")) {
|
|
||||||
dump = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return header + dump;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFromString(String contents) throws InvalidConfigurationException {
|
|
||||||
Validate.notNull(contents, "Contents cannot be null");
|
|
||||||
|
|
||||||
Map input;
|
|
||||||
try {
|
|
||||||
input = (Map) this.yaml.load(contents);
|
|
||||||
} catch (YAMLException var4) {
|
|
||||||
throw new InvalidConfigurationException(var4);
|
|
||||||
} catch (ClassCastException var5) {
|
|
||||||
throw new InvalidConfigurationException("Top level is not a Map.");
|
|
||||||
}
|
|
||||||
|
|
||||||
String header = this.parseHeader(contents);
|
|
||||||
if (header.length() > 0) {
|
|
||||||
this.options().header(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input != null) {
|
|
||||||
this.convertMapsToSections(input, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void convertMapsToSections(Map<?, ?> input, ConfigurationSection section) {
|
|
||||||
Iterator var4 = input.entrySet().iterator();
|
|
||||||
|
|
||||||
while (var4.hasNext()) {
|
|
||||||
Entry<?, ?> entry = (Entry) var4.next();
|
|
||||||
String key = entry.getKey().toString();
|
|
||||||
Object value = entry.getValue();
|
|
||||||
if (value instanceof Map) {
|
|
||||||
this.convertMapsToSections((Map) value, section.createSection(key));
|
|
||||||
} else {
|
|
||||||
section.set(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String parseHeader(String input) {
|
|
||||||
String[] lines = input.split("\r?\n", -1);
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
boolean readingHeader = true;
|
|
||||||
boolean foundHeader = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < lines.length && readingHeader; ++i) {
|
|
||||||
String line = lines[i];
|
|
||||||
if (line.startsWith("# ")) {
|
|
||||||
if (i > 0) {
|
|
||||||
result.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.length() > "# ".length()) {
|
|
||||||
result.append(line.substring("# ".length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
foundHeader = true;
|
|
||||||
} else if (foundHeader && line.length() == 0) {
|
|
||||||
result.append("\n");
|
|
||||||
} else if (foundHeader) {
|
|
||||||
readingHeader = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String buildHeader() {
|
|
||||||
String header = this.options().header();
|
|
||||||
if (this.options().copyHeader()) {
|
|
||||||
Configuration def = this.getDefaults();
|
|
||||||
if (def != null && def instanceof FileConfiguration) {
|
|
||||||
FileConfiguration filedefaults = (FileConfiguration) def;
|
|
||||||
String defaultsHeader = filedefaults.buildHeader();
|
|
||||||
if (defaultsHeader != null && defaultsHeader.length() > 0) {
|
|
||||||
return defaultsHeader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header == null) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
String[] lines = header.split("\r?\n", -1);
|
|
||||||
boolean startedHeader = false;
|
|
||||||
|
|
||||||
for (int i = lines.length - 1; i >= 0; --i) {
|
|
||||||
builder.insert(0, "\n");
|
|
||||||
if (startedHeader || lines[i].length() != 0) {
|
|
||||||
builder.insert(0, lines[i]);
|
|
||||||
builder.insert(0, "# ");
|
|
||||||
startedHeader = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public YamlConfigurationOptions options() {
|
|
||||||
if (this.options == null) {
|
|
||||||
this.options = new YamlConfigurationOptions(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (YamlConfigurationOptions) this.options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static YamlConfiguration loadConfiguration(File file) {
|
|
||||||
Validate.notNull(file, "File cannot be null");
|
|
||||||
YamlConfiguration config = new YamlConfiguration();
|
|
||||||
|
|
||||||
try {
|
|
||||||
config.load(file);
|
|
||||||
} catch (FileNotFoundException var3) {
|
|
||||||
} catch (IOException var4) {
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file, var4);
|
|
||||||
} catch (InvalidConfigurationException var5) {
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + file, var5);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static YamlConfiguration loadConfiguration(Reader reader) {
|
|
||||||
Validate.notNull(reader, "Stream cannot be null");
|
|
||||||
YamlConfiguration config = new YamlConfiguration();
|
|
||||||
|
|
||||||
try {
|
|
||||||
config.load(reader);
|
|
||||||
} catch (IOException var3) {
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", var3);
|
|
||||||
} catch (InvalidConfigurationException var4) {
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "Cannot load configuration from stream", var4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -67,7 +67,7 @@ public class MiaoChat extends JavaPlugin implements Executor, PluginMessageListe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
this.expansion.unregister();
|
try {this.expansion.unregister();} catch (Throwable ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableBungeeCord() {
|
private void enableBungeeCord() {
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
package pw.yumc.MiaoChat;
|
package pw.yumc.MiaoChat;
|
||||||
|
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created on 16-9-8.
|
* Created on 16-9-8.
|
||||||
|
*
|
||||||
* @author MiaoWoo
|
* @author MiaoWoo
|
||||||
*/
|
*/
|
||||||
public class MiaoMessage {
|
public class MiaoMessage {
|
||||||
|
|
||||||
public static final String CHANNEL = "MiaoChat:Default".toLowerCase();
|
public static final String CHANNEL = "MiaoChat:Default".toLowerCase();
|
||||||
public static final String NORMAL_CHANNEL = "MiaoChat:Normal".toLowerCase();
|
public static final String NORMAL_CHANNEL = "MiaoChat:Normal".toLowerCase();
|
||||||
|
private static final Pattern RGB_PATTERN = Pattern.compile("#[a-fA-F0-9]{6}");
|
||||||
private static final int MAX_MESSAGE_LENGTH = 32000;
|
private static final int MAX_MESSAGE_LENGTH = 32000;
|
||||||
private String json;
|
private String json;
|
||||||
|
|
||||||
@@ -58,4 +62,19 @@ public class MiaoMessage {
|
|||||||
input.close();
|
input.close();
|
||||||
output.close();
|
output.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String rgb(String message) {
|
||||||
|
Matcher matcher = RGB_PATTERN.matcher(message);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String hexCode = message.substring(matcher.start(), matcher.end());
|
||||||
|
String replaceSharp = hexCode.replace('#', 'x');
|
||||||
|
char[] ch = replaceSharp.toCharArray();
|
||||||
|
StringBuilder builder = new StringBuilder("");
|
||||||
|
for (char c : ch) {
|
||||||
|
builder.append("&").append(c);
|
||||||
|
}
|
||||||
|
message = message.replace(hexCode, builder.toString());
|
||||||
|
}
|
||||||
|
return ChatColor.translateAlternateColorCodes('&', message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
package pw.yumc.MiaoChat.config;
|
package pw.yumc.MiaoChat.config;
|
||||||
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import pw.yumc.YumCore.bukkit.Log;
|
|
||||||
import pw.yumc.YumCore.bukkit.P;
|
|
||||||
import pw.yumc.YumCore.config.AbstractConfig;
|
|
||||||
import pw.yumc.YumCore.config.FileConfig;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import pw.yumc.YumCore.bukkit.Log;
|
||||||
|
import pw.yumc.YumCore.bukkit.P;
|
||||||
|
import pw.yumc.YumCore.config.FileConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 喵♂呜
|
* @author 喵♂呜
|
||||||
* @since 2016年9月9日 下午4:40:50
|
* @since 2016年9月9日 下午4:40:50
|
||||||
*/
|
*/
|
||||||
public class ChatConfig extends AbstractConfig {
|
public class ChatConfig {
|
||||||
private static String F = "Formats";
|
private static String F = "Formats";
|
||||||
private Map<String, ChatMessagePart> formats;
|
private Map<String, ChatMessagePart> formats;
|
||||||
private LinkedList<ChatRule> rules;
|
private LinkedList<ChatRule> rules;
|
||||||
@@ -48,9 +48,7 @@ public class ChatConfig extends AbstractConfig {
|
|||||||
*/
|
*/
|
||||||
public ChatRule getChatRule(Player player) {
|
public ChatRule getChatRule(Player player) {
|
||||||
for (ChatRule cr : rules) {
|
for (ChatRule cr : rules) {
|
||||||
if (cr.check(player)) {
|
if (cr.check(player)) { return cr; }
|
||||||
return cr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package pw.yumc.MiaoChat.config;
|
|||||||
import me.clip.placeholderapi.PlaceholderAPI;
|
import me.clip.placeholderapi.PlaceholderAPI;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import pw.yumc.MiaoChat.MiaoMessage;
|
||||||
import pw.yumc.YumCore.config.annotation.ConfigNode;
|
import pw.yumc.YumCore.config.annotation.ConfigNode;
|
||||||
import pw.yumc.YumCore.config.annotation.Nullable;
|
import pw.yumc.YumCore.config.annotation.Nullable;
|
||||||
import pw.yumc.YumCore.config.inject.InjectConfigurationSection;
|
import pw.yumc.YumCore.config.inject.InjectConfigurationSection;
|
||||||
import pw.yumc.YumCore.tellraw.Tellraw;
|
import pw.yumc.YumCore.tellraw.Tellraw;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ChatMessagePart extends InjectConfigurationSection {
|
public class ChatMessagePart extends InjectConfigurationSection {
|
||||||
private String text;
|
private String text;
|
||||||
@@ -53,10 +55,10 @@ public class ChatMessagePart extends InjectConfigurationSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<String> f(Player player, List<String> text) {
|
private List<String> f(Player player, List<String> text) {
|
||||||
return PlaceholderAPI.setPlaceholders(player, text);
|
return text.stream().map((line) -> f(player, line)).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String f(Player player, String text) {
|
private String f(Player player, String text) {
|
||||||
return PlaceholderAPI.setPlaceholders(player, text);
|
return MiaoMessage.rgb(PlaceholderAPI.setPlaceholders(player, text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
package pw.yumc.MiaoChat.config;
|
package pw.yumc.MiaoChat.config;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import me.clip.placeholderapi.PlaceholderAPI;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import me.clip.placeholderapi.PlaceholderAPI;
|
|
||||||
import pw.yumc.MiaoChat.MiaoChat;
|
import pw.yumc.MiaoChat.MiaoChat;
|
||||||
import pw.yumc.YumCore.bukkit.P;
|
import pw.yumc.YumCore.bukkit.P;
|
||||||
import pw.yumc.YumCore.config.annotation.Default;
|
import pw.yumc.YumCore.config.annotation.Default;
|
||||||
import pw.yumc.YumCore.config.inject.InjectConfigurationSection;
|
import pw.yumc.YumCore.config.inject.InjectConfigurationSection;
|
||||||
import pw.yumc.YumCore.tellraw.Tellraw;
|
import pw.yumc.YumCore.tellraw.Tellraw;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天规则
|
* 聊天规则
|
||||||
*
|
*
|
||||||
@@ -22,8 +21,9 @@ import pw.yumc.YumCore.tellraw.Tellraw;
|
|||||||
* @since 2016年9月9日 下午4:59:47
|
* @since 2016年9月9日 下午4:59:47
|
||||||
*/
|
*/
|
||||||
public class ChatRule extends InjectConfigurationSection {
|
public class ChatRule extends InjectConfigurationSection {
|
||||||
private transient static MiaoChat plugin = P.getPlugin();
|
private final transient static MiaoChat plugin = P.getPlugin();
|
||||||
private transient static Pattern FORMAT_PATTERN = Pattern.compile("[\\[]([^\\[\\]]+)[]]");
|
private final transient static Pattern FORMAT_PATTERN = Pattern.compile("[\\[]([^\\[\\]]+)[]]");
|
||||||
|
|
||||||
private transient String name;
|
private transient String name;
|
||||||
@Default("50")
|
@Default("50")
|
||||||
private Integer index;
|
private Integer index;
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
package pw.yumc.MiaoChat.listeners;
|
package pw.yumc.MiaoChat.listeners;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@@ -16,7 +9,6 @@ import org.bukkit.event.EventPriority;
|
|||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
import pw.yumc.MiaoChat.MiaoChat;
|
import pw.yumc.MiaoChat.MiaoChat;
|
||||||
import pw.yumc.MiaoChat.MiaoMessage;
|
import pw.yumc.MiaoChat.MiaoMessage;
|
||||||
import pw.yumc.MiaoChat.config.ChatConfig;
|
import pw.yumc.MiaoChat.config.ChatConfig;
|
||||||
@@ -28,14 +20,18 @@ import pw.yumc.YumCore.statistic.Statistics;
|
|||||||
import pw.yumc.YumCore.tellraw.Tellraw;
|
import pw.yumc.YumCore.tellraw.Tellraw;
|
||||||
import pw.yumc.YumCore.update.SubscribeTask;
|
import pw.yumc.YumCore.update.SubscribeTask;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author MiaoWoo
|
* @author MiaoWoo
|
||||||
*/
|
*/
|
||||||
public class ChatListener implements Listener {
|
public class ChatListener implements Listener {
|
||||||
public static Set<Player> offList = new HashSet<>();
|
public static Set<Player> offList = new HashSet<>();
|
||||||
private static Pattern ITEM_PATTERN = Pattern.compile("%([i1-9]?)");
|
private static Pattern PATTERN = Pattern.compile("%([a-z1-9]?)");
|
||||||
|
|
||||||
private final Queue<String> queue = new LinkedList<>();
|
|
||||||
|
|
||||||
private MiaoChat plugin = P.getPlugin();
|
private MiaoChat plugin = P.getPlugin();
|
||||||
private ChatConfig cc = plugin.getChatConfig();
|
private ChatConfig cc = plugin.getChatConfig();
|
||||||
@@ -85,7 +81,7 @@ public class ChatListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private LinkedList<String> handlePattern(String message) {
|
private LinkedList<String> handlePattern(String message) {
|
||||||
Matcher m = ITEM_PATTERN.matcher(message);
|
Matcher m = PATTERN.matcher(message);
|
||||||
Set<String> temp = new HashSet<>();
|
Set<String> temp = new HashSet<>();
|
||||||
LinkedList<String> ilist = new LinkedList<>();
|
LinkedList<String> ilist = new LinkedList<>();
|
||||||
// Log.d("处理聊天物品信息...");
|
// Log.d("处理聊天物品信息...");
|
||||||
@@ -130,6 +126,9 @@ public class ChatListener implements Listener {
|
|||||||
if (player.hasPermission("MiaoChat.color")) {
|
if (player.hasPermission("MiaoChat.color")) {
|
||||||
message = ChatColor.translateAlternateColorCodes('&', message);
|
message = ChatColor.translateAlternateColorCodes('&', message);
|
||||||
}
|
}
|
||||||
|
if (player.hasPermission("MiaoChat.rgb")) {
|
||||||
|
message = MiaoMessage.rgb(message);
|
||||||
|
}
|
||||||
if (!cr.isItem()) {
|
if (!cr.isItem()) {
|
||||||
tr.then(message);
|
tr.then(message);
|
||||||
return tr;
|
return tr;
|
||||||
@@ -146,11 +145,30 @@ public class ChatListener implements Listener {
|
|||||||
String mm = ml.removeFirst();
|
String mm = ml.removeFirst();
|
||||||
if (il.contains(mm)) {
|
if (il.contains(mm)) {
|
||||||
char k = mm.charAt(1);
|
char k = mm.charAt(1);
|
||||||
ItemStack is = k == 'i' ? player.getItemInHand() : player.getInventory().getItem(k - '0' - 1);
|
String key = String.valueOf(k);
|
||||||
|
if (plugin.getChatConfig().getFormats().containsKey(key)) {
|
||||||
|
if (!player.hasPermission("MiaoChat.format.*") && player.hasPermission("MiaoChat.format." + k)) {
|
||||||
|
Log.sender(player, "§c你没有使用 " + mm + " 的权限!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
plugin.getChatConfig().getFormats().get(key).then(tr, player);
|
||||||
|
} else {
|
||||||
|
ItemStack is = null;
|
||||||
|
if (k == 'i') {
|
||||||
|
is = player.getItemInHand();
|
||||||
|
} else {
|
||||||
|
int index = k - '0' - 1;
|
||||||
|
if (index < 10) {
|
||||||
|
is = player.getInventory().getItem(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (is != null && is.getType() != Material.AIR) {
|
if (is != null && is.getType() != Material.AIR) {
|
||||||
// Log.d("处理物品: %s", mm);
|
// Log.d("处理物品: %s", mm);
|
||||||
tr.then(String.format(ChatColor.translateAlternateColorCodes('&', cr.getItemformat()), L10N.getName(is)));
|
tr.then(String.format(ChatColor.translateAlternateColorCodes('&', cr.getItemformat()), L10N.getName(is)));
|
||||||
tr.item(is);
|
tr.item(is);
|
||||||
|
} else {
|
||||||
|
tr.then(cr.getLastColor() + mm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Log.d("追加聊天: %s", mm);
|
// Log.d("追加聊天: %s", mm);
|
||||||
|
|||||||
@@ -1,203 +0,0 @@
|
|||||||
//
|
|
||||||
// Source code recreated from a .class file by IntelliJ IDEA
|
|
||||||
// (powered by FernFlower decompiler)
|
|
||||||
//
|
|
||||||
|
|
||||||
package pw.yumc.YumCore.config;
|
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
|
||||||
import org.apache.commons.lang.Validate;
|
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.yaml.snakeyaml.DumperOptions;
|
|
||||||
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
import org.yaml.snakeyaml.error.YAMLException;
|
|
||||||
import org.yaml.snakeyaml.representer.Representer;
|
|
||||||
import pw.yumc.YumCore.bukkit.Log;
|
|
||||||
import pw.yumc.YumCore.bukkit.P;
|
|
||||||
import pw.yumc.YumCore.config.yaml.BukkitConstructor;
|
|
||||||
import pw.yumc.YumCore.config.yaml.BukkitRepresenter;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public abstract class AbstractConfig extends YamlConfiguration {
|
|
||||||
private static String CONTENT_NOT_BE_NULL = "内容不能为 null";
|
|
||||||
protected static Charset UTF_8 = Charset.forName("UTF-8");
|
|
||||||
protected static String FILE_NOT_BE_NULL = "文件不能为 NULL";
|
|
||||||
protected static String CREATE_NEW_CONFIG = "配置: 创建新的文件 %s ...";
|
|
||||||
protected static String newLine = "\n";
|
|
||||||
protected static Plugin plugin = P.instance;
|
|
||||||
protected DumperOptions yamlOptions = new DumperOptions();
|
|
||||||
protected Representer yamlRepresenter;
|
|
||||||
protected Yaml yamlz;
|
|
||||||
protected Map contentsMap;
|
|
||||||
protected String data;
|
|
||||||
|
|
||||||
public AbstractConfig() {
|
|
||||||
this.yamlRepresenter = BukkitRepresenter.DEFAULT;
|
|
||||||
this.yamlz = new Yaml(BukkitConstructor.DEFAULT, this.yamlRepresenter, this.yamlOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map getContentMap() {
|
|
||||||
return this.contentsMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load(File file) throws IOException, InvalidConfigurationException {
|
|
||||||
Validate.notNull(file, FILE_NOT_BE_NULL);
|
|
||||||
FileInputStream stream = new FileInputStream(file);
|
|
||||||
this.load((Reader) (new InputStreamReader(stream, UTF_8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load(Reader reader) throws IOException, InvalidConfigurationException {
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
|
|
||||||
Throwable var4 = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = input.readLine()) != null) {
|
|
||||||
builder.append(line);
|
|
||||||
builder.append(newLine);
|
|
||||||
}
|
|
||||||
} catch (Throwable var13) {
|
|
||||||
var4 = var13;
|
|
||||||
throw var13;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (input != null) {
|
|
||||||
if (var4 != null) {
|
|
||||||
try {
|
|
||||||
input.close();
|
|
||||||
} catch (Throwable var12) {
|
|
||||||
var4.addSuppressed(var12);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
input.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loadFromString(builder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadFromString(String contents) throws InvalidConfigurationException {
|
|
||||||
Validate.notNull(contents, CONTENT_NOT_BE_NULL);
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.contentsMap = (Map) this.yamlz.load(contents);
|
|
||||||
} catch (ClassCastException | YAMLException var3) {
|
|
||||||
throw new InvalidConfigurationException(var3);
|
|
||||||
}
|
|
||||||
|
|
||||||
String header = this.parseHeader(contents);
|
|
||||||
if (header.length() > 0) {
|
|
||||||
this.options().header(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.contentsMap != null) {
|
|
||||||
this.convertMapsToSections(this.contentsMap, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String parseHeader(String input) {
|
|
||||||
String[] lines = input.split("\r?\n", -1);
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
boolean readingHeader = true;
|
|
||||||
boolean foundHeader = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < lines.length && readingHeader; ++i) {
|
|
||||||
String line = lines[i];
|
|
||||||
if (line.startsWith("# ")) {
|
|
||||||
if (i > 0) {
|
|
||||||
result.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line.length() > "# ".length()) {
|
|
||||||
result.append(line.substring("# ".length()));
|
|
||||||
}
|
|
||||||
|
|
||||||
foundHeader = true;
|
|
||||||
} else if (foundHeader && line.length() == 0) {
|
|
||||||
result.append("\n");
|
|
||||||
} else if (foundHeader) {
|
|
||||||
readingHeader = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void convertMapsToSections(Map<?, ?> input, ConfigurationSection section) {
|
|
||||||
Iterator var4 = input.entrySet().iterator();
|
|
||||||
|
|
||||||
while (var4.hasNext()) {
|
|
||||||
Map.Entry<?, ?> entry = (Map.Entry) var4.next();
|
|
||||||
String key = entry.getKey().toString();
|
|
||||||
Object value = entry.getValue();
|
|
||||||
if (value instanceof Map) {
|
|
||||||
this.convertMapsToSections((Map) value, section.createSection(key));
|
|
||||||
} else {
|
|
||||||
section.set(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save(File file) throws IOException {
|
|
||||||
Validate.notNull(file, FILE_NOT_BE_NULL);
|
|
||||||
Files.createParentDirs(file);
|
|
||||||
if (!file.exists()) {
|
|
||||||
file.createNewFile();
|
|
||||||
Log.i(CREATE_NEW_CONFIG, new Object[]{file.toPath()});
|
|
||||||
}
|
|
||||||
|
|
||||||
Writer writer = new OutputStreamWriter(new FileOutputStream(file), UTF_8);
|
|
||||||
Throwable var3 = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
writer.write(this.data);
|
|
||||||
} catch (Throwable var12) {
|
|
||||||
var3 = var12;
|
|
||||||
throw var12;
|
|
||||||
} finally {
|
|
||||||
if (writer != null) {
|
|
||||||
if (var3 != null) {
|
|
||||||
try {
|
|
||||||
writer.close();
|
|
||||||
} catch (Throwable var11) {
|
|
||||||
var3.addSuppressed(var11);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String saveToString() {
|
|
||||||
this.yamlOptions.setIndent(this.options().indent());
|
|
||||||
this.yamlOptions.setDefaultFlowStyle(FlowStyle.BLOCK);
|
|
||||||
this.yamlRepresenter.setDefaultFlowStyle(FlowStyle.BLOCK);
|
|
||||||
String header = this.buildHeader();
|
|
||||||
String dump = this.yamlz.dump(this.getValues(false));
|
|
||||||
if (dump.equals("{}\n")) {
|
|
||||||
dump = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data = header + dump;
|
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,3 +48,24 @@ help:
|
|||||||
click:
|
click:
|
||||||
type: 'COMMAND'
|
type: 'COMMAND'
|
||||||
command: '管理员@%player_name% 我需要你的帮助!'
|
command: '管理员@%player_name% 我需要你的帮助!'
|
||||||
|
l:
|
||||||
|
text: '&6[&b点击看我仓库]&6'
|
||||||
|
# item:
|
||||||
|
# # 物品枚举
|
||||||
|
# type: STONE
|
||||||
|
# # 物品子ID
|
||||||
|
# damage: 0
|
||||||
|
# # 物品名称(用于萌芽/龙核匹配) 为空则使用 text
|
||||||
|
# name: '§g§s§p'
|
||||||
|
tip:
|
||||||
|
- '&a点击查看 &b%player_name% &a的仓库'
|
||||||
|
click:
|
||||||
|
type: 'COMMAND'
|
||||||
|
command: '/mgs look %player_name% local 1'
|
||||||
|
t:
|
||||||
|
text: '&6[&b点击和我交易&6]&r'
|
||||||
|
tip:
|
||||||
|
- '&a点击和 &b%player_name% &a发起交易'
|
||||||
|
click:
|
||||||
|
type: 'COMMAND'
|
||||||
|
command: '/trade %player_name%'
|
||||||
@@ -26,6 +26,9 @@ permissions:
|
|||||||
${project.artifactId}.color:
|
${project.artifactId}.color:
|
||||||
description: 彩字聊天权限!
|
description: 彩字聊天权限!
|
||||||
default: op
|
default: op
|
||||||
|
${project.artifactId}.rgb:
|
||||||
|
description: RGB聊天权限!
|
||||||
|
default: op
|
||||||
${project.artifactId}.admin:
|
${project.artifactId}.admin:
|
||||||
description: 管理员格式权限!
|
description: 管理员格式权限!
|
||||||
default: op
|
default: op
|
||||||
|
|||||||
Reference in New Issue
Block a user