Rewrote TPSCounter Task

This commit is a squash of 8 optimization commits to TPSCounter.

1. Extracted duplicate code in TPSCounters to ServerSensors.

- TPSCounter tasks now live inside common module
  - ServerTPSCounter and ProxyTPSCounter
- Gathering methods are implemented with ServerSensor interface:
  Player count, TPS, Entity count, Chunk count
- ServerProperties#getOnlinePlayers was replaced with ServerSensor
- Fixed sonar smells: "Hiding field" in TPSStoreTransaction & NavLink

2. Optimizations down to 0.15ms / run

- Optimized entity+chunk count (same for loop)
- Added warm-up for system resource methods
- Removed Stream API usages
- Removed List copy operation
- Entities & chunks only count once per minute
- CPU & RAM averages now produced with Average class
- Maximum player count per minute now produced with Maximum class

Affects issues:
- Fixed #1289
This commit is contained in:
Rsl1122 2020-01-13 22:40:04 +02:00
parent 8a059ced0b
commit c87f981d6a
46 changed files with 1034 additions and 804 deletions

View File

@ -54,6 +54,8 @@ allprojects {
}
}
println "Building artifact for version $fullVersion"
subprojects {
// Build plugins
apply plugin: "java"

View File

@ -20,22 +20,21 @@ import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.ShutdownHook;
import com.djrapitops.plan.gathering.timed.BukkitPingCounter;
import com.djrapitops.plan.gathering.timed.BukkitTPSCounter;
import com.djrapitops.plan.gathering.timed.PaperTPSCounter;
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.upkeep.ConfigStoreTask;
import com.djrapitops.plan.storage.upkeep.DBCleanTask;
import com.djrapitops.plan.storage.upkeep.LogsFolderCleanTask;
import com.djrapitops.plugin.api.Check;
import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import org.bukkit.Bukkit;
import org.bukkit.World;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
@ -55,7 +54,7 @@ public class BukkitTaskSystem extends TaskSystem {
private final ConfigStoreTask configStoreTask;
private final DBCleanTask dbCleanTask;
private final ExtensionServerMethodCallerTask extensionServerMethodCallerTask;
private BukkitTPSCounter tpsCounter;
private TPSCounter tpsCounter;
@Inject
public BukkitTaskSystem(
@ -64,8 +63,7 @@ public class BukkitTaskSystem extends TaskSystem {
ShutdownHook shutdownHook,
RunnableFactory runnableFactory,
PaperTPSCounter paperTPSCountTimer,
BukkitTPSCounter bukkitTPSCountTimer,
ServerTPSCounter<World> tpsCounter,
BukkitPingCounter pingCounter,
ExtensionServerMethodCallerTask extensionServerMethodCallerTask,
@ -80,7 +78,7 @@ public class BukkitTaskSystem extends TaskSystem {
this.shutdownHook = shutdownHook;
this.jsonCacheCleanTask = jsonCacheCleanTask;
this.tpsCounter = Check.isPaperAvailable() ? paperTPSCountTimer : bukkitTPSCountTimer;
this.tpsCounter = tpsCounter;
this.pingCounter = pingCounter;
this.extensionServerMethodCallerTask = extensionServerMethodCallerTask;
@ -139,6 +137,6 @@ public class BukkitTaskSystem extends TaskSystem {
@Override
public void disable() {
super.disable();
Optional.ofNullable(Bukkit.getScheduler()).ifPresent(scheduler -> scheduler.cancelTasks(plugin));
Bukkit.getScheduler().cancelTasks(plugin);
}
}

View File

@ -0,0 +1,99 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering;
import com.djrapitops.plan.Plan;
import com.djrapitops.plugin.api.Check;
import org.bukkit.World;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class BukkitSensor implements ServerSensor<World> {
private final Plan plugin;
private final boolean hasPaper;
@Inject
public BukkitSensor(
Plan plugin
) {
this.plugin = plugin;
hasPaper = Check.isPaperAvailable();
}
@Override
public boolean supportsDirectTPS() {
return hasPaper;
}
@Override
public double getTPS() {
return plugin.getServer().getTPS()[0];
}
@Override
public int getChunkCount(World world) {
if (hasPaper) {
try {
return getChunkCountPaperWay(world);
} catch (BootstrapMethodError | NoSuchMethodError e) {
// Use spigot method
}
}
return getChunkCountSpigotWay(world);
}
private int getChunkCountSpigotWay(World world) {
return world.getLoadedChunks().length;
}
private int getChunkCountPaperWay(World world) {
return world.getChunkCount();
}
@Override
public int getEntityCount(World world) {
if (hasPaper) {
try {
return getEntitiesPaperWay(world);
} catch (BootstrapMethodError | NoSuchMethodError e) {
// Use spigot method
}
}
return getEntitiesSpigotWay(world);
}
private int getEntitiesSpigotWay(World world) {
return world.getEntities().size();
}
private int getEntitiesPaperWay(World world) {
return world.getEntityCount();
}
@Override
public int getOnlinePlayerCount() {
return plugin.getServer().getOnlinePlayers().size();
}
@Override
public Iterable<World> getWorlds() {
return plugin.getServer().getWorlds();
}
}

View File

@ -1,154 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import org.bukkit.World;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
@Singleton
public class BukkitTPSCounter extends TPSCounter {
protected final Plan plugin;
private ServerProperties serverProperties;
private long lastCheckNano;
@Inject
public BukkitTPSCounter(
Plan plugin,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(dbSystem, serverInfo, logger, errorHandler);
this.plugin = plugin;
this.serverProperties = serverProperties;
lastCheckNano = -1;
}
@Override
public void addNewTPSEntry(long nanoTime, long now) {
long diff = nanoTime - lastCheckNano;
lastCheckNano = nanoTime;
if (diff > nanoTime) { // First run's diff = nanoTime + 1, no calc possible.
logger.debug("First run of TPSCountTimer Task.");
return;
}
history.add(calculateTPS(diff, now));
}
/**
* Calculates the TPS
*
* @param diff The time difference between the last run and the new run
* @param now The time right now
* @return the TPS
*/
private TPS calculateTPS(long diff, long now) {
double averageCPUUsage = getCPUUsage();
long usedMemory = SystemUsage.getUsedMemory();
long freeDiskSpace = getFreeDiskSpace();
int playersOnline = serverProperties.getOnlinePlayers();
latestPlayersOnline = playersOnline;
int loadedChunks = getLoadedChunks();
int entityCount = getEntityCount();
return getTPS(diff, now, averageCPUUsage, usedMemory, entityCount, loadedChunks, playersOnline, freeDiskSpace);
}
protected TPS getTPS(long diff, long now,
double cpuUsage, long usedMemory,
int entityCount, int chunksLoaded,
int playersOnline, long freeDiskSpace) {
long difference = diff;
if (difference < TimeUnit.SECONDS.toNanos(1L)) { // No tick count above 20
difference = TimeUnit.SECONDS.toNanos(1L);
}
long twentySeconds = TimeUnit.SECONDS.toNanos(20L);
while (difference > twentySeconds) {
// Add 0 TPS since more than 20 ticks has passed.
history.add(TPSBuilder.get()
.date(now)
.tps(0)
.playersOnline(playersOnline)
.usedCPU(cpuUsage)
.usedMemory(usedMemory)
.entities(entityCount)
.chunksLoaded(chunksLoaded)
.freeDiskSpace(freeDiskSpace)
.toTPS());
difference -= twentySeconds;
}
double tpsN = twentySeconds * 1.0 / difference;
return TPSBuilder.get()
.date(now)
.tps(tpsN)
.playersOnline(playersOnline)
.usedCPU(cpuUsage)
.usedMemory(usedMemory)
.entities(entityCount)
.chunksLoaded(chunksLoaded)
.freeDiskSpace(freeDiskSpace)
.toTPS();
}
/**
* Gets the amount of loaded chunks
*
* @return amount of loaded chunks
*/
private int getLoadedChunks() {
int sum = 0;
for (World world : plugin.getServer().getWorlds()) {
sum += world.getLoadedChunks().length;
}
return sum;
}
/**
* Gets the amount of entities on the server for Bukkit / Spigot
*
* @return amount of entities
*/
protected int getEntityCount() {
int sum = 0;
for (World world : plugin.getServer().getWorlds()) {
sum += world.getEntities().size();
}
return sum;
}
}

View File

@ -1,77 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.Plan;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import org.bukkit.World;
import javax.inject.Inject;
public class PaperTPSCounter extends BukkitTPSCounter {
@Inject
public PaperTPSCounter(
Plan plugin,
DBSystem dbSystem,
ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(plugin, dbSystem, serverInfo, serverInfo.getServerProperties(), logger, errorHandler);
}
@Override
protected TPS getTPS(long diff, long now, double cpuUsage, long usedMemory, int entityCount, int chunksLoaded, int playersOnline, long freeDiskSpace) {
double tps;
try {
tps = plugin.getServer().getTPS()[0];
} catch (NoSuchMethodError e) {
// This method is from Paper
return super.getTPS(diff, now, cpuUsage, usedMemory, entityCount, chunksLoaded, playersOnline, freeDiskSpace);
}
if (tps > 20) {
tps = 20;
}
return TPSBuilder.get()
.date(now)
.tps(tps)
.playersOnline(playersOnline)
.usedCPU(cpuUsage)
.usedMemory(usedMemory)
.entities(entityCount)
.chunksLoaded(chunksLoaded)
.freeDiskSpace(freeDiskSpace)
.toTPS();
}
@Override
protected int getEntityCount() {
try {
return plugin.getServer().getWorlds().stream().mapToInt(World::getEntityCount).sum();
} catch (BootstrapMethodError | NoSuchMethodError e) {
return super.getEntityCount();
}
}
}

View File

@ -32,8 +32,7 @@ public class BukkitServerProperties extends ServerProperties {
server.getVersion(),
server.getBukkitVersion(),
server::getIp,
server.getMaxPlayers(),
() -> server.getOnlinePlayers().size()
server.getMaxPlayers()
);
}

View File

@ -19,6 +19,8 @@ package com.djrapitops.plan.modules.bukkit;
import com.djrapitops.plan.BukkitServerShutdownSave;
import com.djrapitops.plan.BukkitTaskSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.BukkitSensor;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.ServerShutdownSave;
import com.djrapitops.plan.gathering.importing.BukkitImportSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
@ -32,9 +34,10 @@ import com.djrapitops.plan.storage.database.BukkitDBSystem;
import com.djrapitops.plan.storage.database.DBSystem;
import dagger.Binds;
import dagger.Module;
import org.bukkit.World;
/**
* Module for binding Bukkit specific classes to the interface implementations.
* Module for binding Bukkit specific classes as interface implementations.
*
* @author Rsl1122
*/
@ -42,24 +45,29 @@ import dagger.Module;
public interface BukkitSuperClassBindingModule {
@Binds
ServerInfo bindBukkitServerInfo(ServerServerInfo serverServerInfo);
ServerInfo bindServerInfo(ServerServerInfo serverInfo);
@Binds
DBSystem bindBukkitDatabaseSystem(BukkitDBSystem dbSystem);
DBSystem bindDBSystem(BukkitDBSystem dbSystem);
@Binds
ConfigSystem bindBukkitConfigSystem(BukkitConfigSystem bukkitConfigSystem);
ConfigSystem bindConfigSystem(BukkitConfigSystem configSystem);
@Binds
TaskSystem bindBukkitTaskSystem(BukkitTaskSystem bukkitTaskSystem);
TaskSystem bindTaskSystem(BukkitTaskSystem taskSystem);
@Binds
ListenerSystem bindBukkitListenerSystem(BukkitListenerSystem bukkitListenerSystem);
ListenerSystem bindListenerSystem(BukkitListenerSystem listenerSystem);
@Binds
ImportSystem bindImportSystem(BukkitImportSystem bukkitImportSystem);
ImportSystem bindImportSystem(BukkitImportSystem importSystem);
@Binds
ServerShutdownSave bindBukkitServerShutdownSave(BukkitServerShutdownSave bukkitServerShutdownSave);
ServerShutdownSave bindServerShutdownSave(BukkitServerShutdownSave shutdownSave);
@Binds
ServerSensor<World> bindServerSensor(BukkitSensor sensor);
@Binds
ServerSensor<?> bindGenericsServerSensor(ServerSensor<World> sensor);
}

View File

@ -19,7 +19,8 @@ package com.djrapitops.plan;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.timed.BungeePingCounter;
import com.djrapitops.plan.gathering.timed.BungeeTPSCounter;
import com.djrapitops.plan.gathering.timed.ProxyTPSCounter;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
@ -43,7 +44,7 @@ public class BungeeTaskSystem extends TaskSystem {
private final PlanBungee plugin;
private final PlanConfig config;
private final BungeeTPSCounter tpsCounter;
private final TPSCounter tpsCounter;
private final BungeePingCounter pingCounter;
private final LogsFolderCleanTask logsFolderCleanTask;
private final NetworkConfigStoreTask networkConfigStoreTask;
@ -56,7 +57,7 @@ public class BungeeTaskSystem extends TaskSystem {
PlanBungee plugin,
PlanConfig config,
RunnableFactory runnableFactory,
BungeeTPSCounter tpsCounter,
ProxyTPSCounter tpsCounter,
BungeePingCounter pingCounter,
LogsFolderCleanTask logsFolderCleanTask,
NetworkConfigStoreTask networkConfigStoreTask,

View File

@ -0,0 +1,46 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering;
import com.djrapitops.plan.PlanBungee;
import com.djrapitops.plan.identification.properties.RedisCheck;
import com.djrapitops.plan.identification.properties.RedisPlayersOnlineSupplier;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.function.IntSupplier;
@Singleton
public class BungeeSensor implements ServerSensor<Object> {
private final IntSupplier onlinePlayerCountSupplier;
@Inject
public BungeeSensor(PlanBungee plugin) {
onlinePlayerCountSupplier = RedisCheck.isClassAvailable() ? new RedisPlayersOnlineSupplier() : plugin.getProxy()::getOnlineCount;
}
@Override
public boolean supportsDirectTPS() {
return false;
}
@Override
public int getOnlinePlayerCount() {
return onlinePlayerCountSupplier.getAsInt();
}
}

View File

@ -1,64 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class BungeeTPSCounter extends TPSCounter {
private final ServerProperties serverProperties;
@Inject
public BungeeTPSCounter(
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(dbSystem, serverInfo, logger, errorHandler);
this.serverProperties = serverProperties;
}
@Override
public void addNewTPSEntry(long nanoTime, long now) {
int onlineCount = serverProperties.getOnlinePlayers();
TPS tps = TPSBuilder.get()
.date(now)
.playersOnline(onlineCount)
.usedCPU(getCPUUsage())
.usedMemory(SystemUsage.getUsedMemory())
.entities(-1)
.chunksLoaded(-1)
.freeDiskSpace(getFreeDiskSpace())
.toTPS();
history.add(tps);
latestPlayersOnline = onlineCount;
}
}

View File

@ -36,8 +36,7 @@ public class BungeeServerProperties extends ServerProperties {
server.getVersion(),
server.getVersion(),
() -> config.get(ProxySettings.IP),
server.getConfig().getPlayerLimit(),
RedisCheck.isClassAvailable() ? new RedisPlayersOnlineSupplier() : server::getOnlineCount
server.getConfig().getPlayerLimit()
);
}
}

View File

@ -18,6 +18,8 @@ package com.djrapitops.plan.modules.bungee;
import com.djrapitops.plan.BungeeTaskSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.BungeeSensor;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.listeners.BungeeListenerSystem;
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
import com.djrapitops.plan.identification.BungeeServerInfo;
@ -26,7 +28,7 @@ import dagger.Binds;
import dagger.Module;
/**
* Module for binding Bungee specific classes to the interface implementations.
* Module for binding Bungee specific classes as interface implementations.
*
* @author Rsl1122
*/
@ -34,11 +36,14 @@ import dagger.Module;
public interface BungeeSuperClassBindingModule {
@Binds
ServerInfo bindBungeeServerInfo(BungeeServerInfo bungeeServerInfo);
ServerInfo bindServerInfo(BungeeServerInfo serverInfo);
@Binds
TaskSystem bindBungeeTaskSystem(BungeeTaskSystem bungeeTaskSystem);
TaskSystem bindTaskSystem(BungeeTaskSystem taskSystem);
@Binds
ListenerSystem bindBungeeListenerSystem(BungeeListenerSystem bungeeListenerSystem);
ListenerSystem bindListenerSystem(BungeeListenerSystem listenerSystem);
@Binds
ServerSensor<Object> bindServerSensor(BungeeSensor sensor);
}

View File

@ -40,6 +40,8 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.version.VersionCheckSystem;
import com.djrapitops.plugin.benchmarking.Benchmark;
import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
@ -79,6 +81,7 @@ public class PlanSystem implements SubSystem {
private final QueryServiceImplementation queryService;
private final SettingsServiceImplementation settingsService;
private final PluginLogger logger;
private final Timings timings;
private final ErrorHandler errorHandler;
@Inject
@ -101,7 +104,7 @@ public class PlanSystem implements SubSystem {
QueryServiceImplementation queryService,
SettingsServiceImplementation settingsService,
PluginLogger logger,
ErrorHandler errorHandler,
Timings timings, ErrorHandler errorHandler,
PlanAPI.PlanAPIHolder apiHolder
) {
this.files = files;
@ -122,6 +125,7 @@ public class PlanSystem implements SubSystem {
this.queryService = queryService;
this.settingsService = settingsService;
this.logger = logger;
this.timings = timings;
this.errorHandler = errorHandler;
logger.log(L.INFO_COLOR,
@ -179,7 +183,13 @@ public class PlanSystem implements SubSystem {
private void enableSystems(SubSystem... systems) throws EnableException {
for (SubSystem system : systems) {
logger.debug("Enabling: " + system.getClass().getSimpleName());
timings.start("subsystem-enable");
system.enable();
timings.end("subsystem-enable")
.map(Benchmark::toDurationString)
.map(duration -> "Took " + duration)
.ifPresent(logger::debug);
}
}

View File

@ -59,20 +59,20 @@ public class NavLink {
}
public String toHtml() {
String tabID = getTabID();
String usedId = getUsedTabId();
if (collapsed) {
return "<a class=\"collapse-item nav-button\" href=\"#tab-" + tabID + "\">" +
return "<a class=\"collapse-item nav-button\" href=\"#tab-" + usedId + "\">" +
icon.toHtml() + ' ' +
tabName + "</a>";
}
return "<li class=\"nav-item nav-button\">" +
"<a class=\"nav-link\" href=\"#tab-" + tabID + "\">" +
"<a class=\"nav-link\" href=\"#tab-" + usedId + "\">" +
icon.toHtml() +
"<span>" + tabName + "</span></a>" +
"</li>";
}
private String getTabID() {
private String getUsedTabId() {
return format(tabID != null ? tabID : tabName);
}
}

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.domain.mutators.TPSMutator;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.PlanConfig;
@ -58,6 +59,7 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
private final Locale locale;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final ServerSensor<?> serverSensor;
private final Formatter<Long> timeAmount;
private final Formatter<Double> decimals;
@ -70,12 +72,14 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
Locale locale,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerSensor<?> serverSensor,
Formatters formatters
) {
this.config = config;
this.locale = locale;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.serverSensor = serverSensor;
year = formatters.year();
day = formatters.dayLong();
@ -149,7 +153,7 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
private Object getOnlinePlayers(UUID serverUUID, Database db) {
return serverUUID.equals(serverInfo.getServerUUID())
? serverInfo.getServerProperties().getOnlinePlayers()
? serverSensor.getOnlinePlayerCount()
: db.query(TPSQueries.fetchLatestTPSEntryForServer(serverUUID))
.map(TPS::getPlayers).map(Object::toString)
.orElse(locale.get(GenericLang.UNKNOWN).toString());

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.Trend;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
@ -51,6 +52,7 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final ServerSensor<?> serverSensor;
private final Formatter<Long> timeAmount;
private final Formatter<DateHolder> year;
@ -59,11 +61,13 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerSensor<?> serverSensor,
Formatters formatters
) {
this.config = config;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.serverSensor = serverSensor;
year = formatters.year();
day = formatters.dayLong();
@ -109,7 +113,7 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
Integer userCount = db.query(PlayerCountQueries.newPlayerCount(0L, now));
numbers.put("total_players", userCount);
numbers.put("regular_players", db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(now, playtimeThreshold)));
numbers.put("online_players", getOnlinePlayers());
numbers.put("online_players", serverSensor.getOnlinePlayerCount());
UUID serverUUID = serverInfo.getServerUUID();
Optional<DateObj<Integer>> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo));
Optional<DateObj<Integer>> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID));
@ -127,10 +131,6 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
return numbers;
}
private Object getOnlinePlayers() {
return serverInfo.getServerProperties().getOnlinePlayers();
}
private Map<String, Object> createWeeksMap() {
Database db = dbSystem.getDatabase();
long now = System.currentTimeMillis();

View File

@ -0,0 +1,58 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering;
import java.util.Collections;
/**
* Allows sensing values from different server platforms.
*
* @param <W> Type of the class representing a minecraft world.
* @author Rsl1122
*/
public interface ServerSensor<W> {
/**
* Check if server platform provides TPS calculation.
*
* @return false if the server doesn't count TPS.
*/
boolean supportsDirectTPS();
int getOnlinePlayerCount();
default double getTPS() {
return -1;
}
/**
* Get the worlds running on the server platform.
*
* @return Empty collection if the platform doesn't support worlds.
*/
default Iterable<W> getWorlds() {
return Collections.emptyList();
}
default int getChunkCount(W world) {
return -1;
}
default int getEntityCount(W world) {
return -1;
}
}

View File

@ -48,7 +48,7 @@ public class SystemUsage {
* - On some OSes CPU usage information is not available, and system load average is used instead.
* - On some OSes system load average is not available.
*
* @return 0.0 to 1.0 if CPU, or system load average, or -1 if nothing is available.
* @return 0.0 to 100.0 if CPU, or system load average, or -1 if nothing is available.
*/
public static double getAverageSystemLoad() {
double averageUsage;
@ -64,7 +64,7 @@ public class SystemUsage {
if (averageUsage < 0) {
averageUsage = -1; // If unavailable, getSystemLoadAverage() returns -1
}
return averageUsage;
return averageUsage * 100.0;
}
/**

View File

@ -0,0 +1,95 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.TPSStoreTransaction;
import com.djrapitops.plan.utilities.analysis.Average;
import com.djrapitops.plan.utilities.analysis.Maximum;
import com.djrapitops.plan.utilities.analysis.TimerAverage;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
/**
* TPSCounter extension for game server platforms.
*
* @author Rsl1122
*/
@Singleton
public class ProxyTPSCounter extends TPSCounter {
private final ServerSensor<Object> serverSensor;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private Maximum.ForInteger playersOnline;
private Average cpu;
private TimerAverage ram;
@Inject
public ProxyTPSCounter(
ServerSensor<Object> serverSensor,
DBSystem dbSystem,
ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(logger, errorHandler);
this.serverSensor = serverSensor;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
playersOnline = new Maximum.ForInteger(0);
cpu = new Average();
ram = new TimerAverage();
}
@Override
public void pulse() {
long time = System.currentTimeMillis();
boolean shouldSave = ram.add(time, SystemUsage.getUsedMemory());
playersOnline.add(serverSensor.getOnlinePlayerCount());
cpu.add(SystemUsage.getAverageSystemLoad());
if (shouldSave) save(time);
}
private void save(long time) {
long timeLastMinute = time - TimeUnit.MINUTES.toMillis(1L);
int maxPlayers = playersOnline.getMaxAndReset();
double averageCPU = cpu.getAverageAndReset();
long averageRAM = (long) ram.getAverageAndReset(time);
long freeDiskSpace = getFreeDiskSpace();
dbSystem.getDatabase().executeTransaction(new TPSStoreTransaction(
serverInfo.getServerUUID(),
TPSBuilder.get()
.date(timeLastMinute)
.playersOnline(maxPlayers)
.usedCPU(averageCPU)
.usedMemory(averageRAM)
.freeDiskSpace(freeDiskSpace)
.toTPS()
));
}
}

View File

@ -0,0 +1,127 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.TPSStoreTransaction;
import com.djrapitops.plan.utilities.analysis.Average;
import com.djrapitops.plan.utilities.analysis.Maximum;
import com.djrapitops.plan.utilities.analysis.TimerAverage;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* TPSCounter extension for game server platforms.
*
* @author Rsl1122
*/
@Singleton
public class ServerTPSCounter<W> extends TPSCounter {
private final boolean noDirectTPS;
private final ServerSensor<W> serverSensor;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private TPSCalculator indirectTPS;
private TimerAverage directTPS;
private Maximum.ForInteger playersOnline;
private Average cpu;
private Average ram;
@Inject
public ServerTPSCounter(
ServerSensor<W> serverSensor,
DBSystem dbSystem,
ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(logger, errorHandler);
noDirectTPS = !serverSensor.supportsDirectTPS();
this.serverSensor = serverSensor;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
if (noDirectTPS) {
indirectTPS = new TPSCalculator();
} else {
directTPS = new TimerAverage();
}
playersOnline = new Maximum.ForInteger(0);
cpu = new Average();
ram = new Average();
}
@Override
public void pulse() {
long time = System.currentTimeMillis();
Optional<Double> result = pulseTPS(time);
playersOnline.add(serverSensor.getOnlinePlayerCount());
cpu.add(SystemUsage.getAverageSystemLoad());
ram.add(SystemUsage.getUsedMemory());
result.ifPresent(tps -> save(tps, time));
}
private void save(double averageTPS, long time) {
long timeLastMinute = time - TimeUnit.MINUTES.toMillis(1L);
int maxPlayers = playersOnline.getMaxAndReset();
double averageCPU = cpu.getAverageAndReset();
long averageRAM = (long) ram.getAverageAndReset();
int entityCount = 0;
int chunkCount = 0;
for (W world : serverSensor.getWorlds()) {
entityCount += serverSensor.getEntityCount(world);
chunkCount += serverSensor.getChunkCount(world);
}
long freeDiskSpace = getFreeDiskSpace();
dbSystem.getDatabase().executeTransaction(new TPSStoreTransaction(
serverInfo.getServerUUID(),
TPSBuilder.get()
.date(timeLastMinute)
.tps(averageTPS)
.playersOnline(maxPlayers)
.usedCPU(averageCPU)
.usedMemory(averageRAM)
.entities(entityCount)
.chunksLoaded(chunkCount)
.freeDiskSpace(freeDiskSpace)
.toTPS()
));
}
public Optional<Double> pulseTPS(long time) {
if (noDirectTPS) {
return indirectTPS.pulse(time);
} else {
if (directTPS.add(time, serverSensor.getTPS())) {
return Optional.of(directTPS.getAverageAndReset(time));
} else {
return Optional.empty();
}
}
}
}

View File

@ -0,0 +1,86 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.utilities.analysis.TimerAverage;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* Utility for calculating TPS using nano time.
* <p>
* Math:
* - Minecraft runs 20 ticks / second, 1 tick = 50ms
* - To return 20 ticks, 1 second is expected
* - If the call time is less than 1 second, the TPS is still 20, excess is not counted.
* - If the call time is more than 1 second, the TPS is below 20
* - If 2 seconds, TPS is 10
* - If 4 seconds, TPS is 5
* - If 20 seconds, TPS is 1
* - If more than 20 seconds, TPS is 0 for 20 seconds and then according to the other rules.
*
* @author Rsl1122
*/
public class TPSCalculator {
public static final long SECOND_NS = TimeUnit.SECONDS.toNanos(1L);
private long maxBeforeZeroTPS;
private long lastPulse;
private TimerAverage averager;
public TPSCalculator() {
maxBeforeZeroTPS = SECOND_NS * 20L; // 20 ticks
lastPulse = -1;
averager = new TimerAverage();
}
/**
* Pulse the TPS clock.
*
* @param time Current epoch ms
* @return Average TPS for the minute, or empty.
*/
public Optional<Double> pulse(long time) {
boolean firstRun = lastPulse < 0;
long currentPulse = System.nanoTime();
long difference = currentPulse - lastPulse;
lastPulse = currentPulse;
if (firstRun) {
return Optional.empty(); // Can not calculate on first check.
}
// Cap the TPS to a maximum by making nominator 1.
// Expecting the pulse period to be 1 second.
if (difference < SECOND_NS) difference = SECOND_NS;
// Add missed ticks, TPS has been low for a while, see the math in the class javadoc.
while (difference > maxBeforeZeroTPS) {
averager.add(time, 0.0);
difference -= maxBeforeZeroTPS;
}
double tps = maxBeforeZeroTPS * 1.0 / difference;
if (averager.add(time, tps)) {
return Optional.of(averager.getAverageAndReset(time));
}
return Optional.empty();
}
}

View File

@ -17,18 +17,11 @@
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.transactions.events.TPSStoreTransaction;
import com.djrapitops.plugin.logging.L;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.AbsRunnable;
import java.util.ArrayList;
import java.util.List;
/**
* Class responsible for calculating TPS every second.
*
@ -36,45 +29,31 @@ import java.util.List;
*/
public abstract class TPSCounter extends AbsRunnable {
protected final List<TPS> history;
protected final DBSystem dbSystem;
protected final ServerInfo serverInfo;
protected final PluginLogger logger;
protected final ErrorHandler errorHandler;
private boolean diskErrored = false;
protected int latestPlayersOnline = 0;
public TPSCounter(
DBSystem dbSystem,
ServerInfo serverInfo,
PluginLogger logger,
ErrorHandler errorHandler
) {
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.logger = logger;
this.errorHandler = errorHandler;
history = new ArrayList<>();
warmUp();
}
public void warmUp() {
SystemUsage.getAverageSystemLoad();
SystemUsage.getUsedMemory();
SystemUsage.getFreeDiskSpace();
}
@Override
public void run() {
try {
long nanoTime = System.nanoTime();
long now = System.currentTimeMillis();
addNewTPSEntry(nanoTime, now);
if (history.size() >= 60) {
dbSystem.getDatabase().executeTransaction(new TPSStoreTransaction(
serverInfo.getServerUUID(),
new ArrayList<>(history)
));
history.clear();
}
pulse();
} catch (Exception | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
logger.error("TPS Count Task Disabled due to error, reload Plan to re-enable.");
errorHandler.log(L.ERROR, this.getClass(), e);
@ -82,15 +61,7 @@ public abstract class TPSCounter extends AbsRunnable {
}
}
public abstract void addNewTPSEntry(long nanoTime, long now);
public int getLatestPlayersOnline() {
return latestPlayersOnline;
}
protected double getCPUUsage() {
return SystemUsage.getAverageSystemLoad() * 100.0;
}
public abstract void pulse();
protected long getFreeDiskSpace() {
try {

View File

@ -16,7 +16,6 @@
*/
package com.djrapitops.plan.identification.properties;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
/**
@ -34,16 +33,13 @@ public abstract class ServerProperties {
private final Supplier<String> ip;
private final int maxPlayers;
private final IntSupplier onlinePlayers;
protected ServerProperties(
String name,
int port,
String version,
String implVersion,
Supplier<String> ip,
int maxPlayers,
IntSupplier onlinePlayers
int maxPlayers
) {
this.name = name;
this.port = port;
@ -51,7 +47,6 @@ public abstract class ServerProperties {
this.implVersion = implVersion;
this.ip = ip;
this.maxPlayers = maxPlayers;
this.onlinePlayers = onlinePlayers;
}
/**
@ -83,7 +78,4 @@ public abstract class ServerProperties {
return maxPlayers;
}
public int getOnlinePlayers() {
return onlinePlayers.getAsInt();
}
}

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.modules;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.importing.EmptyImportSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
import com.djrapitops.plan.settings.ConfigSystem;
@ -34,12 +35,15 @@ import dagger.Module;
public interface ProxySuperClassBindingModule {
@Binds
DBSystem bindProxyDatabaseSystem(ProxyDBSystem proxyDBSystem);
DBSystem bindDBSystem(ProxyDBSystem dbSystem);
@Binds
ConfigSystem bindProxyConfigSystem(ProxyConfigSystem proxyConfigSystem);
ConfigSystem bindConfigSystem(ProxyConfigSystem configSystem);
@Binds
ImportSystem bindImportSystem(EmptyImportSystem emptyImportSystem);
@Binds
ServerSensor<?> bindServerSensor(ServerSensor<Object> sensor);
}

View File

@ -17,11 +17,9 @@
package com.djrapitops.plan.storage.database.transactions.events;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.storage.database.queries.DataStoreQueries;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import java.util.List;
import java.util.UUID;
/**
@ -32,38 +30,15 @@ import java.util.UUID;
public class TPSStoreTransaction extends Transaction {
private final UUID serverUUID;
private final List<TPS> tpsList;
private final TPS tps;
public TPSStoreTransaction(UUID serverUUID, List<TPS> tpsList) {
public TPSStoreTransaction(UUID serverUUID, TPS tps) {
this.serverUUID = serverUUID;
this.tpsList = tpsList;
this.tps = tps;
}
@Override
protected void performOperations() {
TPS tps = calculateTPS();
execute(DataStoreQueries.storeTPS(serverUUID, tps));
}
private TPS calculateTPS() {
long lastDate = tpsList.get(tpsList.size() - 1).getDate();
double averageTPS = tpsList.stream().mapToDouble(TPS::getTicksPerSecond).average().orElse(0);
int peakPlayersOnline = tpsList.stream().mapToInt(TPS::getPlayers).max().orElse(0);
double averageCPUUsage = tpsList.stream().mapToDouble(TPS::getCPUUsage).average().orElse(0);
long averageUsedMemory = (long) tpsList.stream().mapToLong(TPS::getUsedMemory).average().orElse(0);
int averageEntityCount = (int) tpsList.stream().mapToInt(TPS::getEntityCount).average().orElse(0);
int averageChunksLoaded = (int) tpsList.stream().mapToInt(TPS::getChunksLoaded).average().orElse(0);
long freeDiskSpace = (long) tpsList.stream().mapToLong(TPS::getFreeDiskSpace).average().orElse(0);
return TPSBuilder.get()
.date(lastDate)
.tps(averageTPS)
.playersOnline(peakPlayersOnline)
.usedCPU(averageCPUUsage)
.usedMemory(averageUsedMemory)
.entities(averageEntityCount)
.chunksLoaded(averageChunksLoaded)
.freeDiskSpace(freeDiskSpace)
.toTPS();
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.analysis;
/**
* Utility for averaging data.
*
* @author Rsl1122
*/
public class Average {
private double total;
private int count;
public Average() {
total = 0.0;
count = 0;
}
/**
* Add a new entry and check if save should be done.
*
* @param value TPS value
*/
public void add(double value) {
total += value;
count++;
}
public double getAverageAndReset() {
double average = total / count;
total = 0.0;
count = 0;
return average;
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.analysis;
/**
* Calculates maximum from given values.
*
* @author Rsl1122
*/
public interface Maximum {
class ForInteger {
private int max;
private int startingValue;
public ForInteger() {
this(Integer.MIN_VALUE);
}
public ForInteger(int startingValue) {
this.startingValue = startingValue;
this.max = startingValue;
}
public void add(int value) {
if (value > max) max = value;
}
public int getMaxAndReset() {
int result = max;
max = startingValue;
return result;
}
}
}

View File

@ -0,0 +1,68 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.utilities.analysis;
import java.util.concurrent.TimeUnit;
/**
* Utility for averaging time based data.
*
* @author Rsl1122
*/
public class TimerAverage {
private long savePeriodMs;
private long lastSaveMs;
private double total;
private int count;
public TimerAverage() {
this(TimeUnit.MINUTES.toMillis(1L));
}
public TimerAverage(long savePeriodMs) {
this.savePeriodMs = savePeriodMs;
lastSaveMs = 0;
total = 0.0;
count = 0;
}
/**
* Add a new entry and check if save should be done.
*
* @param time Current epoch ms
* @param value TPS value
* @return If a save should be performed.
*/
public boolean add(long time, double value) {
if (lastSaveMs <= 0) lastSaveMs = time;
if (value < 0.0) return false;
total += value;
count++;
return time - lastSaveMs >= savePeriodMs;
}
public double getAverageAndReset(long time) {
lastSaveMs = time;
double average = total / count;
total = 0.0;
count = 0;
return average;
}
}

View File

@ -79,7 +79,6 @@ import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.nio.file.Files;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
@ -439,7 +438,7 @@ public interface DatabaseTest {
}
@Test
default void testRemovalEverything() throws NoSuchAlgorithmException {
default void testRemovalEverything() {
saveAllData();
db().executeTransaction(new RemoveEverythingTransaction());
@ -456,7 +455,7 @@ public interface DatabaseTest {
assertTrue(db().query(WebUserQueries.fetchAllPlanWebUsers()).isEmpty());
}
default <T extends Map> void assertQueryIsEmpty(Database database, Query<T> query) {
default <T extends Map<?, ?>> void assertQueryIsEmpty(Database database, Query<T> query) {
assertTrue(database.query(query).isEmpty());
}
@ -782,7 +781,7 @@ public interface DatabaseTest {
}
@Test
default void testNewContainerForPlayer() throws NoSuchAlgorithmException {
default void testNewContainerForPlayer() {
saveAllData();
long start = System.nanoTime();
@ -837,7 +836,7 @@ public interface DatabaseTest {
}
@Test
default void playerContainerSupportsAllPlayerKeys() throws NoSuchAlgorithmException, IllegalAccessException {
default void playerContainerSupportsAllPlayerKeys() throws IllegalAccessException {
saveAllData();
PlayerContainer playerContainer = db().query(ContainerFetchQueries.fetchPlayerContainer(playerUUID));
@ -846,7 +845,7 @@ public interface DatabaseTest {
List<String> unsupported = new ArrayList<>();
List<Key> keys = FieldFetcher.getPublicStaticFields(PlayerKeys.class, Key.class);
for (Key key : keys) {
for (Key<?> key : keys) {
if (!playerContainer.supports(key)) {
unsupported.add(key.getKeyName());
}
@ -872,14 +871,14 @@ public interface DatabaseTest {
}
@Test
default void serverContainerSupportsAllServerKeys() throws NoSuchAlgorithmException, IllegalAccessException {
default void serverContainerSupportsAllServerKeys() throws IllegalAccessException {
saveAllData();
ServerContainer serverContainer = db().query(ContainerFetchQueries.fetchServerContainer(serverUUID()));
List<String> unsupported = new ArrayList<>();
List<Key> keys = FieldFetcher.getPublicStaticFields(ServerKeys.class, Key.class);
for (Key key : keys) {
for (Key<?> key : keys) {
if (!serverContainer.supports(key)) {
unsupported.add(key.getKeyName());
}
@ -963,7 +962,7 @@ public interface DatabaseTest {
List<TPS> tpsData = RandomData.randomTPS();
for (TPS tps : tpsData) {
db().executeTransaction(new TPSStoreTransaction(serverUUID(), Collections.singletonList(tps)));
db().executeTransaction(new TPSStoreTransaction(serverUUID(), tps));
}
tpsData.sort(Comparator.comparingInt(TPS::getPlayers));

View File

@ -22,7 +22,6 @@ import dagger.Provides;
import javax.inject.Singleton;
import java.net.InetSocketAddress;
import java.util.Random;
/**
* Dagger module for Bukkit ServerProperties.
@ -41,8 +40,7 @@ public class PluginServerPropertiesModule {
"1.13",
"1.13-git-mock",
() -> new InetSocketAddress(25565).getAddress().getHostAddress(),
20,
() -> new Random().nextInt(20)
20
) {};
}
}

View File

@ -19,6 +19,7 @@ package utilities.dagger;
import com.djrapitops.plan.PlanPlugin;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.exceptions.EnableException;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.settings.config.PlanConfig;
@ -28,16 +29,17 @@ import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.H2DB;
import com.djrapitops.plan.storage.database.MySQLDB;
import com.djrapitops.plan.storage.database.SQLiteDB;
import com.djrapitops.plugin.benchmarking.Timings;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import com.djrapitops.plugin.task.RunnableFactory;
import dagger.Module;
import dagger.Provides;
import org.mockito.Mockito;
import utilities.mocks.TestProcessing;
import javax.inject.Singleton;
import static org.mockito.Mockito.when;
/**
* Module for binding Bukkit specific classes to the interface implementations.
*
@ -54,9 +56,7 @@ public class PluginSuperClassBindingModule {
SQLiteDB.Factory sqLiteDB,
H2DB.Factory h2Factory,
MySQLDB mySQLDB,
PluginLogger logger,
Timings timings,
ErrorHandler errorHandler
PluginLogger logger
) {
return new DBSystem(locale, sqLiteDB, h2Factory, logger) {
@Override
@ -105,4 +105,15 @@ public class PluginSuperClassBindingModule {
return testProcessing;
}
@Provides
@Singleton
ServerSensor<?> provideServerSensor() {
ServerSensor<?> mock = Mockito.mock(ServerSensor.class);
when(mock.getWorlds()).thenCallRealMethod();
when(mock.getChunkCount(Mockito.any())).thenCallRealMethod();
when(mock.getEntityCount(Mockito.any())).thenCallRealMethod();
when(mock.getTPS()).thenCallRealMethod();
return mock;
}
}

View File

@ -17,11 +17,13 @@
package com.djrapitops.plan;
import cn.nukkit.Server;
import cn.nukkit.level.Level;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.ShutdownHook;
import com.djrapitops.plan.gathering.timed.NukkitPingCounter;
import com.djrapitops.plan.gathering.timed.NukkitTPSCounter;
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
@ -49,11 +51,11 @@ public class NukkitTaskSystem extends TaskSystem {
private final ShutdownHook shutdownHook;
private final JSONCache.CleanTask jsonCacheCleanTask;
private final LogsFolderCleanTask logsFolderCleanTask;
private final TPSCounter tpsCounter;
private final NukkitPingCounter pingCounter;
private final ConfigStoreTask configStoreTask;
private final DBCleanTask dbCleanTask;
private final ExtensionServerMethodCallerTask extensionServerMethodCallerTask;
private NukkitTPSCounter tpsCounter;
@Inject
public NukkitTaskSystem(
@ -62,7 +64,7 @@ public class NukkitTaskSystem extends TaskSystem {
ShutdownHook shutdownHook,
RunnableFactory runnableFactory,
NukkitTPSCounter tpsCounter,
ServerTPSCounter<Level> tpsCounter,
NukkitPingCounter pingCounter,
ExtensionServerMethodCallerTask extensionServerMethodCallerTask,

View File

@ -0,0 +1,66 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering;
import cn.nukkit.level.Level;
import com.djrapitops.plan.PlanNukkit;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class NukkitSensor implements ServerSensor<Level> {
private final PlanNukkit plugin;
@Inject
public NukkitSensor(
PlanNukkit plugin
) {
this.plugin = plugin;
}
@Override
public boolean supportsDirectTPS() {
return true;
}
@Override
public double getTPS() {
return plugin.getServer().getTicksPerSecondAverage();
}
@Override
public int getChunkCount(Level world) {
return world.getChunks().size();
}
@Override
public int getEntityCount(Level world) {
return world.getEntities().length;
}
@Override
public int getOnlinePlayerCount() {
return plugin.getServer().getOnlinePlayers().size();
}
@Override
public Iterable<Level> getWorlds() {
return plugin.getServer().getLevels().values();
}
}

View File

@ -1,154 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import cn.nukkit.level.Level;
import com.djrapitops.plan.PlanNukkit;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
@Singleton
public class NukkitTPSCounter extends TPSCounter {
protected final PlanNukkit plugin;
private ServerProperties serverProperties;
private long lastCheckNano;
@Inject
public NukkitTPSCounter(
PlanNukkit plugin,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(dbSystem, serverInfo, logger, errorHandler);
this.plugin = plugin;
this.serverProperties = serverProperties;
lastCheckNano = -1;
}
@Override
public void addNewTPSEntry(long nanoTime, long now) {
long diff = nanoTime - lastCheckNano;
lastCheckNano = nanoTime;
if (diff > nanoTime) { // First run's diff = nanoTime + 1, no calc possible.
logger.debug("First run of TPSCountTimer Task.");
return;
}
history.add(calculateTPS(diff, now));
}
/**
* Calculates the TPS
*
* @param diff The time difference between the last run and the new run
* @param now The time right now
* @return the TPS
*/
private TPS calculateTPS(long diff, long now) {
double averageCPUUsage = getCPUUsage();
long usedMemory = SystemUsage.getUsedMemory();
long freeDiskSpace = getFreeDiskSpace();
int playersOnline = serverProperties.getOnlinePlayers();
latestPlayersOnline = playersOnline;
int loadedChunks = getLoadedChunks();
int entityCount = getEntityCount();
return getTPS(diff, now, averageCPUUsage, usedMemory, entityCount, loadedChunks, playersOnline, freeDiskSpace);
}
protected TPS getTPS(long diff, long now,
double cpuUsage, long usedMemory,
int entityCount, int chunksLoaded,
int playersOnline, long freeDiskSpace) {
long difference = diff;
if (difference < TimeUnit.SECONDS.toNanos(1L)) { // No tick count above 20
difference = TimeUnit.SECONDS.toNanos(1L);
}
long twentySeconds = TimeUnit.SECONDS.toNanos(20L);
while (difference > twentySeconds) {
// Add 0 TPS since more than 20 ticks has passed.
history.add(TPSBuilder.get()
.date(now)
.tps(0)
.playersOnline(playersOnline)
.usedCPU(cpuUsage)
.usedMemory(usedMemory)
.entities(entityCount)
.chunksLoaded(chunksLoaded)
.freeDiskSpace(freeDiskSpace)
.toTPS());
difference -= twentySeconds;
}
double tpsN = twentySeconds * 1.0 / difference;
return TPSBuilder.get()
.date(now)
.tps(tpsN)
.playersOnline(playersOnline)
.usedCPU(cpuUsage)
.usedMemory(usedMemory)
.entities(entityCount)
.chunksLoaded(chunksLoaded)
.freeDiskSpace(freeDiskSpace)
.toTPS();
}
/**
* Gets the amount of loaded chunks
*
* @return amount of loaded chunks
*/
private int getLoadedChunks() {
int sum = 0;
for (Level world : plugin.getServer().getLevels().values()) {
sum += world.getChunks().size();
}
return sum;
}
/**
* Gets the amount of entities on the server for Nukkit
*
* @return amount of entities
*/
protected int getEntityCount() {
int sum = 0;
for (Level world : plugin.getServer().getLevels().values()) {
sum += world.getEntities().length;
}
return sum;
}
}

View File

@ -32,8 +32,7 @@ public class NukkitServerProperties extends ServerProperties {
server.getVersion(),
server.getNukkitVersion(),
server::getIp,
server.getMaxPlayers(),
() -> server.getOnlinePlayers().size()
server.getMaxPlayers()
);
}

View File

@ -16,9 +16,12 @@
*/
package com.djrapitops.plan.modules.nukkit;
import cn.nukkit.level.Level;
import com.djrapitops.plan.NukkitServerShutdownSave;
import com.djrapitops.plan.NukkitTaskSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.NukkitSensor;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.ServerShutdownSave;
import com.djrapitops.plan.gathering.importing.EmptyImportSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
@ -34,7 +37,7 @@ import dagger.Binds;
import dagger.Module;
/**
* Module for binding Nukkit specific classes to the interface implementations.
* Module for binding Nukkit specific classes as interface implementations.
*
* @author Rsl1122
*/
@ -42,24 +45,29 @@ import dagger.Module;
public interface NukkitSuperClassBindingModule {
@Binds
ServerInfo bindNukkitServerInfo(ServerServerInfo serverServerInfo);
ServerInfo bindServerInfo(ServerServerInfo serverInfo);
@Binds
DBSystem bindNukkitDatabaseSystem(NukkitDBSystem dbSystem);
DBSystem bindDBSystem(NukkitDBSystem dbSystem);
@Binds
ConfigSystem bindNukkitConfigSystem(NukkitConfigSystem nukkitConfigSystem);
ConfigSystem bindConfigSystem(NukkitConfigSystem configSystem);
@Binds
TaskSystem bindNukkitTaskSystem(NukkitTaskSystem nukkitTaskSystem);
TaskSystem bindTaskSystem(NukkitTaskSystem taskSystem);
@Binds
ListenerSystem bindNukkitListenerSystem(NukkitListenerSystem nukkitListenerSystem);
ListenerSystem bindListenerSystem(NukkitListenerSystem listenerSystem);
@Binds
ImportSystem bindImportSystem(EmptyImportSystem emptyImportSystem);
@Binds
ServerShutdownSave bindNukkitServerShutdownSave(NukkitServerShutdownSave nukkitServerShutdownSave);
ServerShutdownSave bindServerShutdownSave(NukkitServerShutdownSave shutdownSave);
@Binds
ServerSensor<Level> bindServerSensor(NukkitSensor sensor);
@Binds
ServerSensor<?> bindGenericsServerSensor(ServerSensor<Level> sensor);
}

View File

@ -19,8 +19,9 @@ package com.djrapitops.plan;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.ShutdownHook;
import com.djrapitops.plan.gathering.timed.ServerTPSCounter;
import com.djrapitops.plan.gathering.timed.SpongePingCounter;
import com.djrapitops.plan.gathering.timed.SpongeTPSCounter;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
@ -31,6 +32,7 @@ import com.djrapitops.plugin.api.TimeAmount;
import com.djrapitops.plugin.task.RunnableFactory;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.scheduler.Task;
import org.spongepowered.api.world.World;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -42,7 +44,7 @@ public class SpongeTaskSystem extends TaskSystem {
private final PlanSponge plugin;
private final PlanConfig config;
private final ShutdownHook shutdownHook;
private final SpongeTPSCounter tpsCounter;
private final TPSCounter tpsCounter;
private final JSONCache.CleanTask jsonCacheCleanTask;
private final SpongePingCounter pingCounter;
private final LogsFolderCleanTask logsFolderCleanTask;
@ -57,7 +59,7 @@ public class SpongeTaskSystem extends TaskSystem {
ShutdownHook shutdownHook,
RunnableFactory runnableFactory,
SpongeTPSCounter tpsCounter,
ServerTPSCounter<World> tpsCounter,
SpongePingCounter pingCounter,
ExtensionServerMethodCallerTask extensionServerMethodCallerTask,

View File

@ -0,0 +1,76 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering;
import com.djrapitops.plan.PlanSponge;
import org.spongepowered.api.world.Chunk;
import org.spongepowered.api.world.World;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Iterator;
@Singleton
public class SpongeSensor implements ServerSensor<World> {
private final PlanSponge plugin;
@Inject
public SpongeSensor(PlanSponge plugin) {
this.plugin = plugin;
}
@Override
public boolean supportsDirectTPS() {
return true;
}
@Override
public int getOnlinePlayerCount() {
return plugin.getGame().getServer().getOnlinePlayers().size();
}
@Override
public double getTPS() {
return plugin.getGame().getServer().getTicksPerSecond();
}
@Override
public Iterable<World> getWorlds() {
return plugin.getGame().getServer().getWorlds();
}
@Override
public int getChunkCount(World world) {
return -1;
}
private int getLaggyChunkCount(World world) {
Iterator<Chunk> chunks = world.getLoadedChunks().iterator();
int count = 0;
while (chunks.hasNext()) {
chunks.next();
count++;
}
return count;
}
@Override
public int getEntityCount(World world) {
return world.getEntities().size();
}
}

View File

@ -1,121 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.PlanSponge;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import org.spongepowered.api.world.World;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class SpongeTPSCounter extends TPSCounter {
private long lastCheckNano;
private final PlanSponge plugin;
private ServerProperties serverProperties;
@Inject
public SpongeTPSCounter(
PlanSponge plugin,
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(dbSystem, serverInfo, logger, errorHandler);
this.plugin = plugin;
this.serverProperties = serverProperties;
lastCheckNano = -1;
}
@Override
public void addNewTPSEntry(long nanoTime, long now) {
long diff = nanoTime - lastCheckNano;
lastCheckNano = nanoTime;
if (diff > nanoTime) { // First run's diff = nanoTime + 1, no calc possible.
logger.debug("First run of TPSCountTimer Task.");
return;
}
history.add(calculateTPS(now));
}
/**
* Calculates the TPS
*
* @param now The time right now
* @return the TPS
*/
private TPS calculateTPS(long now) {
double averageCPUUsage = getCPUUsage();
long usedMemory = SystemUsage.getUsedMemory();
double tps = plugin.getGame().getServer().getTicksPerSecond();
int playersOnline = serverProperties.getOnlinePlayers();
latestPlayersOnline = playersOnline;
int loadedChunks = -1; // getLoadedChunks();
int entityCount = getEntityCount();
long freeDiskSpace = getFreeDiskSpace();
return TPSBuilder.get()
.date(now)
.tps(tps)
.playersOnline(playersOnline)
.usedCPU(averageCPUUsage)
.usedMemory(usedMemory)
.entities(entityCount)
.chunksLoaded(loadedChunks)
.freeDiskSpace(freeDiskSpace)
.toTPS();
}
/**
* Gets the amount of loaded chunks
*
* @return amount of loaded chunks
*/
private int getLoadedChunks() {
// DISABLED
int loaded = 0;
for (World world : plugin.getGame().getServer().getWorlds()) {
loaded += world.getLoadedChunks().spliterator().estimateSize();
}
return loaded;
}
/**
* Gets the amount of entities on the server
*
* @return amount of entities
*/
private int getEntityCount() {
return plugin.getGame().getServer().getWorlds().stream().mapToInt(world -> world.getEntities().size()).sum();
}
}

View File

@ -36,8 +36,7 @@ public class SpongeServerProperties extends ServerProperties {
() -> game.getServer().getBoundAddress()
.orElseGet(() -> new InetSocketAddress(25565))
.getAddress().getHostAddress(),
game.getServer().getMaxPlayers(),
() -> game.getServer().getOnlinePlayers().size()
game.getServer().getMaxPlayers()
);
}
}

View File

@ -19,7 +19,9 @@ package com.djrapitops.plan.modules.sponge;
import com.djrapitops.plan.SpongeServerShutdownSave;
import com.djrapitops.plan.SpongeTaskSystem;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.ServerShutdownSave;
import com.djrapitops.plan.gathering.SpongeSensor;
import com.djrapitops.plan.gathering.importing.EmptyImportSystem;
import com.djrapitops.plan.gathering.importing.ImportSystem;
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
@ -34,9 +36,10 @@ import com.djrapitops.plan.storage.file.PlanFiles;
import com.djrapitops.plan.storage.file.SpongePlanFiles;
import dagger.Binds;
import dagger.Module;
import org.spongepowered.api.world.World;
/**
* Module for binding Sponge specific classes to the interface implementations.
* Module for binding Sponge specific classes as interface implementations.
*
* @author Rsl1122
*/
@ -44,27 +47,32 @@ import dagger.Module;
public interface SpongeSuperClassBindingModule {
@Binds
PlanFiles bindSpongePlanFiles(SpongePlanFiles files);
PlanFiles bindPlanFiles(SpongePlanFiles files);
@Binds
ServerInfo bindSpongeServerInfo(ServerServerInfo serverServerInfo);
ServerInfo bindServerInfo(ServerServerInfo serverInfo);
@Binds
DBSystem bindSpongeDatabaseSystem(SpongeDBSystem dbSystem);
DBSystem bindDBSystem(SpongeDBSystem dbSystem);
@Binds
ConfigSystem bindSpongeConfigSystem(SpongeConfigSystem spongeConfigSystem);
ConfigSystem bindConfigSystem(SpongeConfigSystem configSystem);
@Binds
TaskSystem bindSpongeTaskSystem(SpongeTaskSystem spongeTaskSystem);
TaskSystem bindTaskSystem(SpongeTaskSystem taskSystem);
@Binds
ListenerSystem bindSpongeListenerSystem(SpongeListenerSystem spongeListenerSystem);
ListenerSystem bindListenerSystem(SpongeListenerSystem listenerSystem);
@Binds
ImportSystem bindImportSystem(EmptyImportSystem emptyImportSystem);
@Binds
ServerShutdownSave bindSpongeServerShutdownSave(SpongeServerShutdownSave spongeServerShutdownSave);
ServerShutdownSave bindServerShutdownSave(SpongeServerShutdownSave shutdownSave);
@Binds
ServerSensor<World> bindServerSensor(SpongeSensor sensor);
@Binds
ServerSensor<?> bindGenericsServerSensor(ServerSensor<World> sensor);
}

View File

@ -18,8 +18,9 @@ package com.djrapitops.plan;
import com.djrapitops.plan.delivery.webserver.cache.JSONCache;
import com.djrapitops.plan.extension.ExtensionServerMethodCallerTask;
import com.djrapitops.plan.gathering.timed.ProxyTPSCounter;
import com.djrapitops.plan.gathering.timed.TPSCounter;
import com.djrapitops.plan.gathering.timed.VelocityPingCounter;
import com.djrapitops.plan.gathering.timed.VelocityTPSCounter;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
@ -43,7 +44,7 @@ public class VelocityTaskSystem extends TaskSystem {
private final PlanVelocity plugin;
private final PlanConfig config;
private final VelocityTPSCounter tpsCounter;
private final TPSCounter tpsCounter;
private final VelocityPingCounter pingCounter;
private final LogsFolderCleanTask logsFolderCleanTask;
private final NetworkConfigStoreTask networkConfigStoreTask;
@ -56,7 +57,7 @@ public class VelocityTaskSystem extends TaskSystem {
PlanVelocity plugin,
PlanConfig config,
RunnableFactory runnableFactory,
VelocityTPSCounter tpsCounter,
ProxyTPSCounter tpsCounter,
VelocityPingCounter pingCounter,
LogsFolderCleanTask logsFolderCleanTask,
NetworkConfigStoreTask networkConfigStoreTask,

View File

@ -0,0 +1,44 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering;
import com.djrapitops.plan.PlanVelocity;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.function.IntSupplier;
@Singleton
public class VelocitySensor implements ServerSensor<Object> {
private final IntSupplier onlinePlayerCountSupplier;
@Inject
public VelocitySensor(PlanVelocity plugin) {
onlinePlayerCountSupplier = plugin.getProxy()::getPlayerCount;
}
@Override
public boolean supportsDirectTPS() {
return false;
}
@Override
public int getOnlinePlayerCount() {
return onlinePlayerCountSupplier.getAsInt();
}
}

View File

@ -1,64 +0,0 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.gathering.timed;
import com.djrapitops.plan.gathering.SystemUsage;
import com.djrapitops.plan.gathering.domain.TPS;
import com.djrapitops.plan.gathering.domain.builders.TPSBuilder;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.properties.ServerProperties;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plugin.logging.console.PluginLogger;
import com.djrapitops.plugin.logging.error.ErrorHandler;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class VelocityTPSCounter extends TPSCounter {
private final ServerProperties serverProperties;
@Inject
public VelocityTPSCounter(
DBSystem dbSystem,
ServerInfo serverInfo,
ServerProperties serverProperties,
PluginLogger logger,
ErrorHandler errorHandler
) {
super(dbSystem, serverInfo, logger, errorHandler);
this.serverProperties = serverProperties;
}
@Override
public void addNewTPSEntry(long nanoTime, long now) {
int onlineCount = serverProperties.getOnlinePlayers();
TPS tps = TPSBuilder.get()
.date(now)
.playersOnline(onlineCount)
.usedCPU(getCPUUsage())
.usedMemory(SystemUsage.getUsedMemory())
.entities(-1)
.chunksLoaded(-1)
.freeDiskSpace(getFreeDiskSpace())
.toTPS();
history.add(tps);
latestPlayersOnline = onlineCount;
}
}

View File

@ -36,8 +36,7 @@ public class VelocityServerProperties extends ServerProperties {
server.getClass().getPackage().getImplementationVersion(),
server.getClass().getPackage().getImplementationVersion(),
() -> config.get(ProxySettings.IP),
-1,
server::getPlayerCount
-1
);
}
}

View File

@ -18,6 +18,8 @@ package com.djrapitops.plan.modules.velocity;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.VelocityTaskSystem;
import com.djrapitops.plan.gathering.ServerSensor;
import com.djrapitops.plan.gathering.VelocitySensor;
import com.djrapitops.plan.gathering.listeners.ListenerSystem;
import com.djrapitops.plan.gathering.listeners.VelocityListenerSystem;
import com.djrapitops.plan.identification.ServerInfo;
@ -26,7 +28,7 @@ import dagger.Binds;
import dagger.Module;
/**
* Module for binding Velocity specific classes to the interface implementations.
* Module for binding Velocity specific classes as interface implementations.
*
* @author Rsl1122
*/
@ -34,11 +36,14 @@ import dagger.Module;
public interface VelocitySuperClassBindingModule {
@Binds
ServerInfo provideVelocityServerInfo(VelocityServerInfo velocityServerInfo);
ServerInfo bindServerInfo(VelocityServerInfo serverInfo);
@Binds
TaskSystem provideVelocityTaskSystem(VelocityTaskSystem velocityTaskSystem);
TaskSystem bindTaskSystem(VelocityTaskSystem taskSystem);
@Binds
ListenerSystem provideVelocityListenerSystem(VelocityListenerSystem velocityListenerSystem);
ListenerSystem bindListenerSystem(VelocityListenerSystem listenerSystem);
@Binds
ServerSensor<Object> bindServerSensor(VelocitySensor sensor);
}