diff --git a/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayerVersusMutator.java b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayerVersusMutator.java new file mode 100644 index 000000000..e5a05b405 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/data/store/mutators/PlayerVersusMutator.java @@ -0,0 +1,93 @@ +/* + * 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 . + */ +package com.djrapitops.plan.data.store.mutators; + +import com.djrapitops.plan.data.container.PlayerKill; +import com.djrapitops.plan.data.store.containers.DataContainer; +import com.djrapitops.plan.data.store.keys.PlayerKeys; +import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.utilities.Predicates; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class PlayerVersusMutator { + + private final SessionsMutator sessionsMutator; + private final List kills; + private final List deaths; + + public PlayerVersusMutator(SessionsMutator sessionsMutator, List kills, List deaths) { + this.sessionsMutator = sessionsMutator; + this.kills = kills; + this.deaths = deaths; + } + + public static PlayerVersusMutator forContainer(DataContainer container) { + return new PlayerVersusMutator( + SessionsMutator.forContainer(container), + container.getValue(PlayerKeys.PLAYER_KILLS).orElse(Collections.emptyList()), + container.getValue(PlayerKeys.PLAYER_DEATHS_KILLS).orElse(Collections.emptyList()) + ); + } + + public PlayerVersusMutator filterBetween(long after, long before) { + Predicate killWithinDate = Predicates.within(after, before); + + return new PlayerVersusMutator( + sessionsMutator.filterSessionsBetween(after, before), + kills.stream().filter(killWithinDate).collect(Collectors.toList()), + deaths.stream().filter(killWithinDate).collect(Collectors.toList()) + ); + } + + public int toPlayerKillCount() { + return kills.size(); + } + + public int toMobKillCount() { + return sessionsMutator.toMobKillCount(); + } + + public int toPlayerDeathCount() { + return deaths.size(); + } + + public int toMobDeathCount() { + return toDeathCount() - toPlayerDeathCount(); + } + + public int toDeathCount() { + return sessionsMutator.toDeathCount(); + } + + public List toTopWeapons(int limit) { + return kills.stream() + .map(PlayerKill::getWeapon) + .collect(HashMap::new, (map, weapon) -> map.put(weapon, map.getOrDefault(weapon, 0) + 1), (mapOne, mapTwo) -> { + }) // Collected to Map with weapon counts + .entrySet().stream() + .sorted((one, two) -> Integer.compare(two.getValue(), one.getValue())) // Highest first + .limit(limit) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerJSONParser.java index d2dc46887..5b16b674f 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerJSONParser.java @@ -19,10 +19,7 @@ package com.djrapitops.plan.system.json; import com.djrapitops.plan.data.container.GeoInfo; import com.djrapitops.plan.data.store.containers.PlayerContainer; import com.djrapitops.plan.data.store.keys.PlayerKeys; -import com.djrapitops.plan.data.store.mutators.ActivityIndex; -import com.djrapitops.plan.data.store.mutators.PerServerMutator; -import com.djrapitops.plan.data.store.mutators.PingMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; +import com.djrapitops.plan.data.store.mutators.*; import com.djrapitops.plan.data.time.WorldTimes; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.access.queries.containers.PlayerContainerQuery; @@ -81,6 +78,7 @@ public class PlayerJSONParser { Map data = new HashMap<>(); data.put("info", createInfoJSONMap(player, serverNames)); data.put("online_activity", createOnlineActivityJSONMap(sessionsMutator)); + data.put("kill_data", createPvPPvEMap(player)); data.put("nicknames", player.getValue(PlayerKeys.NICKNAMES) .map(nicks -> Nickname.fromDataNicknames(nicks, serverNames, year)) @@ -104,28 +102,28 @@ public class PlayerJSONParser { long now = System.currentTimeMillis(); long monthAgo = now - TimeUnit.DAYS.toMillis(30L); long weekAgo = now - TimeUnit.DAYS.toMillis(7L); - SessionsMutator sessionsMonth = sessionsMutator.filterSessionsBetween(monthAgo, now); - SessionsMutator sessionsWeek = sessionsMutator.filterSessionsBetween(weekAgo, now); + SessionsMutator sessions30d = sessionsMutator.filterSessionsBetween(monthAgo, now); + SessionsMutator sessions7d = sessions30d.filterSessionsBetween(weekAgo, now); Map onlineActivity = new HashMap<>(); - onlineActivity.put("playtime_30d", timeAmount.apply(sessionsMonth.toPlaytime())); - onlineActivity.put("active_playtime_30d", timeAmount.apply(sessionsMonth.toActivePlaytime())); - onlineActivity.put("afk_time_30d", timeAmount.apply(sessionsMonth.toAfkTime())); - onlineActivity.put("average_session_length_30d", timeAmount.apply(sessionsMonth.toAverageSessionLength())); - onlineActivity.put("session_count_30d", sessionsMonth.count()); - onlineActivity.put("player_kill_count_30d", sessionsMonth.toPlayerKillCount()); - onlineActivity.put("mob_kill_count_30d", sessionsMonth.toMobKillCount()); - onlineActivity.put("death_count_30d", sessionsMonth.toDeathCount()); + onlineActivity.put("playtime_30d", timeAmount.apply(sessions30d.toPlaytime())); + onlineActivity.put("active_playtime_30d", timeAmount.apply(sessions30d.toActivePlaytime())); + onlineActivity.put("afk_time_30d", timeAmount.apply(sessions30d.toAfkTime())); + onlineActivity.put("average_session_length_30d", timeAmount.apply(sessions30d.toAverageSessionLength())); + onlineActivity.put("session_count_30d", sessions30d.count()); + onlineActivity.put("player_kill_count_30d", sessions30d.toPlayerKillCount()); + onlineActivity.put("mob_kill_count_30d", sessions30d.toMobKillCount()); + onlineActivity.put("death_count_30d", sessions30d.toDeathCount()); - onlineActivity.put("playtime_7d", timeAmount.apply(sessionsWeek.toPlaytime())); - onlineActivity.put("active_playtime_7d", timeAmount.apply(sessionsWeek.toActivePlaytime())); - onlineActivity.put("afk_time_7d", timeAmount.apply(sessionsWeek.toAfkTime())); - onlineActivity.put("average_session_length_7d", timeAmount.apply(sessionsWeek.toAverageSessionLength())); - onlineActivity.put("session_count_7d", sessionsWeek.count()); - onlineActivity.put("player_kill_count_7d", sessionsWeek.toPlayerKillCount()); - onlineActivity.put("mob_kill_count_7d", sessionsWeek.toMobKillCount()); - onlineActivity.put("death_count_7d", sessionsWeek.toDeathCount()); + onlineActivity.put("playtime_7d", timeAmount.apply(sessions7d.toPlaytime())); + onlineActivity.put("active_playtime_7d", timeAmount.apply(sessions7d.toActivePlaytime())); + onlineActivity.put("afk_time_7d", timeAmount.apply(sessions7d.toAfkTime())); + onlineActivity.put("average_session_length_7d", timeAmount.apply(sessions7d.toAverageSessionLength())); + onlineActivity.put("session_count_7d", sessions7d.count()); + onlineActivity.put("player_kill_count_7d", sessions7d.toPlayerKillCount()); + onlineActivity.put("mob_kill_count_7d", sessions7d.toMobKillCount()); + onlineActivity.put("death_count_7d", sessions7d.toDeathCount()); return onlineActivity; } @@ -164,6 +162,78 @@ public class PlayerJSONParser { return info; } + private Map createPvPPvEMap(PlayerContainer playerContainer) { + long now = System.currentTimeMillis(); + long weekAgo = now - TimeUnit.DAYS.toMillis(7L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + + PlayerVersusMutator playerVersus = PlayerVersusMutator.forContainer(playerContainer); + PlayerVersusMutator playerVersus30d = playerVersus.filterBetween(monthAgo, now); + PlayerVersusMutator playerVersus7d = playerVersus30d.filterBetween(weekAgo, now); + + Map killData = new HashMap<>(); + int pks = playerVersus.toPlayerKillCount(); + int pks7d = playerVersus7d.toPlayerKillCount(); + int pks30d = playerVersus30d.toPlayerKillCount(); + killData.put("player_kills_total", pks); + killData.put("player_kills_30d", pks30d); + killData.put("player_kills_7d", pks7d); + + int playerDeaths = playerVersus.toPlayerDeathCount(); + int playerDeaths30d = playerVersus30d.toPlayerDeathCount(); + int playerDeaths7d = playerVersus7d.toPlayerDeathCount(); + killData.put("player_deaths_total", playerDeaths); + killData.put("player_deaths_30d", playerDeaths30d); + killData.put("player_deaths_7d", playerDeaths7d); + + double kdr = playerDeaths != 0 ? (double) pks / playerDeaths : pks; + double kdr30d = playerDeaths30d != 0 ? (double) pks30d / playerDeaths30d : pks30d; + double krd7d = playerDeaths7d != 0 ? (double) pks7d / playerDeaths7d : pks7d; + killData.put("player_kdr_total", decimals.apply(kdr)); + killData.put("player_kdr_30d", decimals.apply(kdr30d)); + killData.put("player_kdr_7d", decimals.apply(krd7d)); + + int mobKills = playerVersus.toMobKillCount(); + int mobKills30d = playerVersus30d.toMobKillCount(); + int mobKills7d = playerVersus7d.toMobKillCount(); + killData.put("mob_kills_total", mobKills); + killData.put("mob_kills_30d", mobKills30d); + killData.put("mob_kills_7d", mobKills7d); + + int deaths = playerVersus.toDeathCount(); + int deaths30d = playerVersus30d.toDeathCount(); + int deaths7d = playerVersus7d.toDeathCount(); + killData.put("deaths_total", deaths); + killData.put("deaths_30d", deaths30d); + killData.put("deaths_7d", deaths7d); + + long mobDeaths = deaths - playerDeaths; + long mobDeaths30d = deaths30d - playerDeaths30d; + long mobDeaths7d = deaths7d - playerDeaths7d; + + killData.put("mob_deaths_total", mobDeaths); + killData.put("mob_deaths_30d", mobDeaths30d); + killData.put("mob_deaths_7d", mobDeaths7d); + + double mobKdr = mobDeaths != 0 ? (double) mobKills / mobDeaths : mobKills; + double mobKdr30d = mobDeaths30d != 0 ? (double) mobKills30d / mobDeaths30d : mobKills30d; + double mobKdr7d = mobDeaths7d != 0 ? (double) mobKills7d / mobDeaths7d : mobKills7d; + killData.put("mob_kdr_total", decimals.apply(mobKdr)); + killData.put("mob_kdr_30d", decimals.apply(mobKdr30d)); + killData.put("mob_kdr_7d", decimals.apply(mobKdr7d)); + + List topWeapons = playerVersus.toTopWeapons(3); + killData.put("weapon_1st", getWeapon(topWeapons, 0).orElse("-")); + killData.put("weapon_2nd", getWeapon(topWeapons, 1).orElse("-")); + killData.put("weapon_3rd", getWeapon(topWeapons, 2).orElse("-")); + + return killData; + } + + private Optional getWeapon(List list, int index) { + return list.size() <= index ? Optional.empty() : Optional.of(list.get(index)); + } + public static class Nickname { private String nickname; private String server; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java index 56f347dd5..7171c99e6 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java @@ -37,7 +37,7 @@ public class PvPPvEJSONParser implements TabJSONParser> { private DBSystem dbSystem; - private Formatter decimalFormatter; + private Formatter decimals; @Inject public PvPPvEJSONParser( @@ -46,7 +46,7 @@ public class PvPPvEJSONParser implements TabJSONParser> { ) { this.dbSystem = dbSystem; - decimalFormatter = formatters.decimals(); + decimals = formatters.decimals(); } public Map createJSONAsMap(UUID serverUUID) { @@ -70,9 +70,9 @@ public class PvPPvEJSONParser implements TabJSONParser> { numbers.put("player_kills_30d", pks30d); numbers.put("player_kills_7d", pks7d); - numbers.put("player_kdr_avg", decimalFormatter.apply(db.query(KillQueries.averageKDR(0L, now, serverUUID)))); - numbers.put("player_kdr_avg_30d", decimalFormatter.apply(db.query(KillQueries.averageKDR(monthAgo, now, serverUUID)))); - numbers.put("player_kdr_avg_7d", decimalFormatter.apply(db.query(KillQueries.averageKDR(weekAgo, now, serverUUID)))); + numbers.put("player_kdr_avg", decimals.apply(db.query(KillQueries.averageKDR(0L, now, serverUUID)))); + numbers.put("player_kdr_avg_30d", decimals.apply(db.query(KillQueries.averageKDR(monthAgo, now, serverUUID)))); + numbers.put("player_kdr_avg_7d", decimals.apply(db.query(KillQueries.averageKDR(weekAgo, now, serverUUID)))); Long mobKills = db.query(KillQueries.mobKillCount(0L, now, serverUUID)); Long mobKills30d = db.query(KillQueries.mobKillCount(monthAgo, now, serverUUID)); @@ -96,9 +96,12 @@ public class PvPPvEJSONParser implements TabJSONParser> { numbers.put("mob_deaths_30d", mobDeaths30d); numbers.put("mob_deaths_7d", mobDeaths7d); - numbers.put("mob_kdr_total", mobDeaths != 0 ? mobKills * 1.0 / mobDeaths : mobKills); - numbers.put("mob_kdr_30d", mobDeaths30d != 0 ? mobKills30d * 1.0 / mobDeaths30d : mobKills30d); - numbers.put("mob_kdr_7d", mobDeaths7d != 0 ? mobKills7d * 1.0 / mobDeaths7d : mobKills7d); + double mobKdr = mobDeaths != 0 ? (double) mobKills / mobDeaths : mobKills; + double mobKdr30d = mobDeaths30d != 0 ? (double) mobKills30d / mobDeaths30d : mobKills30d; + double mobKdr7d = mobDeaths7d != 0 ? (double) mobKills7d / mobDeaths7d : mobKills7d; + numbers.put("mob_kdr_total", decimals.apply(mobKdr)); + numbers.put("mob_kdr_30d", decimals.apply(mobKdr30d)); + numbers.put("mob_kdr_7d", decimals.apply(mobKdr7d)); return numbers; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/Predicates.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/Predicates.java new file mode 100644 index 000000000..d1b6816e3 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/Predicates.java @@ -0,0 +1,37 @@ +/* + * 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 . + */ +package com.djrapitops.plan.utilities; + +import com.djrapitops.plan.data.store.objects.DateHolder; + +import java.util.function.Predicate; + +/** + * Utility class for different Predicates used in the plugin. + * + * @author Rsl1122 + */ +public class Predicates { + + public static Predicate within(long after, long before) { + return holder -> { + long date = holder.getDate(); + return after < date && date <= before; + }; + } + +} diff --git a/Plan/common/src/main/resources/assets/plan/web/js/player-values.js b/Plan/common/src/main/resources/assets/plan/web/js/player-values.js index 65e34c913..816541560 100644 --- a/Plan/common/src/main/resources/assets/plan/web/js/player-values.js +++ b/Plan/common/src/main/resources/assets/plan/web/js/player-values.js @@ -60,6 +60,55 @@ function loadPlayerOverviewValues(json, error) { $(element).find("#data_deaths_7d").text(data.death_count_7d) } +/* This function loads PvP & PvE tab */ +function loadPvPPvEValues(json, error) { + if (error) { + $('#pvp-pve').addClass('forbidden'); // TODO Figure out 403 + return; + } + + tab = $('#pvp-pve'); + + // as Numbers + data = json.kill_data; + element = $(tab).find('#data_numbers'); + + $(element).find('#data_player_kills_total').text(data.player_kills_total); + $(element).find('#data_player_kills_30d').text(data.player_kills_30d); + $(element).find('#data_player_kills_7d').text(data.player_kills_7d); + + $(element).find('#data_player_deaths_total').text(data.player_deaths_total); + $(element).find('#data_player_deaths_30d').text(data.player_deaths_30d); + $(element).find('#data_player_deaths_7d').text(data.player_deaths_7d); + + $(element).find('#data_player_kdr_total').text(data.player_kdr_total); + $(element).find('#data_player_kdr_30d').text(data.player_kdr_30d); + $(element).find('#data_player_kdr_7d').text(data.player_kdr_7d); + + $(element).find('#data_mob_kills_total').text(data.mob_kills_total); + $(element).find('#data_mob_kills_30d').text(data.mob_kills_30d); + $(element).find('#data_mob_kills_7d').text(data.mob_kills_7d); + + $(element).find('#data_mob_deaths_total').text(data.mob_deaths_total); + $(element).find('#data_mob_deaths_30d').text(data.mob_deaths_30d); + $(element).find('#data_mob_deaths_7d').text(data.mob_deaths_7d); + + $(element).find('#data_mob_kdr_total').text(data.mob_kdr_total); + $(element).find('#data_mob_kdr_30d').text(data.mob_kdr_30d); + $(element).find('#data_mob_kdr_7d').text(data.mob_kdr_7d); + + $(element).find('#data_deaths_total').text(data.deaths_total); + $(element).find('#data_deaths_30d').text(data.deaths_30d); + $(element).find('#data_deaths_7d').text(data.deaths_7d); + + // Insights + element = $(tab).find('#data_insights'); + + $(element).find('#data_weapon_1st').text(data.weapon_1st); + $(element).find('#data_weapon_2nd').text(data.weapon_2nd); + $(element).find('#data_weapon_3rd').text(data.weapon_3rd); +} + function createNicknameTableBody(nicknames) { var table = ''; diff --git a/Plan/common/src/main/resources/assets/plan/web/player.html b/Plan/common/src/main/resources/assets/plan/web/player.html index f5ff6a90b..5c7d33efd 100644 --- a/Plan/common/src/main/resources/assets/plan/web/player.html +++ b/Plan/common/src/main/resources/assets/plan/web/player.html @@ -387,7 +387,7 @@ -
+
@@ -411,59 +411,62 @@ class="fa fa-fw fa-campground col-red"> PvP & PvE as Numbers
- +
- - - - + + + + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - + + +
All TimeLast 30 daysLast 7 days
All TimeLast 30 daysLast 7 days
KDR10.025.830.97 Average KDR / Player +
Player Kills34223434
Player Caused Deaths344035
Mob KDR2.167.215.75 Average Mob KDR
Mob Kills1234423
Mob Caused Deaths5464
Deaths35212364
@@ -477,15 +480,13 @@ class="far fa-fw fa-life-ring col-red"> Insights
-
+

Deadliest PvP Weapon - Diamond Sword (7 kills)

+

2nd PvP Weapon - Diamond Axe (4 kills)

+

3rd PvP Weapon - Iron Sword (2 kills)

-

Rage Quits - 4

+

@@ -1302,6 +1303,7 @@ loadSessionAccordion(json, error); loadPlayerKills(json, error); loadPlayerDeaths(json, error); + loadPvPPvEValues(json, error); if (json) { var series = { worldPie: { diff --git a/Plan/common/src/main/resources/assets/plan/web/server.html b/Plan/common/src/main/resources/assets/plan/web/server.html index 8ddb9b99e..84a04a07b 100644 --- a/Plan/common/src/main/resources/assets/plan/web/server.html +++ b/Plan/common/src/main/resources/assets/plan/web/server.html @@ -2,7 +2,6 @@ - @@ -14,14 +13,12 @@ - + - @@ -254,13 +251,15 @@
- - - - + + + + + + @@ -376,13 +375,15 @@
- Comparing 7 days - 7 days before (22.1. - 29.1.)Last 7 days (29.1. - 5.2.)Trend
+ Comparing 7 days + 7 days before (22.1. - 29.1.)Last 7 days (29.1. - 5.2.)Trend
- - - - + + + + + + @@ -512,10 +513,12 @@
- Comparing 15 days - Last 30 daysLast 7 daysLast 24 hours
+ Comparing 15 days + Last 30 daysLast 7 daysLast 24 hours
- - - - + + + + + + @@ -589,10 +592,12 @@
Player Session Started Length Most played World
Player Session Started Length Most played World
- - - - + + + + + +
All TimeLast 30 daysLast 7 days
All TimeLast 30 daysLast 7 days