From c01ae32f1fc463e79ac24e239cf58a42caa16705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Fri, 23 Aug 2024 23:17:02 +0200 Subject: [PATCH] added networking statistics, refactored aggregates back to appliances --- .../java/eu/mhsl/craftattack/spawn/Main.java | 2 +- .../spawn/aggregate/Aggregate.java | 10 --- .../spawn/appliance/Appliance.java | 5 ++ .../displayName/DisplayName.java | 6 +- .../DisplayNameUpdateListener.java | 2 +- .../spawn/appliances/outlawed/Outlawed.java | 22 +++-- .../appliances/projectStart/ProjectStart.java | 6 +- .../listener/NoAdvancementsListener.java | 22 +++++ .../spawn/appliances/tablist/Tablist.java | 17 ++++ .../spawn/util/statistics/NetworkMonitor.java | 81 +++++++++++++++++++ .../spawn/util/text/ComponentUtil.java | 15 ++++ .../spawn/util/text/DataSizeConverter.java | 14 ++++ .../spawn/util/text/NumberAbbreviation.java | 15 ++++ src/main/resources/config.yml | 5 +- 14 files changed, 193 insertions(+), 29 deletions(-) delete mode 100644 src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java rename src/main/java/eu/mhsl/craftattack/spawn/{aggregates => appliances}/displayName/DisplayName.java (92%) rename src/main/java/eu/mhsl/craftattack/spawn/{aggregates => appliances}/displayName/DisplayNameUpdateListener.java (88%) create mode 100644 src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/NoAdvancementsListener.java create mode 100644 src/main/java/eu/mhsl/craftattack/spawn/util/statistics/NetworkMonitor.java create mode 100644 src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java create mode 100644 src/main/java/eu/mhsl/craftattack/spawn/util/text/NumberAbbreviation.java diff --git a/src/main/java/eu/mhsl/craftattack/spawn/Main.java b/src/main/java/eu/mhsl/craftattack/spawn/Main.java index 609b5de..5160274 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/Main.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/Main.java @@ -1,6 +1,6 @@ package eu.mhsl.craftattack.spawn; -import eu.mhsl.craftattack.spawn.aggregates.displayName.DisplayName; +import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName; import eu.mhsl.craftattack.spawn.api.HttpServer; import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; diff --git a/src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java b/src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java deleted file mode 100644 index 368d0ed..0000000 --- a/src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java +++ /dev/null @@ -1,10 +0,0 @@ -package eu.mhsl.craftattack.spawn.aggregate; - -import eu.mhsl.craftattack.spawn.Main; -import eu.mhsl.craftattack.spawn.appliance.Appliance; - -public abstract class Aggregate extends Appliance { - public T queryAppliance(Class clazz) { - return Main.instance().getAppliance(clazz); - } -} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliance/Appliance.java b/src/main/java/eu/mhsl/craftattack/spawn/appliance/Appliance.java index b2fed1c..8c5b59b 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliance/Appliance.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliance/Appliance.java @@ -1,5 +1,6 @@ package eu.mhsl.craftattack.spawn.appliance; +import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.api.HttpServer; import eu.mhsl.craftattack.spawn.config.Configuration; import org.bukkit.Bukkit; @@ -78,6 +79,10 @@ public abstract class Appliance { eventHandlers().forEach(HandlerList::unregisterAll); } + public T queryAppliance(Class clazz) { + return Main.instance().getAppliance(clazz); + } + private void setCommandExecutor(JavaPlugin plugin, String name, ApplianceCommand executor) { PluginCommand command = plugin.getCommand(name); if(command != null && executor != null) { diff --git a/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayName.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java similarity index 92% rename from src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayName.java rename to src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java index a87260e..0451209 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayName.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayName.java @@ -1,6 +1,6 @@ -package eu.mhsl.craftattack.spawn.aggregates.displayName; +package eu.mhsl.craftattack.spawn.appliances.displayName; -import eu.mhsl.craftattack.spawn.aggregate.Aggregate; +import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarkerListener; import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.function.Supplier; -public class DisplayName extends Aggregate { +public class DisplayName extends Appliance { public void update(Player player) { TextColor playerColor = queryAppliance(AdminMarker.class).getPlayerColor(player); List> prefixes = List.of( diff --git a/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayNameUpdateListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayNameUpdateListener.java similarity index 88% rename from src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayNameUpdateListener.java rename to src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayNameUpdateListener.java index b27ccd4..ac5b109 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayNameUpdateListener.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/displayName/DisplayNameUpdateListener.java @@ -1,4 +1,4 @@ -package eu.mhsl.craftattack.spawn.aggregates.displayName; +package eu.mhsl.craftattack.spawn.appliances.displayName; import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; import org.bukkit.event.EventHandler; diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java index 80df01a..1ba833c 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java @@ -1,7 +1,7 @@ package eu.mhsl.craftattack.spawn.appliances.outlawed; import eu.mhsl.craftattack.spawn.Main; -import eu.mhsl.craftattack.spawn.aggregates.displayName.DisplayName; +import eu.mhsl.craftattack.spawn.appliances.displayName.DisplayName; import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliances.whitelist.Whitelist; @@ -33,17 +33,15 @@ public class Outlawed extends Appliance { super("outlawed"); Bukkit.getScheduler().runTaskTimerAsynchronously( Main.instance(), - () -> { - playerStatusMap.forEach((player, status) -> { - if(!player.isOnline()) return; - if(status != Status.FORCED) return; - try { - Main.instance().getAppliance(Whitelist.class).integrityCheck(player); - } catch (DisconnectInfo.Throwable e) { - Bukkit.getScheduler().runTask(Main.instance(), () -> e.getDisconnectScreen().applyKick(player)); - } - }); - }, + () -> playerStatusMap.forEach((player, status) -> { + if(!player.isOnline()) return; + if(status != Status.FORCED) return; + try { + Main.instance().getAppliance(Whitelist.class).integrityCheck(player); + } catch (DisconnectInfo.Throwable e) { + Bukkit.getScheduler().runTask(Main.instance(), () -> e.getDisconnectScreen().applyKick(player)); + } + }), 20*60, 20*60*5 ); diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java index 2440ba6..459ca0b 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java @@ -5,6 +5,7 @@ import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartCancelCommand; import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartCommand; import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartResetCommand; +import eu.mhsl.craftattack.spawn.appliances.projectStart.listener.NoAdvancementsListener; import eu.mhsl.craftattack.spawn.appliances.projectStart.listener.PlayerInvincibleListener; import eu.mhsl.craftattack.spawn.config.Configuration; import eu.mhsl.craftattack.spawn.util.entity.PlayerUtils; @@ -174,7 +175,10 @@ public class ProjectStart extends Appliance { @Override protected @NotNull List eventHandlers() { - return List.of(new PlayerInvincibleListener()); + return List.of( + new PlayerInvincibleListener(), + new NoAdvancementsListener() + ); } @Override diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/NoAdvancementsListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/NoAdvancementsListener.java new file mode 100644 index 0000000..559fd89 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/listener/NoAdvancementsListener.java @@ -0,0 +1,22 @@ +package eu.mhsl.craftattack.spawn.appliances.projectStart.listener; + +import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; +import eu.mhsl.craftattack.spawn.appliances.projectStart.ProjectStart; +import org.bukkit.advancement.Advancement; +import org.bukkit.advancement.AdvancementProgress; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerAdvancementDoneEvent; + +public class NoAdvancementsListener extends ApplianceListener { + @EventHandler + public void onAdvancement(PlayerAdvancementDoneEvent event) { + if(!getAppliance().isEnabled()) return; + event.message(null); + + Advancement advancement = event.getAdvancement(); + AdvancementProgress progress = event.getPlayer().getAdvancementProgress(advancement); + for(String criteria : progress.getAwardedCriteria()) { + progress.revokeCriteria(criteria); + } + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java index a39e266..b7d0b84 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/tablist/Tablist.java @@ -3,6 +3,7 @@ package eu.mhsl.craftattack.spawn.appliances.tablist; import eu.mhsl.craftattack.spawn.Main; import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliances.report.Report; +import eu.mhsl.craftattack.spawn.util.statistics.NetworkMonitor; import eu.mhsl.craftattack.spawn.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.util.IteratorUtil; import eu.mhsl.craftattack.spawn.util.text.RainbowComponent; @@ -14,15 +15,22 @@ import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.util.List; public class Tablist extends Appliance { private final int refreshRate = Ticks.TICKS_PER_SECOND * 3; private final RainbowComponent serverName = new RainbowComponent(" CraftAttack 7 ", 7, 3); + private NetworkMonitor networkMonitor; + + public Tablist() { + super("tablist"); + } @Override public void onEnable() { + this.networkMonitor = new NetworkMonitor(localConfig().getString("interface"), Duration.ofSeconds(1)); Bukkit.getScheduler().runTaskTimerAsynchronously( Main.instance(), () -> IteratorUtil.onlinePlayers(this::updateHeader), @@ -31,6 +39,11 @@ public class Tablist extends Appliance { ); } + @Override + public void onDisable() { + this.networkMonitor.stop(); + } + public void fullUpdate(Player player) { updateHeader(player); updateFooter(player); @@ -43,6 +56,10 @@ public class Tablist extends Appliance { .append(Component.text("mhsl.eu", NamedTextColor.GOLD)).appendNewline().appendNewline() .append(ComponentUtil.getFormattedMSPT()).appendNewline().appendNewline() .append(ComponentUtil.getFormattedPing(player)).appendNewline() + .append(ComponentUtil.getFormattedNetworkStats( + this.networkMonitor.getTraffic(), + this.networkMonitor.getPackets()) + ).appendNewline() ); } diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/statistics/NetworkMonitor.java b/src/main/java/eu/mhsl/craftattack/spawn/util/statistics/NetworkMonitor.java new file mode 100644 index 0000000..95d1fa9 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/util/statistics/NetworkMonitor.java @@ -0,0 +1,81 @@ +package eu.mhsl.craftattack.spawn.util.statistics; + +import eu.mhsl.craftattack.spawn.Main; +import net.kyori.adventure.util.Ticks; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Duration; + +public class NetworkMonitor { + private long previousRxBytes = 0; + private long previousTxBytes = 0; + + private long previousRxPackets = 0; + private long previousTxPackets = 0; + + private final String iFace; + + private long rxBytesLastDuration = 0; + private long txBytesLastDuration = 0; + private long rxPacketsLastDuration = 0; + private long txPacketsLastDuration = 0; + + private final BukkitTask updateTask; + + public NetworkMonitor(String iFace, Duration sampleDuration) { + this.iFace = iFace; + + this.updateTask = Bukkit.getScheduler().runTaskTimerAsynchronously( + Main.instance(), + this::update, + 0, + sampleDuration.getSeconds() * Ticks.TICKS_PER_SECOND + ); + } + + public record Traffic(long rxBytes, long txBytes) {} + public record Packets(long rxCount, long txCount) {} + + public Traffic getTraffic() { + return new Traffic(rxBytesLastDuration, txBytesLastDuration); + } + + public Packets getPackets() { + return new Packets(rxPacketsLastDuration, txPacketsLastDuration); + } + + public void stop() { + this.updateTask.cancel(); + } + + private void update() { + long rxBytes = getNetworkStatistic("rx_bytes"); + long txBytes = getNetworkStatistic("tx_bytes"); + long rxPackets = getNetworkStatistic("rx_packets"); + long txPackets = getNetworkStatistic("tx_packets"); + + this.rxBytesLastDuration = rxBytes - previousRxBytes; + this.txBytesLastDuration = txBytes - previousTxBytes; + this.rxPacketsLastDuration = rxPackets - previousRxPackets; + this.txPacketsLastDuration = txPackets - previousTxPackets; + + this.previousRxBytes = rxBytes; + this.previousTxBytes = txBytes; + this.previousRxPackets = rxPackets; + this.previousTxPackets = txPackets; + } + + private long getNetworkStatistic(String statistic) { + try { + String path = String.format("/sys/class/net/%s/statistics/%s", this.iFace, statistic); + String content = new String(Files.readAllBytes(Paths.get(path))); + return Long.parseLong(content.trim()); + } catch (IOException e) { + throw new RuntimeException("Failed recieving Network statistic", e); + } + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java index 89ed017..11991e6 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/ComponentUtil.java @@ -1,5 +1,6 @@ package eu.mhsl.craftattack.spawn.util.text; +import eu.mhsl.craftattack.spawn.util.statistics.NetworkMonitor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextColor; @@ -86,4 +87,18 @@ public class ComponentUtil { return builder; } + + public static Component getFormattedNetworkStats(NetworkMonitor.Traffic traffic, NetworkMonitor.Packets packets) { + return Component.text() + .append(Component.text( + DataSizeConverter.convertBytesToHumanReadable(traffic.rxBytes()) + " ↓ " + NumberAbbreviation.abbreviateNumber(packets.rxCount()) + "pps", + NamedTextColor.GREEN + )) + .append(Component.text(" | ", NamedTextColor.GRAY)) + .append(Component.text( + DataSizeConverter.convertBytesToHumanReadable(traffic.txBytes()) + " ↑ " + NumberAbbreviation.abbreviateNumber(packets.rxCount()) + "pps", + NamedTextColor.RED + )) + .build(); + } } diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java new file mode 100644 index 0000000..96083f6 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DataSizeConverter.java @@ -0,0 +1,14 @@ +package eu.mhsl.craftattack.spawn.util.text; + +public class DataSizeConverter { + public static String convertBytesToHumanReadable(long bytes) { + double kbits = bytes * 8.0 / 1000.0; + double mbits = kbits / 1000.0; + + if (mbits >= 1) { + return String.format("%.2f Mbit", mbits); + } else { + return String.format("%.2f Kbit", kbits); + } + } +} \ No newline at end of file diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/NumberAbbreviation.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/NumberAbbreviation.java new file mode 100644 index 0000000..97448b2 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/NumberAbbreviation.java @@ -0,0 +1,15 @@ +package eu.mhsl.craftattack.spawn.util.text; + +public class NumberAbbreviation { + public static > String abbreviateNumber(T number) { + double value = number.doubleValue(); + if (value >= 1_000_000) { + return String.format("%.1fM", value / 1_000_000.0); + } else if (value >= 1_000) { + return String.format("%.1fk", value / 1_000.0); + } else { + return number.toString(); + } + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 2fedc56..af22dde 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -45,4 +45,7 @@ playerLimit: maxPlayers: 0 whitelist: - api: https://mhsl.eu/craftattack/api/user \ No newline at end of file + api: https://mhsl.eu/craftattack/api/user + +tablist: + interface: eth0 \ No newline at end of file