mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-11-10 04:41:26 +01:00
Refactored AnalysisData
This commit is contained in:
parent
25470e6f60
commit
c28207134b
@ -5,6 +5,7 @@ import main.java.com.djrapitops.plan.data.container.Session;
|
|||||||
import main.java.com.djrapitops.plan.data.container.StickyData;
|
import main.java.com.djrapitops.plan.data.container.StickyData;
|
||||||
import main.java.com.djrapitops.plan.data.container.TPS;
|
import main.java.com.djrapitops.plan.data.container.TPS;
|
||||||
import main.java.com.djrapitops.plan.data.element.AnalysisContainer;
|
import main.java.com.djrapitops.plan.data.element.AnalysisContainer;
|
||||||
|
import main.java.com.djrapitops.plan.data.element.HealthNotes;
|
||||||
import main.java.com.djrapitops.plan.data.plugin.PluginData;
|
import main.java.com.djrapitops.plan.data.plugin.PluginData;
|
||||||
import main.java.com.djrapitops.plan.data.time.WorldTimes;
|
import main.java.com.djrapitops.plan.data.time.WorldTimes;
|
||||||
import main.java.com.djrapitops.plan.settings.Settings;
|
import main.java.com.djrapitops.plan.settings.Settings;
|
||||||
@ -51,6 +52,7 @@ public class AnalysisData extends RawData {
|
|||||||
|
|
||||||
private Map<String, Long> analyzedValues;
|
private Map<String, Long> analyzedValues;
|
||||||
private Set<StickyData> stickyMonthData;
|
private Set<StickyData> stickyMonthData;
|
||||||
|
private List<PlayerProfile> players;
|
||||||
|
|
||||||
public AnalysisData() {
|
public AnalysisData() {
|
||||||
analyzedValues = new HashMap<>();
|
analyzedValues = new HashMap<>();
|
||||||
@ -104,7 +106,7 @@ public class AnalysisData extends RawData {
|
|||||||
List<Session> allSessions = profile.getAllSessions();
|
List<Session> allSessions = profile.getAllSessions();
|
||||||
allSessions.sort(new SessionStartComparator());
|
allSessions.sort(new SessionStartComparator());
|
||||||
|
|
||||||
List<PlayerProfile> players = profile.getPlayers();
|
players = profile.getPlayers();
|
||||||
List<PlayerProfile> ops = profile.getOps().collect(Collectors.toList());
|
List<PlayerProfile> ops = profile.getOps().collect(Collectors.toList());
|
||||||
long playersTotal = got("playersTotal", players.size());
|
long playersTotal = got("playersTotal", players.size());
|
||||||
|
|
||||||
@ -126,37 +128,17 @@ public class AnalysisData extends RawData {
|
|||||||
addValue("ops", ops.size());
|
addValue("ops", ops.size());
|
||||||
addValue("playersTotal", playersTotal);
|
addValue("playersTotal", playersTotal);
|
||||||
|
|
||||||
healthTab(now, monthAgo, players, tpsDataMonth);
|
healthTab(now, players, tpsDataMonth);
|
||||||
|
|
||||||
long totalPlaytime = profile.getTotalPlaytime();
|
long totalPlaytime = profile.getTotalPlaytime();
|
||||||
addValue("playtimeTotal", playersTotal != 0 ? FormatUtils.formatTimeAmount(totalPlaytime) : "No Players");
|
addValue("playtimeTotal", playersTotal != 0 ? FormatUtils.formatTimeAmount(totalPlaytime) : "No Players");
|
||||||
addValue("playtimeAverage", playersTotal != 0 ? FormatUtils.formatTimeAmount(MathUtils.averageLong(totalPlaytime, playersTotal)) : "-");
|
addValue("playtimeAverage", playersTotal != 0 ? FormatUtils.formatTimeAmount(MathUtils.averageLong(totalPlaytime, playersTotal)) : "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void healthTab(long now, long monthAgo, List<PlayerProfile> players, List<TPS> tpsDataMonth) {
|
private void healthTab(long now, List<PlayerProfile> players, List<TPS> tpsDataMonth) {
|
||||||
double serverHealth = 100.0;
|
TreeMap<Long, Map<String, Set<UUID>>> activityData = AnalysisUtils.turnToActivityDataMap(now, players);
|
||||||
List<String> healthNotes = new ArrayList<>();
|
|
||||||
|
|
||||||
TreeMap<Long, Map<String, Set<UUID>>> activityData = new TreeMap<>();
|
|
||||||
long fourWeeksAgo = now - TimeAmount.WEEK.ms() * 4L;
|
|
||||||
|
|
||||||
if (!players.isEmpty()) {
|
|
||||||
for (PlayerProfile player : players) {
|
|
||||||
for (long date = now; date >= now - TimeAmount.MONTH.ms() * 2L; date -= TimeAmount.WEEK.ms()) {
|
|
||||||
double activityIndex = player.getActivityIndex(date);
|
|
||||||
String index = FormatUtils.readableActivityIndex(activityIndex)[1];
|
|
||||||
|
|
||||||
Map<String, Set<UUID>> map = activityData.getOrDefault(date, new HashMap<>());
|
|
||||||
Set<UUID> uuids = map.getOrDefault(index, new HashSet<>());
|
|
||||||
uuids.add(player.getUuid());
|
|
||||||
map.put(index, uuids);
|
|
||||||
activityData.put(date, map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Set<UUID>> activityNow = activityData.getOrDefault(now, new HashMap<>());
|
Map<String, Set<UUID>> activityNow = activityData.getOrDefault(now, new HashMap<>());
|
||||||
Map<String, Set<UUID>> activityFourWAgo = activityData.getOrDefault(fourWeeksAgo, new HashMap<>());
|
|
||||||
|
|
||||||
String[] activityStackSeries = ActivityStackGraphCreator.createSeries(activityData);
|
String[] activityStackSeries = ActivityStackGraphCreator.createSeries(activityData);
|
||||||
String activityPieSeries = ActivityPieCreator.createSeriesData(activityNow);
|
String activityPieSeries = ActivityPieCreator.createSeriesData(activityNow);
|
||||||
@ -168,184 +150,14 @@ public class AnalysisData extends RawData {
|
|||||||
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
|
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
|
||||||
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
|
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
|
||||||
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
|
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
|
||||||
Set<UUID> veryActiveFWAG = activityFourWAgo.getOrDefault("Very Active", new HashSet<>());
|
|
||||||
Set<UUID> activeFWAG = activityFourWAgo.getOrDefault("Active", new HashSet<>());
|
|
||||||
Set<UUID> regularFWAG = activityFourWAgo.getOrDefault("Regular", new HashSet<>());
|
|
||||||
|
|
||||||
addValue("playersRegular", (veryActiveNow.size() + activeNow.size() + regularNow.size()));
|
addValue("playersRegular", (veryActiveNow.size() + activeNow.size() + regularNow.size()));
|
||||||
|
|
||||||
Set<UUID> regularRemainCompareSet = new HashSet<>(regularFWAG);
|
HealthNotes healthNotes = new HealthNotes(this, activityData, tpsDataMonth, now);
|
||||||
regularRemainCompareSet.addAll(activeFWAG);
|
healthNotes.analyzeHealth();
|
||||||
regularRemainCompareSet.addAll(veryActiveFWAG);
|
|
||||||
|
|
||||||
int activeFWAGNum = regularRemainCompareSet.size();
|
addValue("healthNotes", healthNotes.parse());
|
||||||
regularRemainCompareSet.removeAll(regularNow);
|
addValue("healthIndex", healthNotes.getServerHealth());
|
||||||
regularRemainCompareSet.removeAll(activeNow);
|
|
||||||
regularRemainCompareSet.removeAll(veryActiveNow);
|
|
||||||
int notRegularAnymore = regularRemainCompareSet.size();
|
|
||||||
int remain = activeFWAGNum - notRegularAnymore;
|
|
||||||
double percRemain = remain * 100.0 / activeFWAGNum;
|
|
||||||
|
|
||||||
Set<UUID> regularNewCompareSet = new HashSet<>(regularNow);
|
|
||||||
regularNewCompareSet.addAll(activeNow);
|
|
||||||
regularNewCompareSet.addAll(veryActiveNow);
|
|
||||||
regularNewCompareSet.removeAll(regularFWAG);
|
|
||||||
regularNewCompareSet.removeAll(activeFWAG);
|
|
||||||
regularNewCompareSet.removeAll(veryActiveFWAG);
|
|
||||||
int newActive = regularNewCompareSet.size();
|
|
||||||
|
|
||||||
int change = newActive - notRegularAnymore;
|
|
||||||
|
|
||||||
String remainNote = "";
|
|
||||||
if (activeFWAGNum != 0) {
|
|
||||||
remainNote = " ";
|
|
||||||
if (percRemain > 50) {
|
|
||||||
remainNote += Html.GREEN_THUMB.parse();
|
|
||||||
} else if (percRemain > 20) {
|
|
||||||
remainNote += Html.YELLOW_FLAG.parse();
|
|
||||||
} else {
|
|
||||||
remainNote += Html.RED_WARN.parse();
|
|
||||||
serverHealth -= 2.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
remainNote += " " + FormatUtils.cutDecimals(percRemain) + "% of regular players have remained active (" + remain + "/" + activeFWAGNum + ")";
|
|
||||||
}
|
|
||||||
if (change > 0) {
|
|
||||||
healthNotes.add(
|
|
||||||
"<p>" + Html.GREEN_THUMB.parse() + " Number of regular players has increased (+" + change + ")<br>" +
|
|
||||||
remainNote + "</p>");
|
|
||||||
} else if (change == 0) {
|
|
||||||
healthNotes.add(
|
|
||||||
"<p>" + Html.GREEN_THUMB.parse() + " Number of regular players has stayed the same (+" + change + ")<br>" +
|
|
||||||
remainNote + "</p>");
|
|
||||||
} else if (change > -20) {
|
|
||||||
healthNotes.add(
|
|
||||||
"<p>" + Html.YELLOW_FLAG.parse() + " Number of regular players has decreased (" + change + ")<br>" +
|
|
||||||
remainNote + "</p>");
|
|
||||||
serverHealth -= 5;
|
|
||||||
} else {
|
|
||||||
healthNotes.add(
|
|
||||||
"<p>" + Html.RED_WARN.parse() + " Number of regular players has decreased (" + change + ")<br>" +
|
|
||||||
remainNote + "</p>");
|
|
||||||
serverHealth -= 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
double avgOnlineOnRegister = MathUtils.averageInt(stickyMonthData.stream().map(StickyData::getOnlineOnJoin));
|
|
||||||
if (avgOnlineOnRegister >= 1) {
|
|
||||||
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " New Players have players to play with when they join ("
|
|
||||||
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
|
|
||||||
} else {
|
|
||||||
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " New Players may not have players to play with when they join ("
|
|
||||||
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
|
|
||||||
serverHealth -= 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
long newM = value("newM");
|
|
||||||
long stuckPerM = value("stuckPerM");
|
|
||||||
|
|
||||||
if (newM != 0) {
|
|
||||||
double stuckPerc = MathUtils.averageDouble(stuckPerM, newM) * 100;
|
|
||||||
if (stuckPerc >= 25) {
|
|
||||||
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " " + FormatUtils.cutDecimals(stuckPerc)
|
|
||||||
+ "% of new players have stuck around (" + stuckPerM + "/" + newM + ")</p>");
|
|
||||||
} else {
|
|
||||||
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " " + FormatUtils.cutDecimals(stuckPerc)
|
|
||||||
+ "% of new players have stuck around (" + stuckPerM + "/" + newM + ")</p>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<PlayerProfile> currentActivePlayers = players.stream()
|
|
||||||
.filter(player -> player.getActivityIndex(now) >= 1.75)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
long twoWeeksAgo = now - TimeAmount.WEEK.ms() * 2L;
|
|
||||||
|
|
||||||
long totalFourToTwoWeeks = 0;
|
|
||||||
long totalLastTwoWeeks = 0;
|
|
||||||
for (PlayerProfile activePlayer : currentActivePlayers) {
|
|
||||||
totalFourToTwoWeeks += activePlayer.getPlaytime(monthAgo, twoWeeksAgo);
|
|
||||||
totalLastTwoWeeks += activePlayer.getPlaytime(twoWeeksAgo, now);
|
|
||||||
}
|
|
||||||
int currentlyActive = currentActivePlayers.size();
|
|
||||||
if (currentlyActive != 0) {
|
|
||||||
long avgFourToTwoWeeks = MathUtils.averageLong(totalFourToTwoWeeks, currentlyActive);
|
|
||||||
long avgLastTwoWeeks = MathUtils.averageLong(totalLastTwoWeeks, currentlyActive);
|
|
||||||
String avgLastTwoWeeksString = FormatUtils.formatTimeAmount(avgLastTwoWeeks);
|
|
||||||
String avgFourToTwoWeeksString = FormatUtils.formatTimeAmount(avgFourToTwoWeeks);
|
|
||||||
if (avgFourToTwoWeeks >= avgLastTwoWeeks) {
|
|
||||||
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " Active players to have things to do (Played "
|
|
||||||
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
|
|
||||||
+ ", last two weeks vs weeks 2-4)</p>");
|
|
||||||
} else if (avgFourToTwoWeeks - avgLastTwoWeeks > TimeAmount.HOUR.ms() * 2L) {
|
|
||||||
healthNotes.add("<p>" + Html.RED_WARN.parse() + " Active players might to be running out of things to do (Played "
|
|
||||||
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
|
|
||||||
+ ", last two weeks vs weeks 2-4)</p>");
|
|
||||||
serverHealth -= 5;
|
|
||||||
} else {
|
|
||||||
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " Active players might to be running out of things to do (Played "
|
|
||||||
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
|
|
||||||
+ ", last two weeks vs weeks 2-4)</p>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long serverDownTime = ServerProfile.serverDownTime(tpsDataMonth);
|
|
||||||
// long serverIdleTime = ServerProfile.serverIdleTime(tpsDataMonth);
|
|
||||||
double aboveThreshold = ServerProfile.aboveLowThreshold(tpsDataMonth);
|
|
||||||
long tpsSpikeMonth = value("tpsSpikeMonth");
|
|
||||||
|
|
||||||
String avgLowThresholdString = " ";
|
|
||||||
if (aboveThreshold >= 0.96) {
|
|
||||||
avgLowThresholdString += Html.GREEN_THUMB.parse();
|
|
||||||
} else if (aboveThreshold >= 0.9) {
|
|
||||||
avgLowThresholdString += Html.YELLOW_FLAG.parse();
|
|
||||||
serverHealth *= 0.9;
|
|
||||||
} else {
|
|
||||||
avgLowThresholdString += Html.RED_WARN.parse();
|
|
||||||
serverHealth *= 0.6;
|
|
||||||
}
|
|
||||||
avgLowThresholdString += " Average TPS was above Low Threshold "
|
|
||||||
+ FormatUtils.cutDecimals(aboveThreshold * 100.0) + "% of the time";
|
|
||||||
|
|
||||||
if (tpsSpikeMonth <= 5) {
|
|
||||||
healthNotes.add("<p>" + Html.GREEN_THUMB.parse()
|
|
||||||
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
|
|
||||||
" " + tpsSpikeMonth + " times<br>" +
|
|
||||||
avgLowThresholdString + "</p>");
|
|
||||||
} else if (tpsSpikeMonth <= 25) {
|
|
||||||
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse()
|
|
||||||
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
|
|
||||||
" " + tpsSpikeMonth + " times<br>" +
|
|
||||||
avgLowThresholdString + "</p>");
|
|
||||||
serverHealth *= 0.95;
|
|
||||||
} else {
|
|
||||||
healthNotes.add("<p>" + Html.RED_WARN.parse()
|
|
||||||
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
|
|
||||||
" " + tpsSpikeMonth + " times<br>" +
|
|
||||||
avgLowThresholdString + "</p>");
|
|
||||||
serverHealth *= 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverDownTime <= TimeAmount.DAY.ms()) {
|
|
||||||
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " Total Server downtime (No Data) was "
|
|
||||||
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
|
|
||||||
} else if (serverDownTime <= TimeAmount.WEEK.ms()) {
|
|
||||||
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " Total Server downtime (No Data) was "
|
|
||||||
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
|
|
||||||
serverHealth *= 0.6;
|
|
||||||
} else {
|
|
||||||
healthNotes.add("<p>" + Html.RED_WARN.parse() + " Total Server downtime (No Data) was "
|
|
||||||
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
|
|
||||||
serverHealth *= 0.3;
|
|
||||||
}
|
|
||||||
// healthNotes.add("<p>" + Html.FA_COLORED_ICON.parse("red", "life-ring") + " Server was idle (No Players) "
|
|
||||||
// + FormatUtils.formatTimeAmount(serverIdleTime) + " in total</p>");
|
|
||||||
|
|
||||||
StringBuilder healthNoteBuilder = new StringBuilder();
|
|
||||||
for (String healthNote : healthNotes) {
|
|
||||||
healthNoteBuilder.append(healthNote);
|
|
||||||
}
|
|
||||||
addValue("healthNotes", healthNoteBuilder.toString());
|
|
||||||
addValue("healthIndex", serverHealth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void commandUsage(Map<String, Integer> commandUsage) {
|
private void commandUsage(Map<String, Integer> commandUsage) {
|
||||||
@ -424,6 +236,10 @@ public class AnalysisData extends RawData {
|
|||||||
addValue("playersStuckPercMonth", newM != 0 ? FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerM, newM) * 100.0) + "%" : "-");
|
addValue("playersStuckPercMonth", newM != 0 ? FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerM, newM) * 100.0) + "%" : "-");
|
||||||
addValue("playersStuckPercWeek", newW != 0 ? FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerW, newW) * 100.0) + "%" : "-");
|
addValue("playersStuckPercWeek", newW != 0 ? FormatUtils.cutDecimals(MathUtils.averageDouble(stuckPerW, newW) * 100.0) + "%" : "-");
|
||||||
|
|
||||||
|
stuckPerDay(newDay, newMonth, newD, playersStuckPerMonth, playersStuckPerWeek);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stuckPerDay(List<PlayerProfile> newDay, List<PlayerProfile> newMonth, long newD, List<PlayerProfile> playersStuckPerMonth, List<PlayerProfile> playersStuckPerWeek) {
|
||||||
if (newD != 0) {
|
if (newD != 0) {
|
||||||
// New Players
|
// New Players
|
||||||
stickyMonthData = newMonth.stream().map(StickyData::new).distinct().collect(Collectors.toSet());
|
stickyMonthData = newMonth.stream().map(StickyData::new).distinct().collect(Collectors.toSet());
|
||||||
@ -434,41 +250,9 @@ public class AnalysisData extends RawData {
|
|||||||
|
|
||||||
int stuckPerD = 0;
|
int stuckPerD = 0;
|
||||||
for (PlayerProfile playerProfile : newDay) {
|
for (PlayerProfile playerProfile : newDay) {
|
||||||
StickyData data = new StickyData(playerProfile);
|
double probability = AnalysisUtils.calculateProbabilityOfStaying(
|
||||||
|
stickyMonthData, stickyW, stickyStuckM, stickyStuckW, playerProfile
|
||||||
Set<StickyData> similarM = new HashSet<>();
|
);
|
||||||
Set<StickyData> similarW = new HashSet<>();
|
|
||||||
for (StickyData stickyData : stickyMonthData) {
|
|
||||||
if (stickyData.distance(data) < 2.5) {
|
|
||||||
similarM.add(stickyData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (StickyData stickyData : stickyW) {
|
|
||||||
if (stickyData.distance(data) < 2.5) {
|
|
||||||
similarW.add(stickyData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double probability = 1.0;
|
|
||||||
|
|
||||||
int stickM = 0;
|
|
||||||
for (StickyData stickyData : stickyStuckM) {
|
|
||||||
if (similarM.contains(stickyData)) {
|
|
||||||
stickM++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
probability *= (stickM / similarM.size());
|
|
||||||
|
|
||||||
int stickW = 0;
|
|
||||||
for (StickyData stickyData : stickyStuckW) {
|
|
||||||
if (similarW.contains(stickyData)) {
|
|
||||||
stickW++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
probability *= (stickW / similarW.size());
|
|
||||||
|
|
||||||
if (probability >= 0.5) {
|
if (probability >= 0.5) {
|
||||||
stuckPerD++;
|
stuckPerD++;
|
||||||
}
|
}
|
||||||
@ -566,7 +350,15 @@ public class AnalysisData extends RawData {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long value(String key) {
|
public long value(String key) {
|
||||||
return analyzedValues.getOrDefault(key, 0L);
|
return analyzedValues.getOrDefault(key, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<StickyData> getStickyMonthData() {
|
||||||
|
return stickyMonthData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PlayerProfile> getPlayers() {
|
||||||
|
return players;
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,252 @@
|
|||||||
|
/*
|
||||||
|
* Licence is provided in the jar as license.yml also here:
|
||||||
|
* https://github.com/Rsl1122/Plan-PlayerAnalytics/blob/master/Plan/src/main/resources/license.yml
|
||||||
|
*/
|
||||||
|
package main.java.com.djrapitops.plan.data.element;
|
||||||
|
|
||||||
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
|
import main.java.com.djrapitops.plan.data.AnalysisData;
|
||||||
|
import main.java.com.djrapitops.plan.data.PlayerProfile;
|
||||||
|
import main.java.com.djrapitops.plan.data.ServerProfile;
|
||||||
|
import main.java.com.djrapitops.plan.data.container.StickyData;
|
||||||
|
import main.java.com.djrapitops.plan.data.container.TPS;
|
||||||
|
import main.java.com.djrapitops.plan.settings.Settings;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.FormatUtils;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.analysis.MathUtils;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.html.Html;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class in charge of Server health analysis.
|
||||||
|
*
|
||||||
|
* @author Rsl1122
|
||||||
|
*/
|
||||||
|
public class HealthNotes {
|
||||||
|
|
||||||
|
private final List<String> healthNotes;
|
||||||
|
private double serverHealth;
|
||||||
|
|
||||||
|
private final AnalysisData analysisData;
|
||||||
|
private final TreeMap<Long, Map<String, Set<UUID>>> activityData;
|
||||||
|
private final List<TPS> tpsDataMonth;
|
||||||
|
private final long now;
|
||||||
|
private final long fourWeeksAgo;
|
||||||
|
|
||||||
|
public HealthNotes(AnalysisData analysisData, TreeMap<Long, Map<String, Set<UUID>>> activityData, List<TPS> tpsDataMonth, long now) {
|
||||||
|
this.healthNotes = new ArrayList<>();
|
||||||
|
serverHealth = 100.0;
|
||||||
|
|
||||||
|
this.analysisData = analysisData;
|
||||||
|
this.activityData = activityData;
|
||||||
|
this.tpsDataMonth = tpsDataMonth;
|
||||||
|
this.now = now;
|
||||||
|
this.fourWeeksAgo = now - TimeAmount.WEEK.ms() * 4L;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void analyzeHealth() {
|
||||||
|
activityChangeNote();
|
||||||
|
newPlayerNote();
|
||||||
|
activePlayerPlaytimeChange();
|
||||||
|
lowPerformance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String parse() {
|
||||||
|
StringBuilder healthNoteBuilder = new StringBuilder();
|
||||||
|
for (String healthNote : healthNotes) {
|
||||||
|
healthNoteBuilder.append(healthNote);
|
||||||
|
}
|
||||||
|
return healthNoteBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getServerHealth() {
|
||||||
|
return serverHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activityChangeNote() {
|
||||||
|
Map<String, Set<UUID>> activityNow = activityData.getOrDefault(now, new HashMap<>());
|
||||||
|
Set<UUID> veryActiveNow = activityNow.getOrDefault("Very Active", new HashSet<>());
|
||||||
|
Set<UUID> activeNow = activityNow.getOrDefault("Active", new HashSet<>());
|
||||||
|
Set<UUID> regularNow = activityNow.getOrDefault("Regular", new HashSet<>());
|
||||||
|
|
||||||
|
Map<String, Set<UUID>> activityFourWAgo = activityData.getOrDefault(fourWeeksAgo, new HashMap<>());
|
||||||
|
Set<UUID> veryActiveFWAG = activityFourWAgo.getOrDefault("Very Active", new HashSet<>());
|
||||||
|
Set<UUID> activeFWAG = activityFourWAgo.getOrDefault("Active", new HashSet<>());
|
||||||
|
Set<UUID> regularFWAG = activityFourWAgo.getOrDefault("Regular", new HashSet<>());
|
||||||
|
|
||||||
|
Set<UUID> regularRemainCompareSet = new HashSet<>(regularFWAG);
|
||||||
|
regularRemainCompareSet.addAll(activeFWAG);
|
||||||
|
regularRemainCompareSet.addAll(veryActiveFWAG);
|
||||||
|
|
||||||
|
int activeFWAGNum = regularRemainCompareSet.size();
|
||||||
|
regularRemainCompareSet.removeAll(regularNow);
|
||||||
|
regularRemainCompareSet.removeAll(activeNow);
|
||||||
|
regularRemainCompareSet.removeAll(veryActiveNow);
|
||||||
|
int notRegularAnymore = regularRemainCompareSet.size();
|
||||||
|
int remain = activeFWAGNum - notRegularAnymore;
|
||||||
|
double percRemain = remain * 100.0 / activeFWAGNum;
|
||||||
|
|
||||||
|
int newActive = getNewActive(veryActiveNow, activeNow, regularNow, veryActiveFWAG, activeFWAG, regularFWAG);
|
||||||
|
|
||||||
|
int change = newActive - notRegularAnymore;
|
||||||
|
|
||||||
|
String remainNote = "";
|
||||||
|
if (activeFWAGNum != 0) {
|
||||||
|
remainNote = " ";
|
||||||
|
if (percRemain > 50) {
|
||||||
|
remainNote += Html.GREEN_THUMB.parse();
|
||||||
|
} else if (percRemain > 20) {
|
||||||
|
remainNote += Html.YELLOW_FLAG.parse();
|
||||||
|
} else {
|
||||||
|
remainNote += Html.RED_WARN.parse();
|
||||||
|
serverHealth -= 2.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
remainNote += " " + FormatUtils.cutDecimals(percRemain) + "% of regular players have remained active ("
|
||||||
|
+ remain + "/" + activeFWAGNum + ")";
|
||||||
|
}
|
||||||
|
if (change > 0) {
|
||||||
|
healthNotes.add(
|
||||||
|
"<p>" + Html.GREEN_THUMB.parse() + " Number of regular players has increased (+" + change + ")<br>" +
|
||||||
|
remainNote + "</p>");
|
||||||
|
} else if (change == 0) {
|
||||||
|
healthNotes.add(
|
||||||
|
"<p>" + Html.GREEN_THUMB.parse() + " Number of regular players has stayed the same (+" + change + ")<br>" +
|
||||||
|
remainNote + "</p>");
|
||||||
|
} else if (change > -20) {
|
||||||
|
healthNotes.add(
|
||||||
|
"<p>" + Html.YELLOW_FLAG.parse() + " Number of regular players has decreased (" + change + ")<br>" +
|
||||||
|
remainNote + "</p>");
|
||||||
|
serverHealth -= 5;
|
||||||
|
} else {
|
||||||
|
healthNotes.add(
|
||||||
|
"<p>" + Html.RED_WARN.parse() + " Number of regular players has decreased (" + change + ")<br>" +
|
||||||
|
remainNote + "</p>");
|
||||||
|
serverHealth -= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newPlayerNote() {
|
||||||
|
double avgOnlineOnRegister = MathUtils.averageInt(analysisData.getStickyMonthData().stream().map(StickyData::getOnlineOnJoin));
|
||||||
|
if (avgOnlineOnRegister >= 1) {
|
||||||
|
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " New Players have players to play with when they join ("
|
||||||
|
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
|
||||||
|
} else {
|
||||||
|
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " New Players may not have players to play with when they join ("
|
||||||
|
+ FormatUtils.cutDecimals(avgOnlineOnRegister) + " on average)</p>");
|
||||||
|
serverHealth -= 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
long newM = analysisData.value("newM");
|
||||||
|
long stuckPerM = analysisData.value("stuckPerM");
|
||||||
|
|
||||||
|
if (newM != 0) {
|
||||||
|
double stuckPerc = MathUtils.averageDouble(stuckPerM, newM) * 100;
|
||||||
|
if (stuckPerc >= 25) {
|
||||||
|
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " " + FormatUtils.cutDecimals(stuckPerc)
|
||||||
|
+ "% of new players have stuck around (" + stuckPerM + "/" + newM + ")</p>");
|
||||||
|
} else {
|
||||||
|
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " " + FormatUtils.cutDecimals(stuckPerc)
|
||||||
|
+ "% of new players have stuck around (" + stuckPerM + "/" + newM + ")</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void activePlayerPlaytimeChange() {
|
||||||
|
List<PlayerProfile> currentActivePlayers = analysisData.getPlayers().stream()
|
||||||
|
.filter(player -> player.getActivityIndex(now) >= 1.75)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
long twoWeeksAgo = now - TimeAmount.WEEK.ms() * 2L;
|
||||||
|
|
||||||
|
long totalFourToTwoWeeks = 0;
|
||||||
|
long totalLastTwoWeeks = 0;
|
||||||
|
for (PlayerProfile activePlayer : currentActivePlayers) {
|
||||||
|
totalFourToTwoWeeks += activePlayer.getPlaytime(analysisData.value("monthAgo"), twoWeeksAgo);
|
||||||
|
totalLastTwoWeeks += activePlayer.getPlaytime(twoWeeksAgo, now);
|
||||||
|
}
|
||||||
|
int currentlyActive = currentActivePlayers.size();
|
||||||
|
if (currentlyActive != 0) {
|
||||||
|
long avgFourToTwoWeeks = MathUtils.averageLong(totalFourToTwoWeeks, currentlyActive);
|
||||||
|
long avgLastTwoWeeks = MathUtils.averageLong(totalLastTwoWeeks, currentlyActive);
|
||||||
|
String avgLastTwoWeeksString = FormatUtils.formatTimeAmount(avgLastTwoWeeks);
|
||||||
|
String avgFourToTwoWeeksString = FormatUtils.formatTimeAmount(avgFourToTwoWeeks);
|
||||||
|
if (avgFourToTwoWeeks >= avgLastTwoWeeks) {
|
||||||
|
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " Active players to have things to do (Played "
|
||||||
|
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
|
||||||
|
+ ", last two weeks vs weeks 2-4)</p>");
|
||||||
|
} else if (avgFourToTwoWeeks - avgLastTwoWeeks > TimeAmount.HOUR.ms() * 2L) {
|
||||||
|
healthNotes.add("<p>" + Html.RED_WARN.parse() + " Active players might to be running out of things to do (Played "
|
||||||
|
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
|
||||||
|
+ ", last two weeks vs weeks 2-4)</p>");
|
||||||
|
serverHealth -= 5;
|
||||||
|
} else {
|
||||||
|
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " Active players might to be running out of things to do (Played "
|
||||||
|
+ avgLastTwoWeeksString + " vs " + avgFourToTwoWeeksString
|
||||||
|
+ ", last two weeks vs weeks 2-4)</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lowPerformance() {
|
||||||
|
long serverDownTime = ServerProfile.serverDownTime(tpsDataMonth);
|
||||||
|
double aboveThreshold = ServerProfile.aboveLowThreshold(tpsDataMonth);
|
||||||
|
long tpsSpikeMonth = analysisData.value("tpsSpikeMonth");
|
||||||
|
|
||||||
|
String avgLowThresholdString = " ";
|
||||||
|
if (aboveThreshold >= 0.96) {
|
||||||
|
avgLowThresholdString += Html.GREEN_THUMB.parse();
|
||||||
|
} else if (aboveThreshold >= 0.9) {
|
||||||
|
avgLowThresholdString += Html.YELLOW_FLAG.parse();
|
||||||
|
serverHealth *= 0.9;
|
||||||
|
} else {
|
||||||
|
avgLowThresholdString += Html.RED_WARN.parse();
|
||||||
|
serverHealth *= 0.6;
|
||||||
|
}
|
||||||
|
avgLowThresholdString += " Average TPS was above Low Threshold "
|
||||||
|
+ FormatUtils.cutDecimals(aboveThreshold * 100.0) + "% of the time";
|
||||||
|
|
||||||
|
if (tpsSpikeMonth <= 5) {
|
||||||
|
healthNotes.add("<p>" + Html.GREEN_THUMB.parse()
|
||||||
|
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
|
||||||
|
" " + tpsSpikeMonth + " times<br>" +
|
||||||
|
avgLowThresholdString + "</p>");
|
||||||
|
} else if (tpsSpikeMonth <= 25) {
|
||||||
|
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse()
|
||||||
|
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
|
||||||
|
" " + tpsSpikeMonth + " times<br>" +
|
||||||
|
avgLowThresholdString + "</p>");
|
||||||
|
serverHealth *= 0.95;
|
||||||
|
} else {
|
||||||
|
healthNotes.add("<p>" + Html.RED_WARN.parse()
|
||||||
|
+ " Average TPS dropped below Low Threshold (" + Settings.THEME_GRAPH_TPS_THRESHOLD_MED.getNumber() + ")" +
|
||||||
|
" " + tpsSpikeMonth + " times<br>" +
|
||||||
|
avgLowThresholdString + "</p>");
|
||||||
|
serverHealth *= 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverDownTime <= TimeAmount.DAY.ms()) {
|
||||||
|
healthNotes.add("<p>" + Html.GREEN_THUMB.parse() + " Total Server downtime (No Data) was "
|
||||||
|
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
|
||||||
|
} else if (serverDownTime <= TimeAmount.WEEK.ms()) {
|
||||||
|
healthNotes.add("<p>" + Html.YELLOW_FLAG.parse() + " Total Server downtime (No Data) was "
|
||||||
|
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
|
||||||
|
serverHealth *= 0.6;
|
||||||
|
} else {
|
||||||
|
healthNotes.add("<p>" + Html.RED_WARN.parse() + " Total Server downtime (No Data) was "
|
||||||
|
+ FormatUtils.formatTimeAmount(serverDownTime) + "</p>");
|
||||||
|
serverHealth *= 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNewActive(Set<UUID> veryActiveNow, Set<UUID> activeNow, Set<UUID> regularNow, Set<UUID> veryActiveFWAG, Set<UUID> activeFWAG, Set<UUID> regularFWAG) {
|
||||||
|
Set<UUID> regularNewCompareSet = new HashSet<>(regularNow);
|
||||||
|
regularNewCompareSet.addAll(activeNow);
|
||||||
|
regularNewCompareSet.addAll(veryActiveNow);
|
||||||
|
regularNewCompareSet.removeAll(regularFWAG);
|
||||||
|
regularNewCompareSet.removeAll(activeFWAG);
|
||||||
|
regularNewCompareSet.removeAll(veryActiveFWAG);
|
||||||
|
return regularNewCompareSet.size();
|
||||||
|
}
|
||||||
|
}
|
@ -3,9 +3,12 @@ package main.java.com.djrapitops.plan.utilities.analysis;
|
|||||||
import com.djrapitops.plugin.api.TimeAmount;
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
import com.djrapitops.plugin.api.utility.log.Log;
|
import com.djrapitops.plugin.api.utility.log.Log;
|
||||||
import main.java.com.djrapitops.plan.api.IPlan;
|
import main.java.com.djrapitops.plan.api.IPlan;
|
||||||
|
import main.java.com.djrapitops.plan.data.PlayerProfile;
|
||||||
import main.java.com.djrapitops.plan.data.container.Session;
|
import main.java.com.djrapitops.plan.data.container.Session;
|
||||||
|
import main.java.com.djrapitops.plan.data.container.StickyData;
|
||||||
import main.java.com.djrapitops.plan.data.time.GMTimes;
|
import main.java.com.djrapitops.plan.data.time.GMTimes;
|
||||||
import main.java.com.djrapitops.plan.data.time.WorldTimes;
|
import main.java.com.djrapitops.plan.data.time.WorldTimes;
|
||||||
|
import main.java.com.djrapitops.plan.utilities.FormatUtils;
|
||||||
import main.java.com.djrapitops.plan.utilities.MiscUtils;
|
import main.java.com.djrapitops.plan.utilities.MiscUtils;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@ -164,4 +167,61 @@ public class AnalysisUtils {
|
|||||||
|
|
||||||
return userSessions;
|
return userSessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double calculateProbabilityOfStaying(Set<StickyData> stickyMonthData, Set<StickyData> stickyW, Set<StickyData> stickyStuckM, Set<StickyData> stickyStuckW, PlayerProfile playerProfile) {
|
||||||
|
StickyData data = new StickyData(playerProfile);
|
||||||
|
|
||||||
|
Set<StickyData> similarM = new HashSet<>();
|
||||||
|
Set<StickyData> similarW = new HashSet<>();
|
||||||
|
for (StickyData stickyData : stickyMonthData) {
|
||||||
|
if (stickyData.distance(data) < 2.5) {
|
||||||
|
similarM.add(stickyData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (StickyData stickyData : stickyW) {
|
||||||
|
if (stickyData.distance(data) < 2.5) {
|
||||||
|
similarW.add(stickyData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double probability = 1.0;
|
||||||
|
|
||||||
|
int stickM = 0;
|
||||||
|
for (StickyData stickyData : stickyStuckM) {
|
||||||
|
if (similarM.contains(stickyData)) {
|
||||||
|
stickM++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
probability *= (stickM / similarM.size());
|
||||||
|
|
||||||
|
int stickW = 0;
|
||||||
|
for (StickyData stickyData : stickyStuckW) {
|
||||||
|
if (similarW.contains(stickyData)) {
|
||||||
|
stickW++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
probability *= (stickW / similarW.size());
|
||||||
|
return probability;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TreeMap<Long, Map<String, Set<UUID>>> turnToActivityDataMap(long time, List<PlayerProfile> players) {
|
||||||
|
TreeMap<Long, Map<String, Set<UUID>>> activityData = new TreeMap<>();
|
||||||
|
if (!players.isEmpty()) {
|
||||||
|
for (PlayerProfile player : players) {
|
||||||
|
for (long date = time; date >= time - TimeAmount.MONTH.ms() * 2L; date -= TimeAmount.WEEK.ms()) {
|
||||||
|
double activityIndex = player.getActivityIndex(date);
|
||||||
|
String index = FormatUtils.readableActivityIndex(activityIndex)[1];
|
||||||
|
|
||||||
|
Map<String, Set<UUID>> map = activityData.getOrDefault(date, new HashMap<>());
|
||||||
|
Set<UUID> uuids = map.getOrDefault(index, new HashSet<>());
|
||||||
|
uuids.add(player.getUuid());
|
||||||
|
map.put(index, uuids);
|
||||||
|
activityData.put(date, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activityData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user