Improve and refactor individual placeholders

This commit is contained in:
filoghost 2021-06-19 17:52:12 +02:00
parent 5543edd47b
commit eeec178e65
43 changed files with 1047 additions and 524 deletions

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.api.placeholder;
/**
* @since 1
*/
public interface IndividualPlaceholder extends IndividualPlaceholderReplacer {
/**
* @since 1
*/
int getRefreshIntervalTicks();
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.api.placeholder;
import org.jetbrains.annotations.Nullable;
/**
* @since 1
*/
public interface IndividualPlaceholderFactory {
/**
* @since 1
*/
@Nullable IndividualPlaceholder getPlaceholder(@Nullable String argument);
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.api.placeholder;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @since 1
*/
@FunctionalInterface
public interface IndividualPlaceholderReplacer {
/**
* @since 1
*/
@Nullable String getReplacement(@NotNull Player player, @Nullable String argument);
}

View File

@ -41,11 +41,22 @@ public class DefaultHolographicDisplaysAPI implements HolographicDisplaysAPI {
@Override
public void registerPlaceholder(@NotNull String identifier, int refreshIntervalTicks, @NotNull PlaceholderReplacer replacer) {
Preconditions.notNull(identifier, "identifier");
Preconditions.notEmpty(identifier, "identifier");
for (char c : identifier.toCharArray()) {
Preconditions.checkArgument(isValidIdentifierCharacter(c), "identifier contains invalid character '" + c + "'");
}
Preconditions.checkArgument(refreshIntervalTicks >= 0, "refreshIntervalTicks should be positive");
Preconditions.notNull(replacer, "replacer");
placeholderRegistry.registerReplacer(plugin, identifier, refreshIntervalTicks, replacer);
placeholderRegistry.registerGlobalPlaceholderReplacer(plugin, identifier, refreshIntervalTicks, replacer);
}
private boolean isValidIdentifierCharacter(char c) {
return ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
|| c == '-'
|| c == '_';
}
@Override

View File

@ -31,7 +31,10 @@ import me.filoghost.holographicdisplays.object.api.APIHologram;
import me.filoghost.holographicdisplays.object.api.APIHologramManager;
import me.filoghost.holographicdisplays.object.internal.InternalHologram;
import me.filoghost.holographicdisplays.object.internal.InternalHologramManager;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderTracker;
import me.filoghost.holographicdisplays.placeholder.TickClock;
import me.filoghost.holographicdisplays.placeholder.TickingTask;
import me.filoghost.holographicdisplays.placeholder.internal.AnimationRegistry;
import me.filoghost.holographicdisplays.placeholder.internal.DefaultPlaceholders;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
@ -52,7 +55,7 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
private APIHologramManager apiHologramManager;
private BungeeServerTracker bungeeServerTracker;
private AnimationRegistry animationRegistry;
private PlaceholderManager placeholderManager;
private PlaceholderRegistry placeholderRegistry;
@Override
public void onCheckedEnable() throws PluginEnableException {
@ -94,12 +97,15 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
throw new PluginEnableException(e, "Couldn't initialize the NMS manager.");
}
configManager = new ConfigManager(getDataFolder().toPath());
bungeeServerTracker = new BungeeServerTracker(this);
animationRegistry = new AnimationRegistry();
placeholderManager = new PlaceholderManager();
configManager = new ConfigManager(getDataFolder().toPath());
internalHologramManager = new InternalHologramManager(nmsManager, placeholderManager);
apiHologramManager = new APIHologramManager(nmsManager, placeholderManager);
placeholderRegistry = new PlaceholderRegistry();
TickClock tickClock = new TickClock();
PlaceholderTracker placeholderTracker = new PlaceholderTracker(placeholderRegistry, tickClock);
PlaceholderLineTracker placeholderLineTracker = new PlaceholderLineTracker(placeholderTracker);
internalHologramManager = new InternalHologramManager(nmsManager, placeholderLineTracker);
apiHologramManager = new APIHologramManager(nmsManager, placeholderLineTracker);
PrintableErrorCollector errorCollector = new PrintableErrorCollector();
@ -109,14 +115,15 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
} catch (ConfigException e) {
errorCollector.add(e, "couldn't automatically convert symbols file to the new format");
}
load(true, errorCollector);
ProtocolLibHook.setup(this, nmsManager, this, errorCollector);
PlaceholderAPIHook.setup();
placeholderManager.startUpdaterTask(this);
load(true, errorCollector);
ProtocolLibHook.setup(this, nmsManager, this, placeholderLineTracker, errorCollector);
PlaceholderAPIHook.setup();
TickingTask tickingTask = new TickingTask(tickClock, placeholderLineTracker);
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, tickingTask, 0, 1);
HologramCommandManager commandManager = new HologramCommandManager(configManager, internalHologramManager, nmsManager);
commandManager.register(this);
@ -127,14 +134,9 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
registerListener(updateNotificationListener);
// Enable the APIs
HolographicDisplaysAPIProvider.setImplementation(new DefaultHolographicDisplaysAPIProvider(
apiHologramManager,
nmsManager,
placeholderManager.getPlaceholderRegistry()));
enableLegacyAPI(
apiHologramManager,
nmsManager,
placeholderManager.getPlaceholderRegistry());
HolographicDisplaysAPIProvider.setImplementation(
new DefaultHolographicDisplaysAPIProvider(apiHologramManager, nmsManager, placeholderRegistry));
enableLegacyAPI(apiHologramManager, nmsManager, placeholderRegistry);
// Register bStats metrics
int pluginID = 3123;
@ -157,7 +159,7 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
}
public void load(boolean deferHologramsCreation, ErrorCollector errorCollector) {
DefaultPlaceholders.resetAndRegister(placeholderManager.getPlaceholderRegistry(), animationRegistry, bungeeServerTracker);
DefaultPlaceholders.resetAndRegister(placeholderRegistry, animationRegistry, bungeeServerTracker);
internalHologramManager.clearAll();

View File

@ -84,7 +84,7 @@ class MetadataHelper {
}
public WrappedWatchableObject getCustomNameWacthableObject(WrappedDataWatcher metadata) {
public WrappedWatchableObject getCustomNameWatchableObject(WrappedDataWatcher metadata) {
return metadata.getWatchableObject(customNameIndex);
}

View File

@ -22,11 +22,8 @@ import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.core.nms.ProtocolPacketSettings;
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
import me.filoghost.holographicdisplays.core.nms.entity.NMSEntity;
import me.filoghost.holographicdisplays.placeholder.RelativePlaceholder;
import me.filoghost.holographicdisplays.placeholder.PlaceholdersUpdateTask;
import me.filoghost.holographicdisplays.placeholder.TrackedLine;
import me.filoghost.holographicdisplays.placeholder.parsing.StringWithPlaceholders;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import me.filoghost.holographicdisplays.placeholder.tracking.TrackedLine;
import me.filoghost.holographicdisplays.util.NMSVersion;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@ -36,10 +33,14 @@ class PacketListener extends PacketAdapter {
private final NMSManager nmsManager;
private final MetadataHelper metadataHelper;
private final ProtocolPacketSettings packetSettings;
PlaceholdersUpdateTask placeholdersUpdateTask;
PlaceholderRegistry placeholderRegistry;
private final PlaceholderLineTracker placeholderLineTracker;
PacketListener(Plugin plugin, NMSManager nmsManager, MetadataHelper metadataHelper, ProtocolPacketSettings packetSettings) {
PacketListener(
Plugin plugin,
NMSManager nmsManager,
MetadataHelper metadataHelper,
ProtocolPacketSettings packetSettings,
PlaceholderLineTracker placeholderLineTracker) {
super(PacketAdapter.params()
.plugin(plugin)
.types(
@ -54,6 +55,7 @@ class PacketListener extends PacketAdapter {
this.nmsManager = nmsManager;
this.metadataHelper = metadataHelper;
this.packetSettings = packetSettings;
this.placeholderLineTracker = placeholderLineTracker;
}
public void registerListener() {
@ -117,9 +119,9 @@ class PacketListener extends PacketAdapter {
return;
}
String customNameWithRelativePlaceholders = replaceRelativePlaceholders(textLine, customName, player);
String customNameWithIndividualPlaceholders = replaceIndividualPlaceholders(textLine, customName, player);
if (customNameWithRelativePlaceholders.equals(customName)) {
if (customNameWithIndividualPlaceholders.equals(customName)) {
return; // No need to modify packets, custom name doesn't need changes
}
@ -133,36 +135,26 @@ class PacketListener extends PacketAdapter {
} else {
WrapperPlayServerSpawnEntityLiving spawnEntityPacket = new WrapperPlayServerSpawnEntityLiving(packet.deepClone());
packetWrapper = spawnEntityPacket;
customNameWatchableObject = metadataHelper.getCustomNameWacthableObject(spawnEntityPacket.getMetadata());
customNameWatchableObject = metadataHelper.getCustomNameWatchableObject(spawnEntityPacket.getMetadata());
}
if (customNameWatchableObject == null) {
return;
}
Object customNameNMSObject = nmsManager.createCustomNameNMSObject(customNameWithRelativePlaceholders);
Object customNameNMSObject = nmsManager.createCustomNameNMSObject(customNameWithIndividualPlaceholders);
metadataHelper.setCustomNameNMSObject(customNameWatchableObject, customNameNMSObject);
event.setPacket(packetWrapper.getHandle());
}
}
private String replaceRelativePlaceholders(StandardTextLine textLine, String text, Player player) {
TrackedLine trackedLine = placeholdersUpdateTask.getTrackedLine(textLine);
private String replaceIndividualPlaceholders(StandardTextLine textLine, String text, Player player) {
TrackedLine trackedLine = placeholderLineTracker.getTrackedLine(textLine);
if (trackedLine == null) {
return text;
}
if (trackedLine.containsRelativePlaceholders()) {
StringWithPlaceholders textWithPlaceholders = new StringWithPlaceholders(text);
textWithPlaceholders.replacePlaceholders(placeholderOccurrence -> {
RelativePlaceholder relativePlaceholder = placeholderRegistry.findRelative(placeholderOccurrence);
if (relativePlaceholder != null) {
return relativePlaceholder.getReplacement(player);
} else {
return null;
}
});
}
text = trackedLine.replaceIndividualPlaceholders(player);
if (PlaceholderAPIHook.isEnabled() && PlaceholderAPIHook.containsPlaceholders(text)) {
text = PlaceholderAPIHook.replacePlaceholders(player, text);

View File

@ -12,6 +12,7 @@ import me.filoghost.holographicdisplays.core.Utils;
import me.filoghost.holographicdisplays.core.hologram.StandardHologram;
import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.core.nms.ProtocolPacketSettings;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import me.filoghost.holographicdisplays.util.VersionUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@ -26,7 +27,12 @@ public class ProtocolLibHook {
private static boolean enabled;
private static PacketSender packetSender;
public static void setup(Plugin plugin, NMSManager nmsManager, ProtocolPacketSettings packetSettings, ErrorCollector errorCollector) {
public static void setup(
Plugin plugin,
NMSManager nmsManager,
ProtocolPacketSettings packetSettings,
PlaceholderLineTracker placeholderLineTracker,
ErrorCollector errorCollector) {
if (!Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) {
return;
}
@ -42,21 +48,21 @@ public class ProtocolLibHook {
String versionNumbers = versionNumbersMatcher.group();
if (!VersionUtils.isVersionGreaterEqual(versionNumbers, "4.4")) {
errorCollector.add("detected old unsupported version of ProtocolLib, support disabled."
errorCollector.add("detected old unsupported version of ProtocolLib, support disabled."
+ " You must use ProtocolLib 4.4.0 or higher");
return;
}
} catch (Exception e) {
errorCollector.add(e, "could not detect ProtocolLib version (" + e.getMessage() + "),"
errorCollector.add(e, "could not detect ProtocolLib version (" + e.getMessage() + "),"
+ " enabling support anyway and hoping for the best");
}
try {
MetadataHelper metadataHelper = new MetadataHelper();
new PacketListener(plugin, nmsManager, metadataHelper, packetSettings).registerListener();
new PacketListener(plugin, nmsManager, metadataHelper, packetSettings, placeholderLineTracker).registerListener();
packetSender = new PacketSender(metadataHelper);
Log.info("Enabled player relative placeholders with ProtocolLib.");
Log.info("Enabled per-player placeholders with ProtocolLib.");
} catch (Exception e) {
errorCollector.add(e, "failed to load ProtocolLib support, is it updated?");
return;

View File

@ -71,7 +71,11 @@ public class V2HologramsAPIProvider extends HologramsAPIProvider {
boolean alreadyRegistered = placeholderRegistry.isRegisteredIdentifier(plugin, textPlaceholder);
if (!alreadyRegistered) {
placeholderRegistry.registerReplacer(plugin, textPlaceholder, refreshIntervalTicks, argument -> replacer.update());
placeholderRegistry.registerGlobalPlaceholderReplacer(
plugin,
textPlaceholder,
refreshIntervalTicks,
argument -> replacer.update());
return true;
} else {
return false;

View File

@ -11,7 +11,7 @@ import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.disk.Configuration;
import me.filoghost.holographicdisplays.legacy.api.v2.V2HologramAdapter;
import me.filoghost.holographicdisplays.object.base.BaseHologram;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@ -36,8 +36,8 @@ public class APIHologram extends BaseHologram<APIHologramLine> implements Hologr
Plugin plugin,
NMSManager nmsManager,
APIHologramManager apiHologramManager,
PlaceholderManager placeholderManager) {
super(source, nmsManager, placeholderManager);
PlaceholderLineTracker placeholderLineTracker) {
super(source, nmsManager, placeholderLineTracker);
Preconditions.notNull(plugin, "plugin");
this.plugin = plugin;
this.apiHologramManager = apiHologramManager;

View File

@ -8,7 +8,7 @@ package me.filoghost.holographicdisplays.object.api;
import me.filoghost.holographicdisplays.api.Hologram;
import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.object.base.BaseHologramManager;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import org.bukkit.Location;
import org.bukkit.plugin.Plugin;
@ -20,15 +20,15 @@ import java.util.List;
public class APIHologramManager extends BaseHologramManager<APIHologram> {
private final NMSManager nmsManager;
private final PlaceholderManager placeholderManager;
private final PlaceholderLineTracker placeholderLineTracker;
public APIHologramManager(NMSManager nmsManager, PlaceholderManager placeholderManager) {
public APIHologramManager(NMSManager nmsManager, PlaceholderLineTracker placeholderLineTracker) {
this.nmsManager = nmsManager;
this.placeholderManager = placeholderManager;
this.placeholderLineTracker = placeholderLineTracker;
}
public APIHologram createHologram(Location source, Plugin plugin) {
APIHologram hologram = new APIHologram(source, plugin, nmsManager, this, placeholderManager);
APIHologram hologram = new APIHologram(source, plugin, nmsManager, this, placeholderLineTracker);
super.addHologram(hologram);
return hologram;
}

View File

@ -10,7 +10,7 @@ import me.filoghost.holographicdisplays.core.hologram.StandardHologram;
import me.filoghost.holographicdisplays.core.hologram.StandardHologramLine;
import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.disk.Configuration;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
@ -23,14 +23,14 @@ import java.util.List;
public abstract class BaseHologram<T extends StandardHologramLine> extends BaseHologramComponent implements StandardHologram {
private final NMSManager nmsManager;
private final PlaceholderManager placeholderManager;
private final PlaceholderLineTracker placeholderLineTracker;
private final List<T> lines;
private final List<T> unmodifiableLinesView;
private boolean deleted;
public BaseHologram(Location location, NMSManager nmsManager, PlaceholderManager placeholderManager) {
this.placeholderManager = placeholderManager;
public BaseHologram(Location location, NMSManager nmsManager, PlaceholderLineTracker placeholderLineTracker) {
this.placeholderLineTracker = placeholderLineTracker;
Preconditions.notNull(location, "location");
this.setLocation(location);
this.nmsManager = nmsManager;
@ -42,8 +42,8 @@ public abstract class BaseHologram<T extends StandardHologramLine> extends BaseH
return nmsManager;
}
protected final PlaceholderManager getPlaceholderManager() {
return placeholderManager;
protected final PlaceholderLineTracker getPlaceholderLineTracker() {
return placeholderLineTracker;
}
@Override

View File

@ -11,7 +11,7 @@ import me.filoghost.holographicdisplays.core.hologram.StandardHologram;
import me.filoghost.holographicdisplays.core.hologram.StandardHologramLine;
import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.core.nms.SpawnFailedException;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import org.bukkit.World;
public abstract class BaseHologramLine extends BaseHologramComponent implements StandardHologramLine {
@ -34,8 +34,8 @@ public abstract class BaseHologramLine extends BaseHologramComponent implements
return hologram.getNMSManager();
}
protected final PlaceholderManager getPlaceholderManager() {
return hologram.getPlaceholderManager();
protected final PlaceholderLineTracker getPlaceholderLineTracker() {
return hologram.getPlaceholderLineTracker();
}
@Override

View File

@ -34,7 +34,7 @@ public abstract class BaseTextLine extends BaseTouchableLine implements Standard
if (textEntity != null) {
textEntity.setCustomNameNMS(text);
getPlaceholderManager().updateTracking(this);
getPlaceholderLineTracker().onTextLineChange(this);
}
}
@ -48,7 +48,7 @@ public abstract class BaseTextLine extends BaseTouchableLine implements Standard
textEntity.setCustomNameNMS(text);
}
getPlaceholderManager().updateTracking(this);
getPlaceholderLineTracker().onTextLineChange(this);
}
@Override

View File

@ -8,7 +8,7 @@ package me.filoghost.holographicdisplays.object.internal;
import me.filoghost.holographicdisplays.HolographicDisplays;
import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.object.base.BaseHologram;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
@ -18,8 +18,8 @@ public class InternalHologram extends BaseHologram<InternalHologramLine> {
private final String name;
protected InternalHologram(Location source, String name, NMSManager nmsManager, PlaceholderManager placeholderManager) {
super(source, nmsManager, placeholderManager);
protected InternalHologram(Location source, String name, NMSManager nmsManager, PlaceholderLineTracker placeholderLineTracker) {
super(source, nmsManager, placeholderLineTracker);
this.name = name;
}

View File

@ -7,21 +7,21 @@ package me.filoghost.holographicdisplays.object.internal;
import me.filoghost.holographicdisplays.core.nms.NMSManager;
import me.filoghost.holographicdisplays.object.base.BaseHologramManager;
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
import org.bukkit.Location;
public class InternalHologramManager extends BaseHologramManager<InternalHologram> {
private final NMSManager nmsManager;
private final PlaceholderManager placeholderManager;
private final PlaceholderLineTracker placeholderLineTracker;
public InternalHologramManager(NMSManager nmsManager, PlaceholderManager placeholderManager) {
public InternalHologramManager(NMSManager nmsManager, PlaceholderLineTracker placeholderLineTracker) {
this.nmsManager = nmsManager;
this.placeholderManager = placeholderManager;
this.placeholderLineTracker = placeholderLineTracker;
}
public InternalHologram createHologram(Location source, String name) {
InternalHologram hologram = new InternalHologram(source, name, nmsManager, placeholderManager);
InternalHologram hologram = new InternalHologram(source, name, nmsManager, placeholderLineTracker);
super.addHologram(hologram);
return hologram;
}

View File

@ -1,38 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
public class PlaceholderManager {
private final PlaceholderRegistry placeholderRegistry;
private final PlaceholdersUpdateTask placeholdersUpdateTask;
public PlaceholderManager() {
this.placeholderRegistry = new PlaceholderRegistry();
PlaceholdersReplacementTracker placeholdersReplacementTracker = new PlaceholdersReplacementTracker(placeholderRegistry);
this.placeholdersUpdateTask = new PlaceholdersUpdateTask(placeholdersReplacementTracker, placeholderRegistry);
placeholderRegistry.setChangeListener(placeholdersReplacementTracker::clearOutdatedSources);
}
public PlaceholderRegistry getPlaceholderRegistry() {
return placeholderRegistry;
}
public void startUpdaterTask(Plugin plugin) {
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, placeholdersUpdateTask, 0, 1);
}
public void updateTracking(StandardTextLine line) {
placeholdersUpdateTask.updateTracking(line);
}
}

View File

@ -1,113 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
public class PlaceholdersReplacementTracker {
private final PlaceholderRegistry registry;
private final Map<PlaceholderOccurrence, ReplacementHolder> currentReplacements;
public PlaceholdersReplacementTracker(PlaceholderRegistry registry) {
this.registry = registry;
// Use WeakHashMap to ensure that when a PlaceholderOccurrence is no longer referenced in other objects
// the corresponding entry is removed from the map automatically.
this.currentReplacements = new WeakHashMap<>();
}
public void clearOutdatedSources() {
// Remove entries whose placeholder expansion sources are outdated
currentReplacements.entrySet().removeIf(entry -> {
PlaceholderOccurrence placeholderOccurrence = entry.getKey();
PlaceholderExpansion currentSource = entry.getValue().source;
PlaceholderExpansion newSource = registry.findBestMatch(placeholderOccurrence);
return !Objects.equals(currentSource, newSource);
});
}
public @Nullable String getOrUpdateReplacement(PlaceholderOccurrence placeholderOccurrence, long currentTick)
throws PlaceholderException {
ReplacementHolder replacementHolder = currentReplacements.get(placeholderOccurrence);
if (replacementHolder == null) {
replacementHolder = createReplacementHolder(placeholderOccurrence);
currentReplacements.put(placeholderOccurrence, replacementHolder);
}
try {
replacementHolder.refreshIfNeeded(placeholderOccurrence, currentTick);
} catch (Throwable t) {
throw new PlaceholderException(t, replacementHolder.source);
}
return replacementHolder.currentReplacement;
}
private ReplacementHolder createReplacementHolder(PlaceholderOccurrence placeholderOccurrence) throws PlaceholderException {
PlaceholderExpansion placeholderExpansion = registry.findBestMatch(placeholderOccurrence);
Placeholder placeholder;
if (placeholderExpansion != null) {
PlaceholderFactory placeholderFactory = placeholderExpansion.getPlaceholderFactory();
try {
placeholder = placeholderFactory.getPlaceholder(placeholderOccurrence.getArgument());
} catch (Throwable t) {
throw new PlaceholderException(t, placeholderExpansion);
}
} else {
placeholder = null;
}
return new ReplacementHolder(placeholderExpansion, placeholder);
}
private static class ReplacementHolder {
final PlaceholderExpansion source;
final Placeholder placeholder;
String currentReplacement;
long lastUpdateTick = -1;
ReplacementHolder(PlaceholderExpansion source, Placeholder placeholder) {
this.source = source;
this.placeholder = placeholder;
}
public void refreshIfNeeded(PlaceholderOccurrence placeholderOccurrence, long currentTick) {
if (needsRefresh(currentTick)) {
currentReplacement = placeholder.getReplacement(placeholderOccurrence.getArgument());
lastUpdateTick = currentTick;
}
}
private boolean needsRefresh(long currentTick) {
if (placeholder == null || lastUpdateTick == currentTick) {
return false; // No need to refresh
}
if (lastUpdateTick == -1) {
return true; // Force at least the initial refresh
}
return currentTick - lastUpdateTick >= placeholder.getRefreshIntervalTicks();
}
}
}

View File

@ -1,121 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import me.filoghost.fcommons.logging.Log;
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import me.filoghost.holographicdisplays.placeholder.parsing.StringWithPlaceholders;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class PlaceholdersUpdateTask implements Runnable {
private final PlaceholdersReplacementTracker placeholdersReplacementTracker;
private final PlaceholderRegistry placeholderRegistry;
private final Map<StandardTextLine, TrackedLine> trackedLines;
private final Map<PlaceholderExpansion, Long> lastErrorLogByPlaceholderExpansion;
private long currentTick;
public PlaceholdersUpdateTask(PlaceholdersReplacementTracker placeholdersReplacementTracker, PlaceholderRegistry placeholderRegistry) {
this.placeholdersReplacementTracker = placeholdersReplacementTracker;
this.placeholderRegistry = placeholderRegistry;
this.trackedLines = new LinkedHashMap<>();
this.lastErrorLogByPlaceholderExpansion = new WeakHashMap<>();
}
@Override
public void run() {
Iterator<TrackedLine> iterator = trackedLines.values().iterator();
while (iterator.hasNext()) {
TrackedLine trackedLine = iterator.next();
if (trackedLine.shouldBeUntracked()) {
iterator.remove();
continue;
}
trackedLine.updateNameWithPlaceholders();
}
currentTick++;
}
public TrackedLine getTrackedLine(StandardTextLine line) {
return trackedLines.get(line);
}
public void updateTracking(StandardTextLine line) {
TrackedLine trackedLine = createTrackedLineIfNeeded(line);
if (trackedLine != null) {
trackedLines.put(line, trackedLine);
trackedLine.updateNameWithPlaceholders();
} else {
TrackedLine untrackedLine = trackedLines.remove(line);
if (untrackedLine != null) {
untrackedLine.restoreOriginalName();
}
}
}
private @Nullable TrackedLine createTrackedLineIfNeeded(StandardTextLine textLine) {
if (!textLine.isAllowPlaceholders()) {
return null;
}
String text = textLine.getText();
if (text == null || text.isEmpty()) {
return null;
}
NMSArmorStand entity = textLine.getNMSArmorStand();
if (entity == null) {
return null;
}
StringWithPlaceholders textWithPlaceholders = new StringWithPlaceholders(text);
if (!textWithPlaceholders.containsPlaceholders()) {
return null;
}
return new TrackedLine(this, placeholderRegistry, textLine, entity, textWithPlaceholders);
}
protected String getCurrentReplacement(PlaceholderOccurrence placeholderOccurrence) {
try {
return placeholdersReplacementTracker.getOrUpdateReplacement(placeholderOccurrence, currentTick);
} catch (PlaceholderException e) {
handleException(e);
return "[Error]";
}
}
private void handleException(PlaceholderException exception) {
PlaceholderExpansion placeholderExpansion = exception.getPlaceholderExpansion();
Long lastErrorLog = lastErrorLogByPlaceholderExpansion.get(placeholderExpansion);
if (lastErrorLog != null && currentTick - lastErrorLog < 20) {
return; // Avoid spamming the console too frequently
}
lastErrorLogByPlaceholderExpansion.put(placeholderExpansion, currentTick);
Log.warning("The placeholder \"" + placeholderExpansion.getIdentifier() + "\""
+ " registered by the plugin " + placeholderExpansion.getPluginName()
+ " generated an exception."
+ " Please contact the author of " + placeholderExpansion.getPluginName(),
exception.getCause());
}
}

View File

@ -1,14 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import org.bukkit.entity.Player;
public interface RelativePlaceholder {
String getReplacement(Player player);
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class StandardPlaceholder {
private final @NotNull PlaceholderExpansion source;
protected StandardPlaceholder(@NotNull PlaceholderExpansion source) {
this.source = source;
}
public final @NotNull PlaceholderExpansion getSource() {
return source;
}
public boolean isIndividual() {
return source.isIndividual();
}
public final int getRefreshIntervalTicks() throws PlaceholderException {
try {
return doGetRefreshIntervalTicks();
} catch (Throwable t) {
throw new PlaceholderException(t, getSource());
}
}
public final @Nullable String getReplacement(Player player, @Nullable String argument) throws PlaceholderException {
try {
return doGetReplacement(player, argument);
} catch (Throwable t) {
throw new PlaceholderException(t, getSource());
}
}
/*
* Below methods may use externally provided objects which can throw any exception
*/
protected abstract int doGetRefreshIntervalTicks() throws Throwable;
protected abstract @Nullable String doGetReplacement(Player player, @Nullable String argument) throws Throwable;
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
public class TickClock {
private long currentTick;
public void incrementTick() {
currentTick++;
}
public long getCurrentTick() {
return currentTick;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import me.filoghost.holographicdisplays.placeholder.tracking.PlaceholderLineTracker;
public class TickingTask implements Runnable {
private final TickClock tickClock;
private final PlaceholderLineTracker placeholderLineTracker;
public TickingTask(TickClock tickClock, PlaceholderLineTracker placeholderLineTracker) {
this.tickClock = tickClock;
this.placeholderLineTracker = placeholderLineTracker;
}
@Override
public void run() {
tickClock.incrementTick();
placeholderLineTracker.updateEntitiesWithGlobalPlaceholders();
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder;
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
import me.filoghost.holographicdisplays.placeholder.parsing.StringWithPlaceholders;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
public class TrackedLine {
private final PlaceholdersUpdateTask placeholdersUpdateTask;
private final PlaceholderRegistry placeholderRegistry;
private final StandardTextLine textLine;
private final NMSArmorStand entity;
private final StringWithPlaceholders nameWithPlaceholders;
private final boolean containsRelativePlaceholders;
TrackedLine(PlaceholdersUpdateTask placeholdersUpdateTask, PlaceholderRegistry placeholderRegistry, StandardTextLine textLine, NMSArmorStand entity, StringWithPlaceholders nameWithPlaceholders) {
this.placeholdersUpdateTask = placeholdersUpdateTask;
this.placeholderRegistry = placeholderRegistry;
this.textLine = textLine;
this.entity = entity;
this.nameWithPlaceholders = nameWithPlaceholders;
this.containsRelativePlaceholders = nameWithPlaceholders.containsPlaceholdersMatching(
occurrence -> this.placeholderRegistry.findRelative(occurrence) != null
);
}
void updateNameWithPlaceholders() {
String newName = nameWithPlaceholders.replacePlaceholders(placeholdersUpdateTask::getCurrentReplacement);
entity.setCustomNameNMS(newName);
}
void restoreOriginalName() {
if (!entity.isDeadNMS()) {
entity.setCustomNameNMS(textLine.getText());
}
}
public boolean containsRelativePlaceholders() {
return containsRelativePlaceholders;
}
public boolean shouldBeUntracked() {
return entity.isDeadNMS();
}
}

View File

@ -30,7 +30,7 @@ public class DefaultPlaceholders {
HolographicDisplays plugin = HolographicDisplays.getInstance();
placeholderRegistry.unregisterAll(plugin);
placeholderRegistry.register(plugin, "rainbow", new AnimationPlaceholder(4, toStringList(
placeholderRegistry.registerGlobalPlaceholder(plugin, "rainbow", new AnimationPlaceholder(4, toStringList(
ChatColor.RED,
ChatColor.GOLD,
ChatColor.YELLOW,
@ -39,17 +39,17 @@ public class DefaultPlaceholders {
ChatColor.LIGHT_PURPLE
)));
placeholderRegistry.registerReplacer(plugin, "time", 10, (argument) -> {
placeholderRegistry.registerGlobalPlaceholderReplacer(plugin, "time", 10, (argument) -> {
return Configuration.timeFormat.format(Instant.now());
});
placeholderRegistry.registerFactory(plugin, "animation", animationRegistry);
placeholderRegistry.registerGlobalPlaceholderFactory(plugin, "animation", animationRegistry);
placeholderRegistry.registerFactory(plugin, "world", new WorldPlayersPlaceholderFactory());
placeholderRegistry.registerGlobalPlaceholderFactory(plugin, "world", new WorldPlayersPlaceholderFactory());
placeholderRegistry.registerFactory(plugin, "online", new OnlinePlayersPlaceholderFactory(bungeeServerTracker));
placeholderRegistry.registerGlobalPlaceholderFactory(plugin, "online", new OnlinePlayersPlaceholderFactory(bungeeServerTracker));
placeholderRegistry.registerReplacer(plugin, "max_players", 20, (serverName) -> {
placeholderRegistry.registerGlobalPlaceholderReplacer(plugin, "max_players", 20, (serverName) -> {
if (serverName == null) {
// No argument specified, return max players of this server
return String.valueOf(Bukkit.getMaxPlayers());
@ -62,7 +62,7 @@ public class DefaultPlaceholders {
return String.valueOf(bungeeServerTracker.getCurrentServerInfo(serverName).getMaxPlayers());
});
placeholderRegistry.registerReplacer(plugin, "status", 20, (serverName) -> {
placeholderRegistry.registerGlobalPlaceholderReplacer(plugin, "status", 20, (serverName) -> {
if (serverName == null) {
return NO_SERVER_SPECIFIED_ERROR;
}
@ -79,7 +79,7 @@ public class DefaultPlaceholders {
}
});
placeholderRegistry.registerReplacer(plugin, "motd", 20, (serverName) -> {
placeholderRegistry.registerGlobalPlaceholderReplacer(plugin, "motd", 20, (serverName) -> {
if (serverName == null) {
return NO_SERVER_SPECIFIED_ERROR;
}
@ -91,7 +91,7 @@ public class DefaultPlaceholders {
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine1();
});
placeholderRegistry.registerReplacer(plugin, "motd2", 20, (serverName) -> {
placeholderRegistry.registerGlobalPlaceholderReplacer(plugin, "motd2", 20, (serverName) -> {
if (serverName == null) {
return NO_SERVER_SPECIFIED_ERROR;
}
@ -102,6 +102,14 @@ public class DefaultPlaceholders {
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine2();
});
placeholderRegistry.registerIndividualPlaceholderReplacer(plugin, "player", Integer.MAX_VALUE, (player, argument) -> {
return player.getName();
});
placeholderRegistry.registerIndividualPlaceholderReplacer(plugin, "displayName", 20, (player, argument) -> {
return player.getDisplayName();
});
}
private static List<String> toStringList(ChatColor... colors) {

View File

@ -0,0 +1,13 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.parsing;
@FunctionalInterface
public interface PlaceholderReplaceFunction {
String getReplacement(PlaceholderOccurrence placeholderOccurrence);
}

View File

@ -9,7 +9,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
public class StringWithPlaceholders {
@ -22,14 +21,24 @@ public class StringWithPlaceholders {
public StringWithPlaceholders(String string) {
this.string = string;
this.stringParts = toStringParts(string);
this.stringParts = splitToParts(string);
}
private StringWithPlaceholders(String string, List<StringPart> stringParts) {
this.string = string;
this.stringParts = stringParts;
}
@Override
public String toString() {
return string;
}
public boolean containsPlaceholders() {
return stringParts != null;
}
public boolean containsPlaceholdersMatching(Predicate<PlaceholderOccurrence> filter) {
public boolean anyMatch(Predicate<PlaceholderOccurrence> filter) {
if (stringParts == null) {
return false;
}
@ -37,7 +46,7 @@ public class StringWithPlaceholders {
for (StringPart stringPart : stringParts) {
if (stringPart instanceof PlaceholderStringPart) {
PlaceholderStringPart placeholderStringPart = (PlaceholderStringPart) stringPart;
if (filter.test(placeholderStringPart.content)) {
if (filter.test(placeholderStringPart.placeholderOccurrence)) {
return true;
}
}
@ -46,7 +55,51 @@ public class StringWithPlaceholders {
return false;
}
public String replacePlaceholders(Function<PlaceholderOccurrence, String> replaceFunction) {
public StringWithPlaceholders partiallyReplacePlaceholders(PlaceholderReplaceFunction replaceFunction) {
if (!containsPlaceholders()) {
return this;
}
StringBuilder output = new StringBuilder();
StringBuilder fullOutput = new StringBuilder();
List<StringPart> newStringParts = null; // Lazy initialization
for (StringPart part : stringParts) {
if (part instanceof PlaceholderStringPart) {
PlaceholderStringPart placeholderStringPart = (PlaceholderStringPart) part;
String replacement = replaceFunction.getReplacement(placeholderStringPart.placeholderOccurrence);
if (replacement != null) {
output.append(replacement);
fullOutput.append(replacement);
} else {
// Placeholder was not replaced, may be replaced later
if (newStringParts == null) {
newStringParts = new ArrayList<>();
}
// Append leading literal string, if present
if (output.length() > 0) {
newStringParts.add(new LiteralStringPart(output.toString()));
output.setLength(0);
}
newStringParts.add(placeholderStringPart);
output.append(placeholderStringPart.nonReplacedString);
fullOutput.append(placeholderStringPart.nonReplacedString);
}
} else {
LiteralStringPart literalStringPart = (LiteralStringPart) part;
output.append(literalStringPart.literalString);
fullOutput.append(literalStringPart.literalString);
}
}
if (output.length() > 0 && newStringParts != null) {
newStringParts.add(new LiteralStringPart(output.toString()));
}
return new StringWithPlaceholders(fullOutput.toString(), newStringParts);
}
public String replacePlaceholders(PlaceholderReplaceFunction replaceFunction) {
if (!containsPlaceholders()) {
return string;
}
@ -59,7 +112,7 @@ public class StringWithPlaceholders {
return output.toString();
}
private @Nullable List<StringPart> toStringParts(String string) {
private @Nullable List<StringPart> splitToParts(String string) {
int placeholderStartIndex = -1;
int lastAppendIndex = 0;
List<StringPart> stringParts = null; // Lazy initialization
@ -116,7 +169,7 @@ public class StringWithPlaceholders {
private interface StringPart {
String getValue(Function<PlaceholderOccurrence, String> placeholderReplaceFunction);
String getValue(PlaceholderReplaceFunction placeholderReplaceFunction);
}
@ -130,7 +183,7 @@ public class StringWithPlaceholders {
}
@Override
public String getValue(Function<PlaceholderOccurrence, String> placeholderReplaceFunction) {
public String getValue(PlaceholderReplaceFunction placeholderReplaceFunction) {
return literalString;
}
@ -139,22 +192,22 @@ public class StringWithPlaceholders {
private static class PlaceholderStringPart implements StringPart {
private final PlaceholderOccurrence content;
private final String unparsedString;
private final PlaceholderOccurrence placeholderOccurrence;
private final String nonReplacedString;
PlaceholderStringPart(PlaceholderOccurrence parsedContent, String unparsedString) {
this.content = parsedContent;
this.unparsedString = unparsedString;
PlaceholderStringPart(PlaceholderOccurrence placeholderOccurrence, String nonReplacedString) {
this.placeholderOccurrence = placeholderOccurrence;
this.nonReplacedString = nonReplacedString;
}
@Override
public String getValue(Function<PlaceholderOccurrence, String> placeholderReplaceFunction) {
String replacement = placeholderReplaceFunction.apply(content);
public String getValue(PlaceholderReplaceFunction placeholderReplaceFunction) {
String replacement = placeholderReplaceFunction.getReplacement(placeholderOccurrence);
if (replacement != null) {
return replacement;
} else {
// If no replacement is provided, leave the unparsed placeholder string
return unparsedString;
return nonReplacedString;
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.registry;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class GlobalPlaceholderExpansion extends PlaceholderExpansion {
private final PlaceholderFactory placeholderFactory;
GlobalPlaceholderExpansion(Plugin plugin, String identifier, PlaceholderFactory placeholderFactory) {
super(plugin, identifier);
this.placeholderFactory = placeholderFactory;
}
@Override
public boolean isIndividual() {
return false;
}
@Override
public @Nullable StandardPlaceholder createPlaceholder(String argument) throws PlaceholderException {
Placeholder placeholder;
try {
placeholder = placeholderFactory.getPlaceholder(argument);
} catch (Throwable t) {
throw new PlaceholderException(t, this);
}
if (placeholder != null) {
return new GlobalStandardPlaceholder(placeholder, this);
} else {
return null;
}
}
private static class GlobalStandardPlaceholder extends StandardPlaceholder {
private final @NotNull Placeholder placeholder;
GlobalStandardPlaceholder(@NotNull Placeholder placeholder, @NotNull GlobalPlaceholderExpansion source) {
super(source);
this.placeholder = placeholder;
}
@Override
protected int doGetRefreshIntervalTicks() {
return placeholder.getRefreshIntervalTicks();
}
@Override
protected @Nullable String doGetReplacement(Player player, @Nullable String argument) {
return placeholder.getReplacement(argument);
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.registry;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholder;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholderFactory;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class IndividualPlaceholderExpansion extends PlaceholderExpansion {
private final IndividualPlaceholderFactory placeholderFactory;
IndividualPlaceholderExpansion(Plugin plugin, String identifier, IndividualPlaceholderFactory placeholderFactory) {
super(plugin, identifier);
this.placeholderFactory = placeholderFactory;
}
@Override
public boolean isIndividual() {
return true;
}
@Override
public @Nullable StandardPlaceholder createPlaceholder(String argument) throws PlaceholderException {
IndividualPlaceholder placeholder;
try {
placeholder = placeholderFactory.getPlaceholder(argument);
} catch (Throwable t) {
throw new PlaceholderException(t, this);
}
if (placeholder != null) {
return new IndividualStandardPlaceholder(placeholder, this);
} else {
return null;
}
}
private static class IndividualStandardPlaceholder extends StandardPlaceholder {
private final @NotNull IndividualPlaceholder placeholder;
IndividualStandardPlaceholder(@NotNull IndividualPlaceholder placeholder, @NotNull IndividualPlaceholderExpansion source) {
super(source);
this.placeholder = placeholder;
}
@Override
protected int doGetRefreshIntervalTicks() {
return placeholder.getRefreshIntervalTicks();
}
@Override
protected @Nullable String doGetReplacement(Player player, @Nullable String argument) {
return placeholder.getReplacement(player, argument);
}
}
}

View File

@ -5,37 +5,21 @@
*/
package me.filoghost.holographicdisplays.placeholder.registry;
import me.filoghost.fcommons.Preconditions;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderIdentifier;
import me.filoghost.holographicdisplays.placeholder.parsing.PluginName;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
public class PlaceholderExpansion {
public abstract class PlaceholderExpansion {
private final PluginName pluginName;
private final PlaceholderIdentifier identifier;
private final PlaceholderFactory placeholderFactory;
public PlaceholderExpansion(Plugin plugin, String identifier, PlaceholderFactory placeholderFactory) {
Preconditions.notNull(plugin, "plugin");
Preconditions.notEmpty(identifier, "identifier");
for (char c : identifier.toCharArray()) {
Preconditions.checkArgument(isValidIdentifierCharacter(c), "identifier contains invalid character '" + c + "'");
}
Preconditions.notNull(placeholderFactory, "placeholderFactory");
public PlaceholderExpansion(Plugin plugin, String identifier) {
this.pluginName = new PluginName(plugin);
this.identifier = new PlaceholderIdentifier(identifier);
this.placeholderFactory = placeholderFactory;
}
private boolean isValidIdentifierCharacter(char c) {
return ('a' <= c && c <= 'z')
|| ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9')
|| c == '-'
|| c == '_';
}
public PluginName getPluginName() {
@ -46,8 +30,8 @@ public class PlaceholderExpansion {
return identifier;
}
public PlaceholderFactory getPlaceholderFactory() {
return placeholderFactory;
}
public abstract boolean isIndividual();
public abstract @Nullable StandardPlaceholder createPlaceholder(String argument) throws PlaceholderException;
}

View File

@ -8,55 +8,63 @@ package me.filoghost.holographicdisplays.placeholder.registry;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Table;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholderFactory;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholderReplacer;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
import me.filoghost.holographicdisplays.placeholder.RelativePlaceholder;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholder;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderIdentifier;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import me.filoghost.holographicdisplays.placeholder.parsing.PluginName;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PlaceholderRegistry {
private final Table<PlaceholderIdentifier, PluginName, PlaceholderExpansion> placeholderExpansions;
private final Map<PlaceholderIdentifier, RelativePlaceholder> relativePlaceholders;
private Runnable changeListener;
public PlaceholderRegistry() {
this.placeholderExpansions = HashBasedTable.create();
relativePlaceholders = new HashMap<>();
registerRelative("player", Player::getName);
registerRelative("displayName", Player::getDisplayName);
}
public void setChangeListener(Runnable changeListener) {
this.changeListener = changeListener;
}
public void registerIndividualPlaceholderReplacer(Plugin plugin, String identifier, int refreshIntervalTicks, IndividualPlaceholderReplacer placeholderReplacer) {
registerIndividualPlaceholder(plugin, identifier, new SimpleIndividualPlaceholder(refreshIntervalTicks, placeholderReplacer));
}
public void registerRelative(String identifier, RelativePlaceholder relativePlaceholder) {
relativePlaceholders.put(new PlaceholderIdentifier(identifier), relativePlaceholder);
public void registerIndividualPlaceholder(Plugin plugin, String identifier, IndividualPlaceholder placeholder) {
registerIndividualPlaceholderFactory(plugin, identifier, (String argument) -> placeholder);
}
public void registerReplacer(Plugin plugin, String identifier, int refreshIntervalTicks, PlaceholderReplacer placeholderReplacer) {
register(plugin, identifier, new SimplePlaceholder(refreshIntervalTicks, placeholderReplacer));
public void registerIndividualPlaceholderFactory(Plugin plugin, String identifier, IndividualPlaceholderFactory factory) {
PlaceholderExpansion expansion = new IndividualPlaceholderExpansion(plugin, identifier, factory);
registerExpansion(expansion);
}
public void registerGlobalPlaceholderReplacer(Plugin plugin, String identifier, int refreshIntervalTicks, PlaceholderReplacer placeholderReplacer) {
registerGlobalPlaceholder(plugin, identifier, new SimpleGlobalPlaceholder(refreshIntervalTicks, placeholderReplacer));
}
public void register(Plugin plugin, String identifier, Placeholder placeholder) {
registerFactory(plugin, identifier, new SingletonPlaceholderFactory(placeholder));
public void registerGlobalPlaceholder(Plugin plugin, String identifier, Placeholder placeholder) {
registerGlobalPlaceholderFactory(plugin, identifier, (String argument) -> placeholder);
}
public void registerFactory(Plugin plugin, String identifier, PlaceholderFactory placeholderFactory) {
PlaceholderExpansion expansion = new PlaceholderExpansion(plugin, identifier, placeholderFactory);
public void registerGlobalPlaceholderFactory(Plugin plugin, String identifier, PlaceholderFactory factory) {
PlaceholderExpansion expansion = new GlobalPlaceholderExpansion(plugin, identifier, factory);
registerExpansion(expansion);
}
private void registerExpansion(PlaceholderExpansion expansion) {
placeholderExpansions.put(expansion.getIdentifier(), expansion.getPluginName(), expansion);
changeListener.run();
}
@ -72,7 +80,7 @@ public class PlaceholderRegistry {
changeListener.run();
}
public @Nullable PlaceholderExpansion findBestMatch(PlaceholderOccurrence textOccurrence) {
public @Nullable PlaceholderExpansion find(PlaceholderOccurrence textOccurrence) {
PluginName pluginName = textOccurrence.getPluginName();
PlaceholderIdentifier identifier = textOccurrence.getIdentifier();
@ -101,9 +109,4 @@ public class PlaceholderRegistry {
return placeholderExpansions.contains(new PlaceholderIdentifier(identifier), new PluginName(plugin));
}
public @Nullable RelativePlaceholder findRelative(PlaceholderOccurrence textOccurrence) {
PlaceholderIdentifier identifier = textOccurrence.getIdentifier();
return relativePlaceholders.get(identifier);
}
}

View File

@ -5,18 +5,15 @@
*/
package me.filoghost.holographicdisplays.placeholder.registry;
import me.filoghost.fcommons.Preconditions;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
class SimplePlaceholder implements Placeholder {
class SimpleGlobalPlaceholder implements Placeholder {
private final int refreshIntervalTicks;
private final PlaceholderReplacer placeholderReplacer;
SimplePlaceholder(int refreshIntervalTicks, PlaceholderReplacer placeholderReplacer) {
Preconditions.checkArgument(refreshIntervalTicks >= 0, "refreshIntervalTicks cannot be negative");
Preconditions.notNull(placeholderReplacer, "placeholderReplacer");
SimpleGlobalPlaceholder(int refreshIntervalTicks, PlaceholderReplacer placeholderReplacer) {
this.refreshIntervalTicks = refreshIntervalTicks;
this.placeholderReplacer = placeholderReplacer;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.registry;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholder;
import me.filoghost.holographicdisplays.api.placeholder.IndividualPlaceholderReplacer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class SimpleIndividualPlaceholder implements IndividualPlaceholder {
private final int refreshIntervalTicks;
private final IndividualPlaceholderReplacer placeholderReplacer;
SimpleIndividualPlaceholder(int refreshIntervalTicks, IndividualPlaceholderReplacer placeholderReplacer) {
this.refreshIntervalTicks = refreshIntervalTicks;
this.placeholderReplacer = placeholderReplacer;
}
@Override
public int getRefreshIntervalTicks() {
return refreshIntervalTicks;
}
@Override
public String getReplacement(@NotNull Player player, @Nullable String argument) {
return placeholderReplacer.getReplacement(player, argument);
}
}

View File

@ -1,26 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.registry;
import me.filoghost.fcommons.Preconditions;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
class SingletonPlaceholderFactory implements PlaceholderFactory {
private final Placeholder placeholder;
SingletonPlaceholderFactory(Placeholder placeholder) {
Preconditions.notNull(placeholder, "placeholder");
this.placeholder = placeholder;
}
@Override
public Placeholder getPlaceholder(String argument) {
return placeholder;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.fcommons.logging.Log;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.TickClock;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import java.util.Map;
import java.util.WeakHashMap;
class PlaceholderExceptionHandler {
private final TickClock tickClock;
private final Map<PlaceholderExpansion, Long> lastErrorLogByPlaceholderExpansion;
PlaceholderExceptionHandler(TickClock tickClock) {
this.tickClock = tickClock;
this.lastErrorLogByPlaceholderExpansion = new WeakHashMap<>();
}
void handle(PlaceholderException exception) {
PlaceholderExpansion placeholderExpansion = exception.getPlaceholderExpansion();
Long lastErrorLog = lastErrorLogByPlaceholderExpansion.get(placeholderExpansion);
long currentTick = tickClock.getCurrentTick();
if (lastErrorLog != null && currentTick - lastErrorLog < 20) {
return; // Avoid spamming the console too frequently
}
lastErrorLogByPlaceholderExpansion.put(placeholderExpansion, currentTick);
Log.warning("The placeholder \"" + placeholderExpansion.getIdentifier() + "\""
+ " registered by the plugin " + placeholderExpansion.getPluginName()
+ " generated an exception."
+ " Please contact the author of " + placeholderExpansion.getPluginName(),
exception.getCause());
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
import me.filoghost.holographicdisplays.placeholder.parsing.StringWithPlaceholders;
import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
public class PlaceholderLineTracker {
private final PlaceholderTracker replacementTracker;
private final Map<StandardTextLine, TrackedLine> trackedLines;
public PlaceholderLineTracker(PlaceholderTracker replacementTracker) {
this.replacementTracker = replacementTracker;
this.trackedLines = new LinkedHashMap<>();
}
public void updateEntitiesWithGlobalPlaceholders() {
Iterator<TrackedLine> iterator = trackedLines.values().iterator();
while (iterator.hasNext()) {
TrackedLine trackedLine = iterator.next();
if (trackedLine.shouldBeUntracked()) {
iterator.remove();
continue;
}
trackedLine.updateEntityWithGlobalPlaceholders();
}
}
public void onTextLineChange(StandardTextLine line) {
TrackedLine trackedLine = createTrackedLineIfNeeded(line);
if (trackedLine != null) {
trackedLines.put(line, trackedLine);
trackedLine.updateEntityWithGlobalPlaceholders(); // Update placeholders instantly to avoid flashing the non-replaced text
} else {
TrackedLine untrackedLine = trackedLines.remove(line);
if (untrackedLine != null) {
untrackedLine.restoreOriginalName();
}
}
}
private @Nullable TrackedLine createTrackedLineIfNeeded(StandardTextLine textLine) {
if (!textLine.isAllowPlaceholders()) {
return null;
}
String text = textLine.getText();
if (text == null || text.isEmpty()) {
return null;
}
NMSArmorStand entity = textLine.getNMSArmorStand();
if (entity == null) {
return null;
}
StringWithPlaceholders textWithPlaceholders = new StringWithPlaceholders(text);
if (!textWithPlaceholders.containsPlaceholders()) {
return null;
}
return new TrackedLine(replacementTracker, textLine, entity, textWithPlaceholders);
}
public TrackedLine getTrackedLine(StandardTextLine line) {
return trackedLines.get(line);
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import me.filoghost.holographicdisplays.placeholder.TickClock;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import me.filoghost.holographicdisplays.placeholder.parsing.StringWithPlaceholders;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.WeakHashMap;
public class PlaceholderTracker {
private final PlaceholderRegistry registry;
private final TickClock tickClock;
private final PlaceholderExceptionHandler exceptionHandler;
// Use WeakHashMap to ensure that when a PlaceholderOccurrence is no longer referenced in other objects
// the corresponding entry is removed from the map automatically.
private final WeakHashMap<PlaceholderOccurrence, TrackedPlaceholder> activePlaceholders;
public PlaceholderTracker(PlaceholderRegistry registry, TickClock tickClock) {
this.registry = registry;
this.tickClock = tickClock;
this.exceptionHandler = new PlaceholderExceptionHandler(tickClock);
this.activePlaceholders = new WeakHashMap<>();
registry.setChangeListener(this::onRegistryChange);
}
private void onRegistryChange() {
// Remove entries whose placeholder expansion sources are outdated
activePlaceholders.entrySet().removeIf(entry -> {
PlaceholderOccurrence placeholderOccurrence = entry.getKey();
PlaceholderExpansion currentSource = entry.getValue().getSource();
PlaceholderExpansion newSource = registry.find(placeholderOccurrence);
return !Objects.equals(currentSource, newSource);
});
}
public @Nullable String updateAndGetGlobalReplacement(PlaceholderOccurrence placeholderOccurrence) {
return updateAndGetReplacement(placeholderOccurrence, null, false);
}
public @Nullable String updateAndGetIndividualReplacement(PlaceholderOccurrence placeholderOccurrence, Player player) {
return updateAndGetReplacement(placeholderOccurrence, player, true);
}
private @Nullable String updateAndGetReplacement(PlaceholderOccurrence placeholderOccurrence, Player player, boolean individual) {
try {
TrackedPlaceholder trackedPlaceholder = getTrackedPlaceholder(placeholderOccurrence);
if (trackedPlaceholder.isIndividual() == individual) {
return trackedPlaceholder.updateAndGetReplacement(player, tickClock.getCurrentTick());
} else {
return null;
}
} catch (PlaceholderException e) {
exceptionHandler.handle(e);
return "[Error]";
}
}
private @NotNull TrackedPlaceholder getTrackedPlaceholder(PlaceholderOccurrence placeholderOccurrence) throws PlaceholderException {
TrackedPlaceholder trackedPlaceholder = activePlaceholders.get(placeholderOccurrence);
if (trackedPlaceholder == null) {
trackedPlaceholder = createTrackedPlaceholder(placeholderOccurrence);
activePlaceholders.put(placeholderOccurrence, trackedPlaceholder);
}
return trackedPlaceholder;
}
private TrackedPlaceholder createTrackedPlaceholder(PlaceholderOccurrence placeholderOccurrence) throws PlaceholderException {
PlaceholderExpansion placeholderExpansion = registry.find(placeholderOccurrence);
StandardPlaceholder placeholder;
if (placeholderExpansion != null) {
placeholder = placeholderExpansion.createPlaceholder(placeholderOccurrence.getArgument());
} else {
placeholder = null;
}
if (placeholder == null) {
return new TrackedNullPlaceholder(placeholderExpansion);
} else if (placeholder.isIndividual()) {
return new TrackedIndividualPlaceholder(placeholder, placeholderOccurrence);
} else {
return new TrackedGlobalPlaceholder(placeholder, placeholderOccurrence);
}
}
public boolean containsIndividualPlaceholders(StringWithPlaceholders nameWithPlaceholders) {
return nameWithPlaceholders.anyMatch(occurrence -> {
PlaceholderExpansion placeholderExpansion = registry.find(occurrence);
return placeholderExpansion != null && placeholderExpansion.isIndividual();
});
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class ReplacementHolder {
private final @NotNull StandardPlaceholder placeholder;
private final @NotNull PlaceholderOccurrence placeholderOccurrence;
private @Nullable String currentReplacement;
private long lastUpdateTick = -1;
ReplacementHolder(@NotNull StandardPlaceholder placeholder, @NotNull PlaceholderOccurrence placeholderOccurrence) {
this.placeholder = placeholder;
this.placeholderOccurrence = placeholderOccurrence;
}
@Nullable String updateAndGet(Player player, long currentTick) throws PlaceholderException {
if (needsRefresh(currentTick)) {
currentReplacement = placeholder.getReplacement(player, placeholderOccurrence.getArgument());
lastUpdateTick = currentTick;
}
return currentReplacement;
}
private boolean needsRefresh(long currentTick) throws PlaceholderException {
if (lastUpdateTick == currentTick) {
return false; // No need to refresh
}
if (lastUpdateTick == -1) {
return true; // Force at least the initial refresh
}
return currentTick - lastUpdateTick >= placeholder.getRefreshIntervalTicks();
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
class TrackedGlobalPlaceholder extends TrackedPlaceholder {
private final ReplacementHolder replacementHolder;
TrackedGlobalPlaceholder(@NotNull StandardPlaceholder placeholder, @NotNull PlaceholderOccurrence placeholderOccurrence) {
super(placeholder.getSource());
this.replacementHolder = new ReplacementHolder(placeholder, placeholderOccurrence);
}
@Override
boolean isIndividual() {
return false;
}
@Override
@Nullable String updateAndGetReplacement(Player player, long currentTick) throws PlaceholderException {
return replacementHolder.updateAndGet(player, currentTick);
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.StandardPlaceholder;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.WeakHashMap;
class TrackedIndividualPlaceholder extends TrackedPlaceholder {
private final @NotNull StandardPlaceholder placeholder;
private final @NotNull PlaceholderOccurrence placeholderOccurrence;
private final WeakHashMap<Player, ReplacementHolder> replacementHolderByPlayer;
TrackedIndividualPlaceholder(@NotNull StandardPlaceholder placeholder, @NotNull PlaceholderOccurrence placeholderOccurrence) {
super(placeholder.getSource());
this.placeholder = placeholder;
this.placeholderOccurrence = placeholderOccurrence;
this.replacementHolderByPlayer = new WeakHashMap<>();
}
@Override
boolean isIndividual() {
return true;
}
@Override
@Nullable String updateAndGetReplacement(Player player, long currentTick) throws PlaceholderException {
return replacementHolderByPlayer
.computeIfAbsent(player, key -> new ReplacementHolder(placeholder, placeholderOccurrence))
.updateAndGet(player, currentTick);
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderReplaceFunction;
import me.filoghost.holographicdisplays.placeholder.parsing.StringWithPlaceholders;
import org.bukkit.entity.Player;
public class TrackedLine {
private final PlaceholderTracker placeholderTracker;
private final StandardTextLine textLine;
private final NMSArmorStand entity;
private final StringWithPlaceholders textWithPlaceholders;
private final boolean containsIndividualPlaceholders;
private final PlaceholderReplaceFunction globalReplaceFunction;
private StringWithPlaceholders textWithGlobalReplacements;
TrackedLine(
PlaceholderTracker placeholderTracker,
StandardTextLine textLine,
NMSArmorStand entity,
StringWithPlaceholders textWithPlaceholders) {
this.placeholderTracker = placeholderTracker;
this.textLine = textLine;
this.entity = entity;
this.textWithPlaceholders = textWithPlaceholders;
this.containsIndividualPlaceholders = placeholderTracker.containsIndividualPlaceholders(textWithPlaceholders);
this.globalReplaceFunction = placeholderTracker::updateAndGetGlobalReplacement;
}
void updateEntityWithGlobalPlaceholders() {
textWithGlobalReplacements = textWithPlaceholders.partiallyReplacePlaceholders(globalReplaceFunction);
entity.setCustomNameNMS(textWithGlobalReplacements.toString());
}
public String replaceIndividualPlaceholders(Player player) {
if (containsIndividualPlaceholders) {
return textWithGlobalReplacements.replacePlaceholders((occurrence) ->
placeholderTracker.updateAndGetIndividualReplacement(occurrence, player));
} else {
return textWithGlobalReplacements.toString();
}
}
void restoreOriginalName() {
if (!entity.isDeadNMS()) {
entity.setCustomNameNMS(textLine.getText());
}
}
public boolean shouldBeUntracked() {
return entity.isDeadNMS();
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
class TrackedNullPlaceholder extends TrackedPlaceholder {
TrackedNullPlaceholder(@Nullable PlaceholderExpansion placeholderExpansion) {
super(placeholderExpansion);
}
@Override
@Nullable String updateAndGetReplacement(Player player, long currentTick) {
return null;
}
@Override
boolean isIndividual() {
return false;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.placeholder.tracking;
import me.filoghost.holographicdisplays.placeholder.PlaceholderException;
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
abstract class TrackedPlaceholder {
private final @Nullable PlaceholderExpansion source;
TrackedPlaceholder(@Nullable PlaceholderExpansion source) {
this.source = source;
}
final @Nullable PlaceholderExpansion getSource() {
return source;
}
abstract boolean isIndividual();
abstract @Nullable String updateAndGetReplacement(Player player, long currentTick) throws PlaceholderException;
}