added settings with technicalTab toggle

This commit is contained in:
Elias Müller 2024-08-24 01:37:02 +02:00
parent c01ae32f1f
commit 70058c552d
10 changed files with 304 additions and 24 deletions

View File

@ -17,6 +17,7 @@ import eu.mhsl.craftattack.spawn.appliances.help.Help;
import eu.mhsl.craftattack.spawn.appliances.playerlimit.PlayerLimit;
import eu.mhsl.craftattack.spawn.appliances.report.Report;
import eu.mhsl.craftattack.spawn.appliances.restart.Restart;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.tablist.Tablist;
import eu.mhsl.craftattack.spawn.appliances.titleClear.TitleClear;
import eu.mhsl.craftattack.spawn.appliances.whitelist.Whitelist;
@ -59,7 +60,8 @@ public final class Main extends JavaPlugin {
new DisplayName(),
new Debug(),
new Fleischerchest(),
new CustomAdvancements()
new CustomAdvancements(),
new Settings()
);
Bukkit.getLogger().info("Loading appliances...");

View File

@ -0,0 +1,81 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.Setting;
import eu.mhsl.craftattack.spawn.appliances.settings.settings.TechnicalTablistSetting;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.WeakHashMap;
public class Settings extends Appliance {
public enum Key {
TechnicalTab,
}
public record OpenSettingsInventory(Inventory inventory, List<Setting<?>> settings) {}
private final WeakHashMap<Player, OpenSettingsInventory> openSettingsInventories = new WeakHashMap<>();
private final WeakHashMap<Player, List<Setting<?>>> settingsCache = new WeakHashMap<>();
private List<Setting<?>> getSettings(Player player) {
if(settingsCache.containsKey(player)) return settingsCache.get(player);
List<Setting<?>> settings = List.of(
new TechnicalTablistSetting()
);
settings.forEach(setting -> setting.initializeFromPlayer(player));
this.settingsCache.put(player, settings);
return settings;
}
public <T> T getSetting(Player player, Key key, Class<T> clazz) {
Setting<?> setting = getSettings(player).stream()
.filter(s -> s.getKey().equals(key))
.findFirst()
.orElseThrow();
if(!clazz.equals(setting.dataType())) throw new IllegalStateException("Tried to retrieve Setting with Datatype " + clazz.getSimpleName() + " but expected " + setting.dataType().getSimpleName());
if(!clazz.isInstance(setting.state())) throw new ClassCastException(clazz.getSimpleName() + " is not an instance of " + setting.dataType().getSimpleName());
return clazz.cast(setting.state());
}
public void openSettings(Player player) {
Inventory inventory = Bukkit.createInventory(null, 9, Component.text("Einstellungen"));
List<Setting<?>> settings = getSettings(player);
settings.forEach(setting -> inventory.addItem(setting.buildItem()));
player.openInventory(inventory);
this.openSettingsInventories.put(player, new OpenSettingsInventory(inventory, settings));
}
public void onSettingsClose(Player player) {
if(!openSettingsInventories.containsKey(player)) return;
openSettingsInventories.remove(player);
player.updateInventory();
}
public boolean hasSettingsOpen(Player player) {
return this.openSettingsInventories.containsKey(player);
}
public OpenSettingsInventory getOpenInventory(Player player) {
if(!hasSettingsOpen(player)) throw new RuntimeException("Cannot retrieve data from closed Settings inventory!");
return this.openSettingsInventories.get(player);
}
@Override
protected @NotNull List<Listener> eventHandlers() {
return List.of(new SettingsInventoryListener());
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(new SettingsCommand());
}
}

View File

@ -0,0 +1,17 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class SettingsCommand extends ApplianceCommand.PlayerChecked<Settings> {
public SettingsCommand() {
super("settings");
}
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
getAppliance().openSettings(getPlayer());
}
}

View File

@ -0,0 +1,30 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
public class SettingsInventoryListener extends ApplianceListener<Settings> {
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
Player player = (Player) event.getWhoClicked();
if(!getAppliance().hasSettingsOpen(player)) return;
event.setCancelled(true);
Settings.OpenSettingsInventory openInventory = getAppliance().getOpenInventory(player);
openInventory.settings().stream()
.filter(setting -> setting.buildItem().equals(event.getCurrentItem()))
.findFirst()
.ifPresent(setting -> {
setting.triggerChange(player);
openInventory.inventory().setItem(event.getSlot(), setting.buildItem());
});
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
getAppliance().onSettingsClose((Player) event.getPlayer());
}
}

View File

@ -0,0 +1,59 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.List;
public abstract class BoolSetting extends Setting<Boolean> {
private boolean state;
public BoolSetting(Settings.Key key) {
super(key);
}
protected abstract String title();
protected abstract String description();
@Override
public void fromStorage(PersistentDataContainer container) {
this.state = Boolean.TRUE.equals(container.get(getNamespacedKey(), PersistentDataType.BOOLEAN));
}
@Override
protected void toStorage(PersistentDataContainer container, Boolean value) {
container.set(getNamespacedKey(), PersistentDataType.BOOLEAN, value);
}
@Override
public ItemMeta buildMeta(ItemMeta meta) {
meta.displayName(Component.text(title(), NamedTextColor.WHITE));
meta.lore(List.of(
Component.empty()
.append(Component.text("Status: ", NamedTextColor.DARK_GRAY))
.append(Component.text(this.state ? "An" : "Aus", this.state ? NamedTextColor.GREEN : NamedTextColor.RED)),
Component.empty(),
Component.text(description(), NamedTextColor.GRAY)
));
return meta;
}
@Override
protected void change() {
this.state = !this.state;
}
@Override
public Class<?> dataType() {
return Boolean.class;
}
@Override
public Boolean state() {
return state;
}
}

View File

@ -0,0 +1,49 @@
package eu.mhsl.craftattack.spawn.appliances.settings.datatypes;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
public abstract class Setting<TDataType> {
private final Settings.Key key;
public Setting(Settings.Key key) {
this.key = key;
}
public NamespacedKey getNamespacedKey() {
return new NamespacedKey(Main.instance(), key.name());
}
public Settings.Key getKey() {
return key;
}
public void initializeFromPlayer(Player p) {
fromStorage(p.getPersistentDataContainer());
}
public void triggerChange(Player p) {
this.change();
toStorage(p.getPersistentDataContainer(), this.state());
}
public ItemStack buildItem() {
ItemStack stack = new ItemStack(icon(), 1);
stack.setItemMeta(buildMeta(stack.getItemMeta()));
return stack;
}
protected abstract Material icon();
public abstract ItemMeta buildMeta(ItemMeta meta);
protected abstract void change();
protected abstract void fromStorage(PersistentDataContainer container);
protected abstract void toStorage(PersistentDataContainer container, TDataType value);
public abstract Class<?> dataType();
public abstract TDataType state();
}

View File

@ -0,0 +1,26 @@
package eu.mhsl.craftattack.spawn.appliances.settings.settings;
import eu.mhsl.craftattack.spawn.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.BoolSetting;
import org.bukkit.Material;
public class TechnicalTablistSetting extends BoolSetting {
public TechnicalTablistSetting() {
super(Settings.Key.TechnicalTab);
}
@Override
protected String title() {
return "Technische Informationen";
}
@Override
protected String description() {
return "Zeige erweiterte Informationen und Statistiken in der Tabliste an";
}
@Override
protected Material icon() {
return Material.COMMAND_BLOCK_MINECART;
}
}

View File

@ -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.appliances.settings.Settings;
import eu.mhsl.craftattack.spawn.util.statistics.NetworkMonitor;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import eu.mhsl.craftattack.spawn.util.IteratorUtil;
@ -20,7 +21,6 @@ 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;
@ -30,12 +30,13 @@ public class Tablist extends Appliance {
@Override
public void onEnable() {
int tabRefreshRate = 3;
this.networkMonitor = new NetworkMonitor(localConfig().getString("interface"), Duration.ofSeconds(1));
Bukkit.getScheduler().runTaskTimerAsynchronously(
Main.instance(),
() -> IteratorUtil.onlinePlayers(this::updateHeader),
refreshRate,
refreshRate
tabRefreshRate * Ticks.TICKS_PER_SECOND,
tabRefreshRate * Ticks.TICKS_PER_SECOND
);
}
@ -50,17 +51,23 @@ public class Tablist extends Appliance {
}
private void updateHeader(Player player) {
player.sendPlayerListHeader(
Component.newline()
boolean detailedInfo = queryAppliance(Settings.class).getSetting(player, Settings.Key.TechnicalTab, Boolean.class);
Component header = Component.newline()
.append(serverName.getRainbowState()).appendNewline()
.append(Component.text("mhsl.eu", NamedTextColor.GOLD)).appendNewline().appendNewline()
.append(ComponentUtil.getFormattedMSPT()).appendNewline().appendNewline()
.append(ComponentUtil.getFormattedTickTimes(detailedInfo)).appendNewline();
if(detailedInfo) {
header = header
.appendNewline()
.append(ComponentUtil.getFormattedPing(player)).appendNewline()
.append(ComponentUtil.getFormattedNetworkStats(
this.networkMonitor.getTraffic(),
this.networkMonitor.getPackets())
).appendNewline()
);
).appendNewline();
}
player.sendPlayerListHeader(header);
}
private void updateFooter(Player player) {

View File

@ -2,6 +2,8 @@ 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.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
@ -39,7 +41,7 @@ public class ComponentUtil {
.build();
}
public static Component getFormattedMSPT() {
public static Component getFormattedTickTimes(boolean detailed) {
long[] times = Bukkit.getServer().getTickTimes();
float mspt = ((float) Arrays.stream(times).sum() / times.length) * 1.0E-6f;
float roundedMspt = Math.round(mspt * 100f) / 100f;
@ -50,12 +52,18 @@ public class ComponentUtil {
TextColor percentageColor = ColorUtil.mapGreenToRed(loadPercentage, 80, 100, true);
TextColor tpsColor = ColorUtil.mapGreenToRed(roundedTPS, 15, 20, false);
return Component.text()
ComponentBuilder<TextComponent, TextComponent.Builder> tickTimes = Component.text()
.append(Component.text("Serverlast: ", NamedTextColor.GRAY))
.append(Component.text(loadPercentage + "% ", percentageColor))
.appendNewline()
.appendNewline();
if(detailed) {
tickTimes
.append(Component.text(roundedMspt + "mspt", msptColor))
.append(Component.text(" | ", NamedTextColor.GRAY))
.append(Component.text(" | ", NamedTextColor.GRAY));
}
return tickTimes
.append(Component.text(roundedTPS + "tps", tpsColor))
.build();
}

View File

@ -36,3 +36,4 @@ commands:
kick:
panicBan:
vogelfrei:
settings: