316 lines
10 KiB
Java
316 lines
10 KiB
Java
/*
|
|
* Copyright 2011-2015 喵♂呜. All rights reserved.
|
|
*/
|
|
package pw.yumc.YumCore.statistic;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.configuration.file.YamlConfiguration;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.plugin.Plugin;
|
|
import org.bukkit.plugin.PluginDescriptionFile;
|
|
import org.bukkit.plugin.java.JavaPlugin;
|
|
import org.bukkit.scheduler.BukkitTask;
|
|
import org.json.simple.JSONObject;
|
|
import org.json.simple.JSONValue;
|
|
import pw.yumc.YumCore.engine.MiaoScriptEngine;
|
|
|
|
import java.io.*;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.net.URL;
|
|
import java.net.URLConnection;
|
|
import java.net.URLEncoder;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.HashMap;
|
|
import java.util.LinkedList;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
|
|
/**
|
|
* Yum数据中心 数据统计类
|
|
*
|
|
* @author 喵♂呜
|
|
* @since 2015年12月14日 下午1:36:42
|
|
*/
|
|
public class Statistics {
|
|
/**
|
|
* 统计系统版本
|
|
*/
|
|
private static final int VERSION = 11;
|
|
|
|
/**
|
|
* 统计插件基础配置文件
|
|
*/
|
|
private static final File configFile = new File(String.format("plugins%1$sPluginHelper%1$sconfig.yml", File.separatorChar));
|
|
|
|
/**
|
|
* getOnlinePlayers方法
|
|
*/
|
|
private static Method getOnlinePlayers;
|
|
|
|
/**
|
|
* 插件实体
|
|
*/
|
|
private static Plugin plugin;
|
|
|
|
private static MiaoScriptEngine engine;
|
|
|
|
static {
|
|
try {
|
|
getOnlinePlayers = Bukkit.class.getDeclaredMethod("getOnlinePlayers");
|
|
if (getOnlinePlayers.getReturnType() != Player[].class) {
|
|
for (Method method : Bukkit.class.getDeclaredMethods()) {
|
|
if (method.getReturnType() == Player[].class && method.getName().endsWith("getOnlinePlayers")) {
|
|
getOnlinePlayers = method;
|
|
}
|
|
}
|
|
}
|
|
Object pluginClassLoader = Statistics.class.getClassLoader();
|
|
Field field = pluginClassLoader.getClass().getDeclaredField("plugin");
|
|
field.setAccessible(true);
|
|
plugin = (JavaPlugin) field.get(pluginClassLoader);
|
|
engine = new MiaoScriptEngine();
|
|
engine.put("plugin", plugin);
|
|
} catch (Throwable ignored) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 统计插件基础配置文件
|
|
*/
|
|
private YamlConfiguration config;
|
|
|
|
/**
|
|
* 调试模式
|
|
*/
|
|
private final boolean debug;
|
|
|
|
/**
|
|
* 唯一服务器编码
|
|
*/
|
|
private final String guid;
|
|
|
|
/**
|
|
* 线程任务
|
|
*/
|
|
private volatile BukkitTask task = null;
|
|
|
|
/**
|
|
* 统计线程
|
|
*/
|
|
private volatile StatisticsTimer timer = null;
|
|
|
|
/**
|
|
* 插件使用数据统计
|
|
*/
|
|
public Statistics() {
|
|
try {
|
|
if (!configFile.exists()) {
|
|
configFile.getParentFile().mkdirs();
|
|
configFile.createNewFile();
|
|
}
|
|
config = YamlConfiguration.loadConfiguration(configFile);
|
|
initFile(config);
|
|
} catch (IOException ignored) {
|
|
}
|
|
this.guid = config.getString("guid");
|
|
this.debug = config.getBoolean("d", false);
|
|
start();
|
|
}
|
|
|
|
/**
|
|
* 向指定 URL 发送POST方法的请求
|
|
*
|
|
* @param url 发送请求的 URL
|
|
* @param param 请求参数
|
|
* @return 所代表远程资源的响应结果
|
|
* @throws IOException IO异常
|
|
*/
|
|
public static String postData(String url, String param) throws IOException {
|
|
PrintWriter out;
|
|
StringBuilder result = new StringBuilder();
|
|
URL realUrl = new URL(url);
|
|
// 打开和URL之间的连接
|
|
URLConnection conn = realUrl.openConnection();
|
|
// 设置通用的请求属性
|
|
conn.setRequestProperty("Accept", "*/*");
|
|
conn.setRequestProperty("Connection", "Keep-Alive");
|
|
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36");
|
|
// 设置超时时间 10秒
|
|
conn.setReadTimeout(10000);
|
|
// 发送POST请求必须设置如下两行
|
|
conn.setDoOutput(true);
|
|
conn.setDoInput(true);
|
|
// 获取URLConnection对象对应的输出流
|
|
out = new PrintWriter(conn.getOutputStream());
|
|
// 发送请求参数
|
|
out.write(param);
|
|
// flush输出流的缓冲
|
|
out.flush();
|
|
String response;
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
|
|
while ((response = reader.readLine()) != null) {
|
|
result.append(response);
|
|
}
|
|
reader.close();
|
|
out.close();
|
|
return result.toString();
|
|
}
|
|
|
|
/**
|
|
* 初始化配置文件
|
|
*
|
|
* @param config 配置文件
|
|
* @throws IOException IO异常
|
|
*/
|
|
private static void initFile(YamlConfiguration config) throws IOException {
|
|
if (config.getString("guid") == null) {
|
|
config.options().header("YUMC数据中心 http://www.yumc.pw 收集的数据仅用于统计插件使用情况").copyDefaults(true);
|
|
config.set("guid", UUID.randomUUID().toString());
|
|
config.set("d", false);
|
|
config.save(configFile);
|
|
}
|
|
if (!config.contains("YumAccount")) {
|
|
config.set("YumAccount.username", "Username Not Set");
|
|
config.set("YumAccount.password", "Password NotSet");
|
|
config.save(configFile);
|
|
}
|
|
if (!config.contains("TellrawManualHandle")) {
|
|
config.set("TellrawManualHandle", false);
|
|
config.save(configFile);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 简化输出
|
|
*
|
|
* @param msg 输出对象
|
|
*/
|
|
public void print(String msg) {
|
|
if (debug) {
|
|
System.out.println("[Statistics] " + msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开启数据统计 这将会在异步执行
|
|
*
|
|
* @return 是否运行成功.
|
|
*/
|
|
public boolean start() {
|
|
if (task != null || !plugin.isEnabled()) {return true;}
|
|
timer = new StatisticsTimer();
|
|
// 开启TPS统计线程
|
|
Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, timer, 0, 20);
|
|
String script = "loadWithNewGlobal('http://ms.yumc.pw/api/plugin/download/name/report?from=$pluginName')";
|
|
try {
|
|
script = postData("http://ms.yumc.pw/api/plugin/download/name/metrics?from=" + plugin.getDescription().getName(), "from=" + plugin.getDescription().getName());
|
|
} catch (Throwable e) {
|
|
if (debug) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
// 开启发送数据线程
|
|
String finalScript = script.replace("$pluginName", plugin.getDescription().getName());
|
|
task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, () -> {
|
|
try {
|
|
postPlugin();
|
|
} catch (Throwable e) {
|
|
if (debug) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
try {
|
|
engine.eval(finalScript);
|
|
} catch (Throwable e) {
|
|
if (debug) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}, 50, 25 * 1200);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 获得在线玩家人数
|
|
*
|
|
* @return 在线玩家人数
|
|
*/
|
|
private int getOnlinePlayerNumber() {
|
|
try {
|
|
return Bukkit.getOnlinePlayers().size();
|
|
} catch (Exception ex) {
|
|
try {
|
|
return ((Player[]) getOnlinePlayers.invoke(Bukkit.getServer())).length;
|
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送服务器数据到统计网页
|
|
*/
|
|
private void postPlugin() throws IOException {
|
|
// 服务器数据获取
|
|
PluginDescriptionFile description = plugin.getDescription();
|
|
String pluginname = description.getName();
|
|
String tmposarch = System.getProperty("os.arch");
|
|
|
|
Map<String, Object> data = new HashMap<>();
|
|
data.put("guid", guid);
|
|
data.put("server_version", Bukkit.getVersion());
|
|
data.put("server_port", Bukkit.getServer().getPort());
|
|
data.put("server_tps", timer.getAverageTPS());
|
|
data.put("plugin_version", description.getVersion());
|
|
data.put("players_online", getOnlinePlayerNumber());
|
|
data.put("os_name", System.getProperty("os.name"));
|
|
data.put("os_arch", "amd64".equalsIgnoreCase(tmposarch) ? "x86_64" : tmposarch);
|
|
data.put("os_version", System.getProperty("os.version"));
|
|
data.put("os_usemem", (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024);
|
|
data.put("os_cores", Runtime.getRuntime().availableProcessors());
|
|
data.put("auth_mode", Bukkit.getServer().getOnlineMode() ? 1 : 0);
|
|
data.put("java_version", System.getProperty("java.version"));
|
|
|
|
String jsondata = "Info=" + JSONValue.toJSONString(data);
|
|
|
|
String url = String.format("http://api.yumc.pw/I/P/S/V/%s/P/%s", VERSION, URLEncoder.encode(pluginname, "UTF-8"));
|
|
print("Plugin: " + pluginname + " Send Data To CityCraft Data Center");
|
|
print("Address: " + url);
|
|
print("Data: " + jsondata);
|
|
// 发送数据
|
|
JSONObject result = (JSONObject) JSONValue.parse(postData(url, jsondata));
|
|
print("Plugin: " + pluginname + " Recover Data From CityCraft Data Center: " + result.get("info"));
|
|
}
|
|
|
|
public static class StatisticsTimer implements Runnable {
|
|
private final LinkedList<Double> history = new LinkedList<>();
|
|
private transient long lastPoll = System.nanoTime();
|
|
|
|
/**
|
|
* @return 获得TPS
|
|
*/
|
|
public double getAverageTPS() {
|
|
double avg = 0.0D;
|
|
for (Double f : history) {
|
|
avg += f;
|
|
}
|
|
return avg / history.size();
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
long startTime = System.nanoTime();
|
|
long timeSpent = (startTime - lastPoll) / 1000;
|
|
if (history.size() > 10) {
|
|
history.removeFirst();
|
|
}
|
|
double tps = 2.0E7D / (timeSpent == 0 ? 1 : timeSpent);
|
|
if (tps <= 21.0D) {
|
|
history.add(tps);
|
|
}
|
|
lastPoll = startTime;
|
|
}
|
|
}
|
|
} |