mirror of
https://github.com/PaperMC/Paper.git
synced 2024-09-27 06:03:09 +02:00
193 lines
7.1 KiB
Java
193 lines
7.1 KiB
Java
|
/*
|
||
|
* This file is licensed under the MIT License (MIT).
|
||
|
*
|
||
|
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
* THE SOFTWARE.
|
||
|
*/
|
||
|
package co.aikar.timings;
|
||
|
|
||
|
import co.aikar.util.LoadingMap;
|
||
|
import com.google.common.collect.EvictingQueue;
|
||
|
import org.bukkit.Bukkit;
|
||
|
import org.bukkit.Server;
|
||
|
import org.bukkit.command.Command;
|
||
|
import org.bukkit.plugin.Plugin;
|
||
|
import org.bukkit.plugin.java.PluginClassLoader;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.List;
|
||
|
import java.util.Map;
|
||
|
import java.util.concurrent.ConcurrentHashMap;
|
||
|
import java.util.logging.Level;
|
||
|
import org.jetbrains.annotations.NotNull;
|
||
|
import org.jetbrains.annotations.Nullable;
|
||
|
|
||
|
/**
|
||
|
* @deprecated Timings will likely be replaced with Spark in the future
|
||
|
*/
|
||
|
@Deprecated(forRemoval = true)
|
||
|
public final class TimingsManager {
|
||
|
static final Map<TimingIdentifier, TimingHandler> TIMING_MAP = LoadingMap.of(
|
||
|
new ConcurrentHashMap<>(4096, .5F), TimingHandler::new
|
||
|
);
|
||
|
public static final FullServerTickHandler FULL_SERVER_TICK = new FullServerTickHandler();
|
||
|
public static final TimingHandler TIMINGS_TICK = Timings.ofSafe("Timings Tick", FULL_SERVER_TICK);
|
||
|
public static final Timing PLUGIN_GROUP_HANDLER = Timings.ofSafe("Plugins");
|
||
|
public static String url = "https://timings.aikar.co/";
|
||
|
public static List<String> hiddenConfigs = new ArrayList<String>();
|
||
|
public static boolean privacy = false;
|
||
|
|
||
|
static final List<TimingHandler> HANDLERS = new ArrayList<>(1024);
|
||
|
static final List<TimingHistory.MinuteReport> MINUTE_REPORTS = new ArrayList<>(64);
|
||
|
|
||
|
static EvictingQueue<TimingHistory> HISTORY = EvictingQueue.create(12);
|
||
|
static long timingStart = 0;
|
||
|
static long historyStart = 0;
|
||
|
static boolean needsFullReset = false;
|
||
|
static boolean needsRecheckEnabled = false;
|
||
|
|
||
|
private TimingsManager() {}
|
||
|
|
||
|
/**
|
||
|
* Resets all timing data on the next tick
|
||
|
*/
|
||
|
static void reset() {
|
||
|
needsFullReset = true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ticked every tick by CraftBukkit to count the number of times a timer
|
||
|
* caused TPS loss.
|
||
|
*/
|
||
|
static void tick() {
|
||
|
if (Timings.timingsEnabled) {
|
||
|
boolean violated = FULL_SERVER_TICK.isViolated();
|
||
|
|
||
|
for (TimingHandler handler : HANDLERS) {
|
||
|
if (handler.isSpecial()) {
|
||
|
// We manually call this
|
||
|
continue;
|
||
|
}
|
||
|
handler.processTick(violated);
|
||
|
}
|
||
|
|
||
|
TimingHistory.playerTicks += Bukkit.getOnlinePlayers().size();
|
||
|
TimingHistory.timedTicks++;
|
||
|
// Generate TPS/Ping/Tick reports every minute
|
||
|
}
|
||
|
}
|
||
|
static void stopServer() {
|
||
|
Timings.timingsEnabled = false;
|
||
|
recheckEnabled();
|
||
|
}
|
||
|
static void recheckEnabled() {
|
||
|
synchronized (TIMING_MAP) {
|
||
|
for (TimingHandler timings : TIMING_MAP.values()) {
|
||
|
timings.checkEnabled();
|
||
|
}
|
||
|
}
|
||
|
needsRecheckEnabled = false;
|
||
|
}
|
||
|
static void resetTimings() {
|
||
|
if (needsFullReset) {
|
||
|
// Full resets need to re-check every handlers enabled state
|
||
|
// Timing map can be modified from async so we must sync on it.
|
||
|
synchronized (TIMING_MAP) {
|
||
|
for (TimingHandler timings : TIMING_MAP.values()) {
|
||
|
timings.reset(true);
|
||
|
}
|
||
|
}
|
||
|
Bukkit.getLogger().log(Level.INFO, "Timings Reset");
|
||
|
HISTORY.clear();
|
||
|
needsFullReset = false;
|
||
|
needsRecheckEnabled = false;
|
||
|
timingStart = System.currentTimeMillis();
|
||
|
} else {
|
||
|
// Soft resets only need to act on timings that have done something
|
||
|
// Handlers can only be modified on main thread.
|
||
|
for (TimingHandler timings : HANDLERS) {
|
||
|
timings.reset(false);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HANDLERS.clear();
|
||
|
MINUTE_REPORTS.clear();
|
||
|
|
||
|
TimingHistory.resetTicks(true);
|
||
|
historyStart = System.currentTimeMillis();
|
||
|
}
|
||
|
|
||
|
@NotNull
|
||
|
static TimingHandler getHandler(@Nullable String group, @NotNull String name, @Nullable Timing parent) {
|
||
|
return TIMING_MAP.get(new TimingIdentifier(group, name, parent));
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* <p>Due to access restrictions, we need a helper method to get a Command TimingHandler with String group</p>
|
||
|
*
|
||
|
* Plugins should never call this
|
||
|
*
|
||
|
* @param pluginName Plugin this command is associated with
|
||
|
* @param command Command to get timings for
|
||
|
* @return TimingHandler
|
||
|
*/
|
||
|
@NotNull
|
||
|
public static Timing getCommandTiming(@Nullable String pluginName, @NotNull Command command) {
|
||
|
Plugin plugin = null;
|
||
|
final Server server = Bukkit.getServer();
|
||
|
if (!( server == null || pluginName == null ||
|
||
|
"minecraft".equals(pluginName) || "bukkit".equals(pluginName) ||
|
||
|
"spigot".equalsIgnoreCase(pluginName) || "paper".equals(pluginName)
|
||
|
)) {
|
||
|
plugin = server.getPluginManager().getPlugin(pluginName);
|
||
|
}
|
||
|
if (plugin == null) {
|
||
|
// Plugin is passing custom fallback prefix, try to look up by class loader
|
||
|
plugin = getPluginByClassloader(command.getClass());
|
||
|
}
|
||
|
if (plugin == null) {
|
||
|
return Timings.ofSafe("Command: " + pluginName + ":" + command.getTimingName());
|
||
|
}
|
||
|
|
||
|
return Timings.ofSafe(plugin, "Command: " + pluginName + ":" + command.getTimingName());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Looks up the class loader for the specified class, and if it is a PluginClassLoader, return the
|
||
|
* Plugin that created this class.
|
||
|
*
|
||
|
* @param clazz Class to check
|
||
|
* @return Plugin if created by a plugin
|
||
|
*/
|
||
|
@Nullable
|
||
|
public static Plugin getPluginByClassloader(@Nullable Class<?> clazz) {
|
||
|
if (clazz == null) {
|
||
|
return null;
|
||
|
}
|
||
|
final ClassLoader classLoader = clazz.getClassLoader();
|
||
|
if (classLoader instanceof PluginClassLoader) {
|
||
|
PluginClassLoader pluginClassLoader = (PluginClassLoader) classLoader;
|
||
|
return pluginClassLoader.getPlugin();
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|