diff --git a/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java index 6ac45c7..5b4951e 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java @@ -67,12 +67,28 @@ public abstract class AbstractCNPlayer implements CNPlayer { private final Map trackers = new WeakHashMap<>(); private final ReadWriteLock trackerLock = new ReentrantReadWriteLock(); + private final List otherActionBarFeatures = new ArrayList<>(); protected AbstractCNPlayer(CustomNameplates plugin, Channel channel) { this.plugin = plugin; this.channel = channel; } + @Override + public void acquireActionBar(String id) { + this.otherActionBarFeatures.add(id); + } + + @Override + public void releaseActionBar(String id) { + this.otherActionBarFeatures.remove(id); + } + + @Override + public boolean shouldCNTakeOverActionBar() { + return otherActionBarFeatures.isEmpty(); + } + @Override public List activePlaceholdersToRefresh() { Placeholder[] activePlaceholders = activePlaceholders(); diff --git a/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java index 7038ac8..d86dc9e 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java @@ -181,6 +181,27 @@ public interface CNPlayer { */ Placeholder[] activePlaceholders(); + /** + * Acquires the actionbar, preventing CustomNameplates from taking control of it. + * + * @param id The feature ID requesting the actionbar. + */ + void acquireActionBar(String id); + + /** + * Releases the actionbar, allowing CustomNameplates to take control again if no other features are active. + * + * @param id The feature ID releasing the actionbar. + */ + void releaseActionBar(String id); + + /** + * Checks if CustomNameplates should take control of the actionbar. + * + * @return True if CustomNameplates should take over the actionbar, false otherwise. + */ + boolean shouldCNTakeOverActionBar(); + /** * Retrieves the list of placeholders that need to be refreshed based on their refresh intervals. * diff --git a/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java b/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java index e00fc6f..5101a7d 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/ConfigManager.java @@ -115,6 +115,9 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable { protected boolean chatChatControlRed; protected boolean chatChatty; + protected boolean twDialogue; + protected boolean twCinematic; + protected String configVersion; public ConfigManager(CustomNameplates plugin) { @@ -196,6 +199,9 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable { chatChatControlRed = config.getBoolean("integrations.chat.ChatControlRed", false); chatChatty = config.getBoolean("integrations.chat.Chatty", false); + twDialogue = config.getBoolean("integrations.typewriter.dialogue", true); + twCinematic = config.getBoolean("integrations.typewriter.cinematic", true); + // Packs generateOnStart = !config.getBoolean("resource-pack.disable-generation-on-start", false); namespace = config.getString("resource-pack.namespace", "nameplates"); @@ -365,6 +371,14 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable { return instance.metrics; } + public static boolean twDialogue() { + return instance.twDialogue; + } + + public static boolean twCinematic() { + return instance.twCinematic; + } + public static int defaultPlaceholderRefreshInterval() { return instance.defaultPlaceholderRefreshInterval; } diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/feature/actionbar/ActionBarSender.java b/backend/src/main/java/net/momirealms/customnameplates/backend/feature/actionbar/ActionBarSender.java index 443a819..0f545c0 100644 --- a/backend/src/main/java/net/momirealms/customnameplates/backend/feature/actionbar/ActionBarSender.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/feature/actionbar/ActionBarSender.java @@ -160,6 +160,8 @@ public class ActionBarSender implements Feature { if (isTemporarilyHidden()) return; if (latestContent != null) { updateLastUpdateTime(); + // do not send if other plugins have taken over the actionbar + if (!owner.shouldCNTakeOverActionBar()) return; Object packet = CustomNameplates.getInstance().getPlatform().setActionBarTextPacket(AdventureHelper.miniMessageToMinecraftComponent(latestContent, "nameplates", "actionbar")); CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet); } diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/feature/pack/ResourcePackManagerImpl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/feature/pack/ResourcePackManagerImpl.java index fcc54dd..6180427 100644 --- a/backend/src/main/java/net/momirealms/customnameplates/backend/feature/pack/ResourcePackManagerImpl.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/feature/pack/ResourcePackManagerImpl.java @@ -41,7 +41,6 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.*; public class ResourcePackManagerImpl implements ResourcePackManager { diff --git a/backend/src/main/resources/config.yml b/backend/src/main/resources/config.yml index 3d68d6c..5dddb85 100644 --- a/backend/src/main/resources/config.yml +++ b/backend/src/main/resources/config.yml @@ -36,6 +36,10 @@ integrations: Essentials: false # Integration with Essentials chat ChatControlRed: false # Integration with ChatControlRed Chatty: false # Integration with Chatty + # Let typewriter take over actionbar on certain events + typewriter: + dialogue: true + cinematic: true # Resource Pack Generation Settings: Configure resource pack generation behavior. resource-pack: diff --git a/gradle.properties b/gradle.properties index 893aa9b..9dfb725 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ # Project settings # Rule: [major update].[feature update].[bug fix] -project_version=3.0.15.2 -config_version=34 +project_version=3.0.16 +config_version=35 project_group=net.momirealms # Supported languages diff --git a/platforms/bukkit/compatibility/build.gradle.kts b/platforms/bukkit/compatibility/build.gradle.kts index f640bd5..bb4f1f2 100644 --- a/platforms/bukkit/compatibility/build.gradle.kts +++ b/platforms/bukkit/compatibility/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { compileOnly(files("libs/AdvancedChat-1.3.7.jar")) compileOnly(files("libs/CMIAPI-9.7.4.1.jar")) compileOnly(files("libs/ChatControl-Red-10.28.3.jar")) + compileOnly(files("libs/Typewriter.jar")) compileOnly("net.william278.huskchat:huskchat-bukkit:3.0.4") compileOnly("net.essentialsx:EssentialsX:2.20.1") compileOnly("net.essentialsx:EssentialsXChat:2.20.1") diff --git a/platforms/bukkit/compatibility/libs/Typewriter.jar b/platforms/bukkit/compatibility/libs/Typewriter.jar new file mode 100644 index 0000000..d5ebb21 Binary files /dev/null and b/platforms/bukkit/compatibility/libs/Typewriter.jar differ diff --git a/platforms/bukkit/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/quest/TypeWriterListener.java b/platforms/bukkit/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/quest/TypeWriterListener.java new file mode 100644 index 0000000..f379b00 --- /dev/null +++ b/platforms/bukkit/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/quest/TypeWriterListener.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.quest; + +import com.typewritermc.engine.paper.events.AsyncCinematicEndEvent; +import com.typewritermc.engine.paper.events.AsyncCinematicStartEvent; +import com.typewritermc.engine.paper.events.AsyncDialogueEndEvent; +import com.typewritermc.engine.paper.events.AsyncDialogueStartEvent; +import net.momirealms.customnameplates.api.AbstractCNPlayer; +import net.momirealms.customnameplates.api.ConfigManager; +import net.momirealms.customnameplates.api.CustomNameplates; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class TypeWriterListener implements Listener { + + private final CustomNameplates plugin; + + public TypeWriterListener(CustomNameplates plugin) { + this.plugin = plugin; + } + + @EventHandler(ignoreCancelled = true) + public void onDialogueStart(AsyncDialogueStartEvent event) { + if (!ConfigManager.twDialogue()) return; + Player player = event.getPlayer(); + AbstractCNPlayer cnPlayer = (AbstractCNPlayer) plugin.getPlayer(player.getUniqueId()); + cnPlayer.acquireActionBar("TWDialogue"); + } + + @EventHandler(ignoreCancelled = true) + public void onDialogueEnd(AsyncDialogueEndEvent event) { + if (!ConfigManager.twDialogue()) return; + Player player = event.getPlayer(); + AbstractCNPlayer cnPlayer = (AbstractCNPlayer) plugin.getPlayer(player.getUniqueId()); + cnPlayer.releaseActionBar("TWDialogue"); + } + + @EventHandler(ignoreCancelled = true) + public void onCinematicStart(AsyncCinematicStartEvent event) { + if (!ConfigManager.twCinematic()) return; + Player player = event.getPlayer(); + AbstractCNPlayer cnPlayer = (AbstractCNPlayer) plugin.getPlayer(player.getUniqueId()); + cnPlayer.acquireActionBar("TWCinematic"); + } + + @EventHandler(ignoreCancelled = true) + public void onCinematicEnd(AsyncCinematicEndEvent event) { + if (!ConfigManager.twCinematic()) return; + Player player = event.getPlayer(); + AbstractCNPlayer cnPlayer = (AbstractCNPlayer) plugin.getPlayer(player.getUniqueId()); + cnPlayer.releaseActionBar("TWCinematic"); + } +} diff --git a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java index a72c842..64cddef 100644 --- a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java +++ b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java @@ -40,6 +40,7 @@ import net.momirealms.customnameplates.bukkit.command.BukkitCommandManager; import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExpansion; import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExtraExpansion; import net.momirealms.customnameplates.bukkit.compatibility.cosmetic.MagicCosmeticsHook; +import net.momirealms.customnameplates.bukkit.compatibility.quest.TypeWriterListener; import net.momirealms.customnameplates.bukkit.compatibility.region.WorldGuardRegion; import net.momirealms.customnameplates.bukkit.requirement.BukkitRequirementManager; import net.momirealms.customnameplates.bukkit.scheduler.BukkitSchedulerAdapter; @@ -203,6 +204,10 @@ public class BukkitCustomNameplates extends CustomNameplates implements Listener } catch (Exception ignore) { } } + if (Bukkit.getPluginManager().isPluginEnabled("Typewriter")) { + TypeWriterListener listener = new TypeWriterListener(this); + Bukkit.getPluginManager().registerEvents(listener, this.getBootstrap()); + } if (VersionHelper.isFolia()) { this.foliaTrackerTask = getScheduler().asyncRepeating(() -> { diff --git a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java index 624409b..fe45711 100644 --- a/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java +++ b/platforms/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitPlatform.java @@ -98,6 +98,7 @@ public class BukkitPlatform implements Platform { registerPacketConsumer((player, event, packet) -> { if (!ConfigManager.actionbarModule()) return; if (!ConfigManager.catchOtherActionBar()) return; + if (!player.shouldCNTakeOverActionBar()) return; try { // some plugins would send null to clear the actionbar, what a bad solution Object component = Optional.ofNullable(Reflections.field$ClientboundSetActionBarTextPacket$text.get(packet)).orElse(Reflections.instance$Component$empty); @@ -119,6 +120,7 @@ public class BukkitPlatform implements Platform { registerPacketConsumer((player, event, packet) -> { if (!ConfigManager.actionbarModule()) return; if (!ConfigManager.catchOtherActionBar()) return; + if (!player.shouldCNTakeOverActionBar()) return; try { boolean actionBar = (boolean) Reflections.field$ClientboundSystemChatPacket$overlay.get(packet); if (actionBar) { diff --git a/platforms/bukkit/src/main/resources/plugin.yml b/platforms/bukkit/src/main/resources/plugin.yml index ca5b094..3334bb2 100644 --- a/platforms/bukkit/src/main/resources/plugin.yml +++ b/platforms/bukkit/src/main/resources/plugin.yml @@ -17,6 +17,7 @@ softdepend: - EssentialsChat - ChatControlRed - WorldGuard + - Typewriter permissions: nameplates.command.equip: default: true