From 5d2d3e09338d4b2aafff80f718712d570b89100e Mon Sep 17 00:00:00 2001 From: XiaoMoMi <70987828+Xiao-MoMi@users.noreply.github.com> Date: Mon, 7 Oct 2024 22:45:12 +0800 Subject: [PATCH] Improve interval system --- .../api/AbstractCNPlayer.java | 141 +-------------- .../customnameplates/api/CNPlayer.java | 2 - .../api/feature/TimeStampData.java | 7 + .../api/placeholder/PlaceholderManager.java | 10 +- .../placeholder/PlaceholderManagerImpl.java | 166 ++++++++++++++++-- 5 files changed, 172 insertions(+), 154 deletions(-) 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 750ea0a..cf75e40 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/AbstractCNPlayer.java @@ -18,7 +18,6 @@ package net.momirealms.customnameplates.api; import net.momirealms.customnameplates.api.feature.Feature; -import net.momirealms.customnameplates.api.feature.RelationalFeature; import net.momirealms.customnameplates.api.feature.TimeStampData; import net.momirealms.customnameplates.api.network.Tracker; import net.momirealms.customnameplates.api.placeholder.Placeholder; @@ -69,15 +68,7 @@ public abstract class AbstractCNPlayer implements CNPlayer { for (Placeholder placeholder : activePlaceholders) { childrenFirstList(placeholder, placeholderWithChildren); } - placeholderWithChildren = placeholderWithChildren.stream().distinct().toList(); - List placeholdersToUpdate = new ArrayList<>(); - for (Placeholder placeholder : placeholderWithChildren) { - int interval = placeholder.refreshInterval(); - if (interval > 0 && MainTask.getTicks() % interval == 0) { - placeholdersToUpdate.add(placeholder); - } - } - return placeholdersToUpdate; + return placeholderWithChildren.stream().distinct().toList(); } @Override @@ -492,134 +483,4 @@ public abstract class AbstractCNPlayer implements CNPlayer { public int hashCode() { return entityID(); } - - @Override - public void updateAndNotifyChanges(List placeholdersToUpdate) { - Set featuresToNotifyUpdates = new HashSet<>(); - Map> relationalFeaturesToNotifyUpdates = new HashMap<>(); - List delayedPlaceholdersToUpdate = new ArrayList<>(); - for (Placeholder placeholder : placeholdersToUpdate) { - if (placeholder instanceof PlayerPlaceholder playerPlaceholder) { - TimeStampData previous = this.getValue(placeholder); - if (previous == null) { - String value = playerPlaceholder.request(this); - this.setValue(placeholder, new TimeStampData<>(value, MainTask.getTicks(), true)); - featuresToNotifyUpdates.addAll(this.activeFeatures(placeholder)); - } else { - if (previous.ticks() == MainTask.getTicks()) { - if (previous.hasValueChanged()) { - featuresToNotifyUpdates.addAll(this.activeFeatures(placeholder)); - } - continue; - } - String value = playerPlaceholder.request(this); - if (!previous.data().equals(value)) { - previous.data(value); - previous.updateTicks(true); - featuresToNotifyUpdates.addAll(this.activeFeatures(placeholder)); - } else { - previous.updateTicks(false); - } - } - } else if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) { - delayedPlaceholdersToUpdate.add(relationalPlaceholder); - } else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) { - TimeStampData previous = this.getValue(placeholder); - if (previous == null) { - String value; - // if the shared placeholder has been updated by other players - if (MainTask.hasRequested(sharedPlaceholder.countId())) { - value = sharedPlaceholder.getLatestValue(); - } else { - value = sharedPlaceholder.request(); - } - this.setValue(placeholder, new TimeStampData<>(value, MainTask.getTicks(), true)); - featuresToNotifyUpdates.addAll(this.activeFeatures(placeholder)); - } else { - // The placeholder has been refreshed by other codes - if (previous.ticks() == MainTask.getTicks()) { - if (previous.hasValueChanged()) { - featuresToNotifyUpdates.addAll(this.activeFeatures(placeholder)); - } - continue; - } - String value; - // if the shared placeholder has been updated by other players - if (MainTask.hasRequested(sharedPlaceholder.countId())) { - value = sharedPlaceholder.getLatestValue(); - } else { - value = sharedPlaceholder.request(); - } - if (!previous.data().equals(value)) { - previous.data(value); - previous.updateTicks(true); - featuresToNotifyUpdates.addAll(this.activeFeatures(placeholder)); - } else { - previous.updateTicks(false); - } - } - } - } - - for (RelationalPlaceholder placeholder : delayedPlaceholdersToUpdate) { - for (CNPlayer nearby : this.nearbyPlayers()) { - TimeStampData previous = this.getRelationalValue(placeholder, nearby); - if (previous == null) { - String value = placeholder.request(this, nearby); - this.setRelationalValue(placeholder, nearby, new TimeStampData<>(value, MainTask.getTicks(), true)); - for (Feature feature : this.activeFeatures(placeholder)) { - // Filter features that will not be updated for all players - if (!featuresToNotifyUpdates.contains(feature)) { - List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); - players.add(nearby); - } - } - } else { - if (previous.ticks() == MainTask.getTicks()) { - if (previous.hasValueChanged()) { - for (Feature feature : this.activeFeatures(placeholder)) { - // Filter features that will not be updated for all players - if (!featuresToNotifyUpdates.contains(feature)) { - List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); - players.add(nearby); - } - } - } - continue; - } - String value = placeholder.request(this, nearby); - if (!previous.data().equals(value)) { - previous.data(value); - previous.updateTicks(true); - for (Feature feature : this.activeFeatures(placeholder)) { - // Filter features that will not be updated for all players - if (!featuresToNotifyUpdates.contains(feature)) { - List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); - players.add(nearby); - } - } - } else { - previous.updateTicks(false); - } - } - } - } - - // Switch to another thread for updating - plugin.getScheduler().async().execute(() -> { - // Async task takes time and the player might have been offline - if (!isOnline()) return; - for (Feature feature : featuresToNotifyUpdates) { - feature.notifyPlaceholderUpdates(this, false); - } - for (Map.Entry> innerEntry : relationalFeaturesToNotifyUpdates.entrySet()) { - Feature feature = innerEntry.getKey(); - if (feature instanceof RelationalFeature relationalFeature) { - for (CNPlayer other : innerEntry.getValue()) { - relationalFeature.notifyPlaceholderUpdates(this, other, false); - } - } - } - }); - } } 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 9a96458..a7f09a0 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/CNPlayer.java @@ -378,6 +378,4 @@ public interface CNPlayer { * Save the player's current nameplate/bubble to database */ void save(); - - void updateAndNotifyChanges(List placeholdersToUpdate); } diff --git a/api/src/main/java/net/momirealms/customnameplates/api/feature/TimeStampData.java b/api/src/main/java/net/momirealms/customnameplates/api/feature/TimeStampData.java index eaea022..bb073ad 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/feature/TimeStampData.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/feature/TimeStampData.java @@ -60,6 +60,13 @@ public class TimeStampData { this.packedTicks = packBooleanAndTicks(changed, MainTask.getTicks()); } + /** + * Consume the flag + */ + public void resetChangedFlag() { + this.packedTicks = packedTicks & 0x7FFFFFFF;; + } + /** * Returns the stored data. * diff --git a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java index 746f5f8..933af2e 100644 --- a/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java +++ b/api/src/main/java/net/momirealms/customnameplates/api/placeholder/PlaceholderManager.java @@ -37,7 +37,15 @@ public interface PlaceholderManager extends Reloadable { void refreshPlaceholders(); /** - * Gets the refresh interval for a placeholder by its ID. + * Gets the refresh interval for a placeholder by its countId. + * + * @param countId the countId of the placeholder + * @return the refresh interval in ticks + */ + int getRefreshInterval(int countId); + + /** + * Gets the refresh interval for a placeholder by its id. * * @param id the ID of the placeholder * @return the refresh interval in ticks diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/placeholder/PlaceholderManagerImpl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/placeholder/PlaceholderManagerImpl.java index 0899cf9..a1f0b2b 100644 --- a/backend/src/main/java/net/momirealms/customnameplates/backend/placeholder/PlaceholderManagerImpl.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/placeholder/PlaceholderManagerImpl.java @@ -23,8 +23,7 @@ import net.momirealms.customnameplates.api.CNPlayer; import net.momirealms.customnameplates.api.ConfigManager; import net.momirealms.customnameplates.api.CustomNameplates; import net.momirealms.customnameplates.api.MainTask; -import net.momirealms.customnameplates.api.feature.OffsetFont; -import net.momirealms.customnameplates.api.feature.PreParsedDynamicText; +import net.momirealms.customnameplates.api.feature.*; import net.momirealms.customnameplates.api.feature.background.Background; import net.momirealms.customnameplates.api.feature.bubble.Bubble; import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig; @@ -54,9 +53,11 @@ public class PlaceholderManagerImpl implements PlaceholderManager { private final CustomNameplates plugin; private final HashMap refreshIntervals = new HashMap<>(); + private final HashMap fasterRefreshIntervals = new HashMap<>(); private final Map registeredPlaceholders = new HashMap<>(); private final HashMap> childrenText = new HashMap<>(); private final HashMap nestedPlaceholders = new HashMap<>(); + private final List delayedInitTexts = new ArrayList<>(); public PlaceholderManagerImpl(CustomNameplates plugin) { this.plugin = plugin; @@ -92,6 +93,11 @@ public class PlaceholderManagerImpl implements PlaceholderManager { this.nestedPlaceholders.put(placeholder.id().replace("%np_", "%nameplates_").replace("%rel_np_", "%rel_nameplates_"), placeholder); } this.childrenText.clear(); + + for (PreParsedDynamicText preParsedText : this.delayedInitTexts) { + preParsedText.init(); + } + this.delayedInitTexts.clear(); } @Override @@ -99,7 +105,9 @@ public class PlaceholderManagerImpl implements PlaceholderManager { this.refreshIntervals.clear(); this.registeredPlaceholders.clear(); this.childrenText.clear(); + this.delayedInitTexts.clear(); this.nestedPlaceholders.clear(); + this.fasterRefreshIntervals.clear(); PlaceholderCounter.reset(); } @@ -317,7 +325,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager { PreParsedDynamicText defaultValue = new PreParsedDynamicText(inner.getString("default", "")); ArrayList list = new ArrayList<>(); list.add(placeholderToSwitch); - list.add(defaultValue); + delayedInitTexts.add(defaultValue); Map valueMap = new HashMap<>(); Section results = inner.getSection("case"); if (results != null) { @@ -325,7 +333,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager { if (strEntry.getValue() instanceof String string) { PreParsedDynamicText preParsedDynamicText = new PreParsedDynamicText(string); valueMap.put(strEntry.getKey(), preParsedDynamicText); - list.add(preParsedDynamicText); + delayedInitTexts.add(preParsedDynamicText); } } } @@ -471,7 +479,6 @@ public class PlaceholderManagerImpl implements PlaceholderManager { for (Map.Entry entry : section.getStringRouteMappedValues(false).entrySet()) { String id = entry.getKey(); if (entry.getValue() instanceof Section placeholderSection) { - List list = new ArrayList<>(); ArrayList> orderedTexts = new ArrayList<>(); for (Map.Entry conditionEntry : placeholderSection.getStringRouteMappedValues(false).entrySet()) { if (conditionEntry.getValue() instanceof Section inner) { @@ -479,7 +486,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager { Requirement[] requirements = plugin.getRequirementManager().parseRequirements(inner.getSection("conditions")); PreParsedDynamicText preParsedDynamicText = new PreParsedDynamicText(text); orderedTexts.add(Pair.of(preParsedDynamicText, requirements)); - list.add(preParsedDynamicText); + delayedInitTexts.add(preParsedDynamicText); } } Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_conditional_" + id + "%", () -> { @@ -516,9 +523,9 @@ public class PlaceholderManagerImpl implements PlaceholderManager { } return ""; }); - childrenText.put(placeholder1, list); - childrenText.put(placeholder2, list); - childrenText.put(placeholder3, list); + childrenText.put(placeholder1, List.of()); + childrenText.put(placeholder2, List.of()); + childrenText.put(placeholder3, List.of()); } } } @@ -527,10 +534,143 @@ public class PlaceholderManagerImpl implements PlaceholderManager { public void refreshPlaceholders() { for (CNPlayer player : plugin.getOnlinePlayers()) { if (!player.isOnline()) continue; - player.updateAndNotifyChanges(player.activePlaceholdersToRefresh()); + Set featuresToNotifyUpdates = new HashSet<>(); + Map> relationalFeaturesToNotifyUpdates = new HashMap<>(); + List delayedPlaceholdersToUpdate = new ArrayList<>(); + for (Placeholder placeholder : player.activePlaceholdersToRefresh()) { + if (placeholder instanceof PlayerPlaceholder playerPlaceholder) { + TimeStampData previous = player.getValue(placeholder); + if (previous == null) { + String value = playerPlaceholder.request(player); + player.setValue(placeholder, new TimeStampData<>(value, MainTask.getTicks(), true)); + featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder)); + } else { + if (previous.ticks() > MainTask.getTicks() - getRefreshInterval(placeholder.countId())) { + if (previous.hasValueChanged()) { + previous.resetChangedFlag(); + featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder)); + } + continue; + } + String value = playerPlaceholder.request(player); + if (!previous.data().equals(value)) { + previous.data(value); + previous.updateTicks(true); + featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder)); + } else { + previous.updateTicks(false); + } + } + } else if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) { + delayedPlaceholdersToUpdate.add(relationalPlaceholder); + } else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) { + TimeStampData previous = player.getValue(placeholder); + if (previous == null) { + String value; + // if the shared placeholder has been updated by other players + if (MainTask.hasRequested(sharedPlaceholder.countId())) { + value = sharedPlaceholder.getLatestValue(); + } else { + value = sharedPlaceholder.request(); + } + player.setValue(placeholder, new TimeStampData<>(value, MainTask.getTicks(), true)); + featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder)); + } else { + // The placeholder has been refreshed by other codes + if (previous.ticks() > MainTask.getTicks() - getRefreshInterval(placeholder.countId())) { + if (previous.hasValueChanged()) { + previous.resetChangedFlag(); + featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder)); + } + continue; + } + String value; + // if the shared placeholder has been updated by other players + if (MainTask.hasRequested(sharedPlaceholder.countId())) { + value = sharedPlaceholder.getLatestValue(); + } else { + value = sharedPlaceholder.request(); + } + if (!previous.data().equals(value)) { + previous.data(value); + previous.updateTicks(true); + featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder)); + } else { + previous.updateTicks(false); + } + } + } + } + + for (RelationalPlaceholder placeholder : delayedPlaceholdersToUpdate) { + for (CNPlayer nearby : player.nearbyPlayers()) { + TimeStampData previous = player.getRelationalValue(placeholder, nearby); + if (previous == null) { + String value = placeholder.request(player, nearby); + player.setRelationalValue(placeholder, nearby, new TimeStampData<>(value, MainTask.getTicks(), true)); + for (Feature feature : player.activeFeatures(placeholder)) { + // Filter features that will not be updated for all players + if (!featuresToNotifyUpdates.contains(feature)) { + List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); + players.add(nearby); + } + } + } else { + if (previous.ticks() > MainTask.getTicks() - getRefreshInterval(placeholder.countId())) { + if (previous.hasValueChanged()) { + previous.resetChangedFlag(); + for (Feature feature : player.activeFeatures(placeholder)) { + // Filter features that will not be updated for all players + if (!featuresToNotifyUpdates.contains(feature)) { + List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); + players.add(nearby); + } + } + } + continue; + } + String value = placeholder.request(player, nearby); + if (!previous.data().equals(value)) { + previous.data(value); + previous.updateTicks(true); + for (Feature feature : player.activeFeatures(placeholder)) { + // Filter features that will not be updated for all players + if (!featuresToNotifyUpdates.contains(feature)) { + List players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>()); + players.add(nearby); + } + } + } else { + previous.updateTicks(false); + } + } + } + } + + // Switch to another thread for updating + plugin.getScheduler().async().execute(() -> { + // Async task takes time and the player might have been offline + if (!player.isOnline()) return; + for (Feature feature : featuresToNotifyUpdates) { + feature.notifyPlaceholderUpdates(player, false); + } + for (Map.Entry> innerEntry : relationalFeaturesToNotifyUpdates.entrySet()) { + Feature feature = innerEntry.getKey(); + if (feature instanceof RelationalFeature relationalFeature) { + for (CNPlayer other : innerEntry.getValue()) { + relationalFeature.notifyPlaceholderUpdates(player, other, false); + } + } + } + }); } } + @Override + public int getRefreshInterval(int countId) { + return fasterRefreshIntervals.getOrDefault(countId, ConfigManager.defaultRefreshInterval()); + } + @Override public int getRefreshInterval(String id) { return refreshIntervals.getOrDefault(id, ConfigManager.defaultRefreshInterval()); @@ -540,9 +680,13 @@ public class PlaceholderManagerImpl implements PlaceholderManager { public T registerPlaceholder(T placeholder) { Placeholder nested = nestedPlaceholders.get(placeholder.id()); if (nested != null) { - placeholder.addChildren(nested.children()); + for (Placeholder child : nested.children()) { + placeholder.addChild(child); + child.addParent(placeholder); + } } registeredPlaceholders.put(placeholder.id(), placeholder); + fasterRefreshIntervals.put(placeholder.countId(), getRefreshInterval(placeholder.id())); return placeholder; }