diff --git a/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java b/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java index 6a30da0..ba87099 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java +++ b/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java @@ -5,7 +5,6 @@ import com.triassic.geyserdebuginfo.configuration.Configuration; import com.triassic.geyserdebuginfo.configuration.ConfigurationContainer; import com.triassic.geyserdebuginfo.listener.PlayerJoinListener; -import com.triassic.geyserdebuginfo.manager.BossBarManager; import com.triassic.geyserdebuginfo.manager.PlaceholderManager; import com.triassic.geyserdebuginfo.manager.PlayerDataManager; import com.triassic.geyserdebuginfo.placeholder.modifiers.MathModifierProvider; @@ -34,8 +33,6 @@ public class GeyserDebugInfo implements Extension { @Getter private Configuration config; @Getter - private BossBarManager bossBarManager; - @Getter private PlayerDataManager playerDataManager; @Getter private PlaceholderManager placeholderManager; @@ -76,7 +73,6 @@ public void onPreInitialize(GeyserPreInitializeEvent event) { this.playerDataManager = new PlayerDataManager(this.dataFolder().toFile(), logger(), false); this.placeholderManager = new PlaceholderManager(); - this.bossBarManager = new BossBarManager(this); this.eventBus().register(new PlayerJoinListener(this)); Stream.of( @@ -102,7 +98,6 @@ public void onDefineCommands(GeyserDefineCommandsEvent event) { @Subscribe public void onShutdown(GeyserShutdownEvent event) { - bossBarManager.shutdown(); playerDataManager.savePlayerData(); } diff --git a/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java b/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java index 6d8577d..570d4f1 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java +++ b/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java @@ -2,35 +2,34 @@ import com.triassic.geyserdebuginfo.GeyserDebugInfo; import com.triassic.geyserdebuginfo.command.AbstractCommand; -import com.triassic.geyserdebuginfo.manager.BossBarManager; +import com.triassic.geyserdebuginfo.display.DisplayType; +import com.triassic.geyserdebuginfo.manager.DisplayManager; import com.triassic.geyserdebuginfo.manager.PlayerDataManager; import org.geysermc.geyser.api.command.Command; import org.geysermc.geyser.api.command.CommandSource; -import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.jetbrains.annotations.NotNull; public class ToggleCommand extends AbstractCommand { - private final BossBarManager bossBarManager; + private final GeyserDebugInfo instance; private final PlayerDataManager playerDataManager; + private final DisplayManager displayManager; public ToggleCommand(final GeyserDebugInfo instance) { super(instance, "toggle", "Toggle the display of the F3 debug menu", true, true); - this.bossBarManager = instance.getBossBarManager(); + this.instance = instance; this.playerDataManager = instance.getPlayerDataManager(); + this.displayManager = new DisplayManager(instance); } @Override protected void execute(@NotNull CommandSource source, @NotNull Command command, @NotNull String[] args) { final GeyserSession session = (GeyserSession) source.connection(); - final SessionPlayerEntity player = session.getPlayerEntity(); - if (player != null && !playerDataManager.isF3Enabled(player.getUuid())) { - bossBarManager.createBossBar(player); - } else { - bossBarManager.removeBossBar(player); - playerDataManager.setF3Enabled(player.getUuid(), false); + switch (args[0]) { + case "actionbar" -> displayManager.subscribePlayer(session, DisplayType.ACTIONBAR); + case "bossbar" -> displayManager.subscribePlayer(session, DisplayType.BOSSBAR); } } } diff --git a/src/main/java/com/triassic/geyserdebuginfo/display/Display.java b/src/main/java/com/triassic/geyserdebuginfo/display/Display.java new file mode 100644 index 0000000..fa3111e --- /dev/null +++ b/src/main/java/com/triassic/geyserdebuginfo/display/Display.java @@ -0,0 +1,52 @@ +package com.triassic.geyserdebuginfo.display; + +import lombok.Getter; +import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public abstract class Display { + + protected final GeyserSession session; + @Getter + protected final DisplayType displayType; + protected final long refreshInterval; + private ScheduledFuture updateTask; + + public Display(@NotNull GeyserSession session, @NotNull DisplayType displayType, long refreshInterval) { + this.session = session; + this.displayType = displayType; + this.refreshInterval = refreshInterval; + } + + /** + * Updates the display with the given text. + */ + public abstract void updateDisplay(); + + /** + * Removes the display from the player. + */ + public abstract void removeDisplay(); + + /** + * Starts the scheduled update task for the display. + */ + public void startUpdating(ScheduledExecutorService executor) { + updateTask = executor.scheduleAtFixedRate(this::updateDisplay, 0, refreshInterval, TimeUnit.MILLISECONDS); + } + + /** + * Stops the scheduled update task for the display. + */ + public void stopUpdating() { + if (updateTask != null) { + updateTask.cancel(false); + updateTask = null; + } + removeDisplay(); + } +} diff --git a/src/main/java/com/triassic/geyserdebuginfo/display/DisplayType.java b/src/main/java/com/triassic/geyserdebuginfo/display/DisplayType.java new file mode 100644 index 0000000..12ec382 --- /dev/null +++ b/src/main/java/com/triassic/geyserdebuginfo/display/DisplayType.java @@ -0,0 +1,6 @@ +package com.triassic.geyserdebuginfo.display; + +public enum DisplayType { + ACTIONBAR, + BOSSBAR +} diff --git a/src/main/java/com/triassic/geyserdebuginfo/display/displays/ActionBarDisplay.java b/src/main/java/com/triassic/geyserdebuginfo/display/displays/ActionBarDisplay.java new file mode 100644 index 0000000..4b9a73b --- /dev/null +++ b/src/main/java/com/triassic/geyserdebuginfo/display/displays/ActionBarDisplay.java @@ -0,0 +1,42 @@ +package com.triassic.geyserdebuginfo.display.displays; + +import com.triassic.geyserdebuginfo.GeyserDebugInfo; +import com.triassic.geyserdebuginfo.display.Display; +import com.triassic.geyserdebuginfo.display.DisplayType; +import com.triassic.geyserdebuginfo.manager.PlaceholderManager; +import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; +import org.geysermc.geyser.session.GeyserSession; +import org.jetbrains.annotations.NotNull; + +public class ActionBarDisplay extends Display { + + private final GeyserDebugInfo instance; + private final SetTitlePacket titlePacket; + private final PlaceholderManager placeholderManager; + + public ActionBarDisplay(GeyserDebugInfo instance, @NotNull GeyserSession session) { + super(session, DisplayType.ACTIONBAR, 50); + + this.titlePacket = new SetTitlePacket(); + titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); + titlePacket.setFadeOutTime(1); + titlePacket.setStayTime(2); + titlePacket.setXuid(""); + titlePacket.setPlatformOnlineId(""); + + this.instance = instance; + this.placeholderManager = instance.getPlaceholderManager(); + } + + @Override + public void updateDisplay() { + titlePacket.setText(placeholderManager.setPlaceholders(session, instance.getConfig().getDisplay().getActionBar().getText())); + session.sendUpstreamPacket(titlePacket); + } + + @Override + public void removeDisplay() { + titlePacket.setText(""); + session.sendUpstreamPacket(titlePacket); + } +} diff --git a/src/main/java/com/triassic/geyserdebuginfo/display/displays/BossBarDisplay.java b/src/main/java/com/triassic/geyserdebuginfo/display/displays/BossBarDisplay.java new file mode 100644 index 0000000..29b1beb --- /dev/null +++ b/src/main/java/com/triassic/geyserdebuginfo/display/displays/BossBarDisplay.java @@ -0,0 +1,44 @@ +package com.triassic.geyserdebuginfo.display.displays; + +import com.triassic.geyserdebuginfo.GeyserDebugInfo; +import com.triassic.geyserdebuginfo.display.Display; +import com.triassic.geyserdebuginfo.display.DisplayType; +import com.triassic.geyserdebuginfo.manager.PlaceholderManager; +import net.kyori.adventure.text.Component; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.session.cache.BossBar; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class BossBarDisplay extends Display { + + private final GeyserDebugInfo instance; + private final BossBar bossBar; + private final PlaceholderManager placeholderManager; + + public BossBarDisplay(GeyserDebugInfo instance, @NotNull GeyserSession session, long entityId) { + super(session, DisplayType.BOSSBAR, 50); + this.bossBar = new BossBar(session, entityId, Component.empty(), 1.0f, 1, 0, 0); + session.getEntityCache().addBossBar(session.playerUuid(), bossBar); + + this.instance = instance; + this.placeholderManager = instance.getPlaceholderManager(); + } + + @Override + public void updateDisplay() { + List displayFormat = instance.getConfig().getDisplay().getBossBar().getText(); + String displayText = displayFormat.stream() + .map(line -> placeholderManager.setPlaceholders(session, line)) + .collect(Collectors.joining("\n")); + + bossBar.updateTitle(Component.text(displayText)); + } + + @Override + public void removeDisplay() { + bossBar.removeBossBar(); + } +} diff --git a/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java b/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java index c343b75..3b84d9e 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java +++ b/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java @@ -1,7 +1,6 @@ package com.triassic.geyserdebuginfo.listener; import com.triassic.geyserdebuginfo.GeyserDebugInfo; -import com.triassic.geyserdebuginfo.manager.BossBarManager; import com.triassic.geyserdebuginfo.manager.PlayerDataManager; import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; @@ -9,39 +8,29 @@ import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; -/** - * This class is responsible for listening for player connection events. - */ public class PlayerJoinListener { - private final BossBarManager bossBarManager; private final PlayerDataManager playerDataManager; public PlayerJoinListener( final GeyserDebugInfo instance ) { - this.bossBarManager = instance.getBossBarManager(); this.playerDataManager = instance.getPlayerDataManager(); } - /** - * Called when a player joins. - */ @Subscribe public void onJoin(final SessionJoinEvent event) { final GeyserSession session = (GeyserSession) event.connection(); final SessionPlayerEntity player = session.getPlayerEntity(); - if (playerDataManager.isF3Enabled(player.getUuid())) - bossBarManager.createBossBar(player); + if (playerDataManager.isF3Enabled(player.getUuid())) { + // TODO: Re-implement this. + } } - /** - * Called when a player disconnects. - */ @Subscribe public void onDisconnect(final SessionDisconnectEvent event) { final GeyserSession session = (GeyserSession) event.connection(); - bossBarManager.removeBossBar(session.getPlayerEntity(), false); + // TODO: Remove Bossbar } } diff --git a/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java b/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java deleted file mode 100644 index bc18ce2..0000000 --- a/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.triassic.geyserdebuginfo.manager; - -import com.triassic.geyserdebuginfo.GeyserDebugInfo; -import net.kyori.adventure.text.Component; -import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.session.cache.BossBar; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -public class BossBarManager { - - private final GeyserDebugInfo instance; - private final PlaceholderManager placeholderManager; - private final PlayerDataManager playerDataManager; - private final HashMap bossBars; - private final ScheduledExecutorService executor; - - public BossBarManager( - final GeyserDebugInfo instance - ) { - this.instance = instance; - this.playerDataManager = instance.getPlayerDataManager(); - this.placeholderManager = instance.getPlaceholderManager(); - this.bossBars = new HashMap<>(); - this.executor = Executors.newSingleThreadScheduledExecutor(); - - executor.scheduleAtFixedRate(this::updateAllBossBars, 0, instance.getConfig().getDisplay().getBossBar().getRefreshInterval(), TimeUnit.MILLISECONDS); - } - - public void createBossBar(final @NotNull SessionPlayerEntity player) { - final GeyserSession session = player.getSession(); - - if (bossBars.containsKey(player)) - return; - - final long entityId = session.getEntityCache().getNextEntityId().incrementAndGet(); - BossBar bossBar = new BossBar(session, entityId, Component.empty(), 1.0f, 1, 0, 0); - player.getSession().getEntityCache().addBossBar(player.getUuid(), bossBar); - playerDataManager.setF3Enabled(player.getUuid(), true); - bossBars.put(player, bossBar); - } - - public void removeBossBar(final @NotNull SessionPlayerEntity player) { - removeBossBar(player, true); - } - - public void removeBossBar(final @NotNull SessionPlayerEntity player, boolean updatePlayerData) { - BossBar bossBar = bossBars.remove(player); - if (bossBar != null) - bossBar.removeBossBar(); - - if (updatePlayerData) - playerDataManager.setF3Enabled(player.getUuid(), false); - } - - private void updateAllBossBars() { - for (Map.Entry entry : bossBars.entrySet()) { - updateBossBar(entry.getKey()); - } - } - - private void updateBossBar(final @NotNull SessionPlayerEntity player) { - BossBar bossBar = bossBars.get(player); - - if (bossBar != null) { - List displayFormat = instance.getConfig().getDisplay().getBossBar().getText(); - - String displayText = displayFormat.stream() - .map(line -> placeholderManager.setPlaceholders(player.getSession(), line)) - .collect(Collectors.joining("\n")); - - bossBar.updateTitle(Component.text(displayText)); - } - } - - public void shutdown() { - executor.shutdown(); - } -} \ No newline at end of file diff --git a/src/main/java/com/triassic/geyserdebuginfo/manager/DisplayManager.java b/src/main/java/com/triassic/geyserdebuginfo/manager/DisplayManager.java new file mode 100644 index 0000000..fc61bf4 --- /dev/null +++ b/src/main/java/com/triassic/geyserdebuginfo/manager/DisplayManager.java @@ -0,0 +1,71 @@ +package com.triassic.geyserdebuginfo.manager; + +import com.triassic.geyserdebuginfo.GeyserDebugInfo; +import com.triassic.geyserdebuginfo.display.Display; +import com.triassic.geyserdebuginfo.display.DisplayType; +import com.triassic.geyserdebuginfo.display.displays.ActionBarDisplay; +import com.triassic.geyserdebuginfo.display.displays.BossBarDisplay; +import org.geysermc.geyser.session.GeyserSession; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +public class DisplayManager { + + private final GeyserDebugInfo instance; + private final Map> activeDisplays; + private final ScheduledExecutorService executor; + private final ExecutorService asyncExecutor; + + public DisplayManager(GeyserDebugInfo instance) { + this.instance = instance; + this.activeDisplays = new ConcurrentHashMap<>(); + this.executor = Executors.newScheduledThreadPool(10); + this.asyncExecutor = Executors.newCachedThreadPool(); + } + + public void subscribePlayer(final GeyserSession session, final DisplayType displayType) { + asyncExecutor.submit(() -> { + Display display = createDisplay(session, displayType); + activeDisplays.computeIfAbsent(session, k -> ConcurrentHashMap.newKeySet()).add(display); + display.startUpdating(executor); + }); + } + + public void unsubscribePlayer(final GeyserSession session, final DisplayType displayType) { + asyncExecutor.submit(() -> { + Set displays = activeDisplays.get(session); + if (displays != null) { + displays.stream() + .filter(display -> display.getDisplayType() == displayType) + .findFirst() + .ifPresent(display -> { + displays.remove(display); + display.stopUpdating(); + if (displays.isEmpty()) { + activeDisplays.remove(session); + } + }); + } + }); + } + + private Display createDisplay(final GeyserSession session, final DisplayType displayType) { + return switch (displayType) { + case ACTIONBAR -> new ActionBarDisplay(instance, session); + case BOSSBAR -> { + long entityId = session.getEntityCache().getNextEntityId().incrementAndGet(); + yield new BossBarDisplay(instance, session, entityId); + } + }; + } + + public void shutdown() { + executor.shutdown(); + asyncExecutor.shutdown(); + } +}