diff --git a/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/HologramConfiguration.java b/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/HologramConfiguration.java index 46355090..cc99e188 100644 --- a/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/HologramConfiguration.java +++ b/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/HologramConfiguration.java @@ -73,4 +73,13 @@ public interface HologramConfiguration { * @return The hologram visibility update interval in milliseconds. */ int getUpdateVisibilityInterval(); + + /** + * Returns the interval at which text holograms refresh dynamic text updates. + * + * @return The hologram text refresh interval in milliseconds. + */ + default int getHologramUpdateInterval() { + return 200; + } } diff --git a/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/hologram/Hologram.java b/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/hologram/Hologram.java index ba19bacd..c6c10732 100644 --- a/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/hologram/Hologram.java +++ b/plugins/fancyholograms-v2/api/src/main/java/de/oliver/fancyholograms/api/hologram/Hologram.java @@ -1,5 +1,7 @@ package de.oliver.fancyholograms.api.hologram; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import de.oliver.fancyholograms.api.data.HologramData; import de.oliver.fancyholograms.api.data.TextHologramData; import de.oliver.fancyholograms.api.data.property.Visibility; @@ -16,6 +18,7 @@ import org.lushplugins.chatcolorhandler.paper.PaperColor; import java.util.*; +import java.util.concurrent.TimeUnit; /** * Abstract base class for creating, updating, and managing holograms. @@ -40,6 +43,12 @@ public abstract class Hologram { * Set of UUIDs of players to whom the hologram is currently shown. */ protected final @NotNull Set viewers = new HashSet<>(); + private static final UUID NULL_PLAYER_KEY = new UUID(0L, 0L); + private final Cache cachedTextPerPlayer = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.SECONDS) + .maximumSize(512) + .build(); + private String lastRawText = ""; protected Hologram(@NotNull final HologramData data) { this.data = data; @@ -184,6 +193,7 @@ public final void updateHologram() { * Use {@link #forceUpdate()} if this hologram is not registered to the HologramManager. */ public final void queueUpdate() { + clearTextCache(); data.setHasChanges(true); } @@ -355,13 +365,32 @@ public final Component getShownText(@Nullable final Player player) { return null; } - var text = String.join("\n", textData.getText()); + final String rawText = String.join("\n", textData.getText()); + + if (!rawText.equals(lastRawText)) { + cachedTextPerPlayer.invalidateAll(); + lastRawText = rawText; + } if (Bukkit.isStopping()) { - return MiniMessage.miniMessage().deserialize(text); + return MiniMessage.miniMessage().deserialize(rawText); + } + + final UUID cacheKey = player != null ? player.getUniqueId() : NULL_PLAYER_KEY; + final Component cached = cachedTextPerPlayer.getIfPresent(cacheKey); + if (cached != null) { + return cached; } - return PaperColor.handler().translate(text, player); + final Component translated = PaperColor.handler().translate(rawText, player); + cachedTextPerPlayer.put(cacheKey, translated); + return translated; + } + + @ApiStatus.Internal + public void clearTextCache() { + cachedTextPerPlayer.invalidateAll(); + lastRawText = ""; } @Override diff --git a/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/FancyHologramsConfiguration.java b/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/FancyHologramsConfiguration.java index 5cf71d1a..f91cf607 100644 --- a/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/FancyHologramsConfiguration.java +++ b/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/FancyHologramsConfiguration.java @@ -26,19 +26,21 @@ public final class FancyHologramsConfiguration implements HologramConfiguration private static final String CONFIG_VISIBILITY_DISTANCE = "visibility_distance"; private static final String CONFIG_REGISTER_COMMANDS = "register_commands"; private static final String CONFIG_UPDATE_VISIBILITY_INTERVAL = "update_visibility_interval"; + private static final String CONFIG_HOLOGRAM_UPDATE_INTERVAL = "performance.hologram_update_interval_ms"; private static final String CONFIG_REPORT_ERRORS_TO_SENTRY = "report_errors_to_sentry"; private static final String CONFIG_VERSION = "config_version"; - private static final Map> CONFIG_COMMENTS = Map.of( - CONFIG_VERSION, List.of("Config version, do not modify."), - CONFIG_AUTOSAVE_ENABLED, List.of("Whether autosave is enabled."), - CONFIG_AUTOSAVE_INTERVAL, List.of("The interval at which autosave is performed in minutes."), - CONFIG_SAVE_ON_CHANGED, List.of("Whether the plugin should save holograms when they are changed."), - CONFIG_LOG_LEVEL, List.of("The log level for the plugin (DEBUG, INFO, WARN, ERROR)."), - CONFIG_LOG_ON_WORLD_LOAD, List.of("Whether hologram loading should be logged on world loading. Disable this if you load worlds dynamically to prevent console spam."), - CONFIG_VERSION_NOTIFICATIONS, List.of("Whether the plugin should send notifications for new updates."), - CONFIG_VISIBILITY_DISTANCE, List.of("The default visibility distance for holograms."), - CONFIG_REGISTER_COMMANDS, List.of("Whether the plugin should register its commands."), - CONFIG_UPDATE_VISIBILITY_INTERVAL, List.of("The interval at which hologram visibility is updated in ticks.") + private static final Map> CONFIG_COMMENTS = Map.ofEntries( + Map.entry(CONFIG_VERSION, List.of("Config version, do not modify.")), + Map.entry(CONFIG_AUTOSAVE_ENABLED, List.of("Whether autosave is enabled.")), + Map.entry(CONFIG_AUTOSAVE_INTERVAL, List.of("The interval at which autosave is performed in minutes.")), + Map.entry(CONFIG_SAVE_ON_CHANGED, List.of("Whether the plugin should save holograms when they are changed.")), + Map.entry(CONFIG_LOG_LEVEL, List.of("The log level for the plugin (DEBUG, INFO, WARN, ERROR).")), + Map.entry(CONFIG_LOG_ON_WORLD_LOAD, List.of("Whether hologram loading should be logged on world loading. Disable this if you load worlds dynamically to prevent console spam.")), + Map.entry(CONFIG_VERSION_NOTIFICATIONS, List.of("Whether the plugin should send notifications for new updates.")), + Map.entry(CONFIG_VISIBILITY_DISTANCE, List.of("The default visibility distance for holograms.")), + Map.entry(CONFIG_REGISTER_COMMANDS, List.of("Whether the plugin should register its commands.")), + Map.entry(CONFIG_UPDATE_VISIBILITY_INTERVAL, List.of("The interval at which hologram visibility is updated in ticks.")), + Map.entry(CONFIG_HOLOGRAM_UPDATE_INTERVAL, List.of("The interval at which text holograms refresh placeholder updates in milliseconds. Lower values are more responsive but use more CPU.")) ); /** * Indicates whether autosave is enabled. @@ -78,6 +80,10 @@ public final class FancyHologramsConfiguration implements HologramConfiguration * The interval at which hologram visibility is updated. */ private int updateVisibilityInterval; + /** + * The interval at which text holograms refresh dynamic text updates. + */ + private int hologramUpdateInterval; private void updateChecker(@NotNull FancyHolograms plugin, @NotNull FileConfiguration config) { final int latestVersion = 1; @@ -145,6 +151,7 @@ private void setOptions(@NotNull FileConfiguration config) { defaultVisibilityDistance = (int) ConfigHelper.getOrDefault(config, CONFIG_VISIBILITY_DISTANCE, 20); registerCommands = (boolean) ConfigHelper.getOrDefault(config, CONFIG_REGISTER_COMMANDS, true); updateVisibilityInterval = (int) ConfigHelper.getOrDefault(config, CONFIG_UPDATE_VISIBILITY_INTERVAL, 20); + hologramUpdateInterval = Math.max(10, (int) ConfigHelper.getOrDefault(config, CONFIG_HOLOGRAM_UPDATE_INTERVAL, 200)); config.set(CONFIG_REPORT_ERRORS_TO_SENTRY, null); } @@ -209,4 +216,9 @@ public boolean isRegisterCommands() { public int getUpdateVisibilityInterval() { return updateVisibilityInterval; } -} \ No newline at end of file + + @Override + public int getHologramUpdateInterval() { + return hologramUpdateInterval; + } +} diff --git a/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/HologramManagerImpl.java b/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/HologramManagerImpl.java index ddd3ab7c..87224afa 100644 --- a/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/HologramManagerImpl.java +++ b/plugins/fancyholograms-v2/src/main/java/de/oliver/fancyholograms/HologramManagerImpl.java @@ -227,6 +227,10 @@ void initializeTasks() { for (final var hologram : this.getHolograms()) { HologramData data = hologram.getData(); if (data.hasChanges()) { + if (data instanceof TextHologramData) { + hologram.clearTextCache(); + } + hologram.forceUpdate(); hologram.refreshForViewersInWorld(); data.setHasChanges(false); @@ -259,7 +263,7 @@ void initializeTasks() { } } } - }, 50, 50, TimeUnit.MILLISECONDS); + }, 50, this.plugin.getHologramConfiguration().getHologramUpdateInterval(), TimeUnit.MILLISECONDS); } /**