mirror of
https://github.com/filoghost/HolographicDisplays.git
synced 2024-12-21 08:17:36 +01:00
Partial placeholder refactoring
This commit is contained in:
parent
19f3a87ba3
commit
750e74360a
api/src/main/java/me/filoghost/holographicdisplays/api
core/src/main/java/me/filoghost/holographicdisplays/core
plugin/src
main/java/me/filoghost/holographicdisplays
DefaultBackendAPI.javaHolographicDisplays.java
bridge
object
api
base
internal
placeholder
DynamicLineData.javaPlaceholder.javaPlaceholderException.javaPlaceholderManager.javaPlaceholdersManager.javaPlaceholdersRegistry.javaPlaceholdersReplacementTracker.javaPlaceholdersUpdateTask.java
internal
AnimationPlaceholder.javaAnimationRegistry.javaDefaultPlaceholders.javaOnlinePlayersPlaceholderFactory.javaStaticPlaceholder.javaWorldPlayersPlaceholderFactory.java
parsing
registry
util
task
test/java/me/filoghost/holographicdisplays/placeholder/parsing
@ -48,27 +48,16 @@ public class HologramsAPI {
|
||||
return BackendAPI.getImplementation().getHolograms(plugin);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers a new placeholder that can be used in holograms created with commands.
|
||||
* With this method, you can basically expand the core of HolographicDisplays.
|
||||
*
|
||||
* @param plugin the owner plugin of the placeholder
|
||||
* @param textPlaceholder the text that the placeholder will be associated to (e.g.: "{onlinePlayers}")
|
||||
* @param refreshRate the refresh rate of the placeholder, in seconds. Keep in mind that the minimum is 0.1 seconds, and that will be rounded to tenths of seconds
|
||||
* @param replacer the implementation that will return the text to replace the placeholder, where the update() method is called every <b>refreshRate</b> seconds
|
||||
* @return true if the registration was successfull, false if it was already registered
|
||||
*/
|
||||
public static boolean registerPlaceholder(Plugin plugin, String textPlaceholder, double refreshRate, PlaceholderReplacer replacer) {
|
||||
return BackendAPI.getImplementation().registerPlaceholder(plugin, textPlaceholder, refreshRate, replacer);
|
||||
public static void registerPlaceholder(Plugin plugin, String identifier, int refreshIntervalTicks, PlaceholderReplacer replacer) {
|
||||
BackendAPI.getImplementation().registerPlaceholder(plugin, identifier, refreshIntervalTicks, replacer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all the placeholders registered by a given plugin.
|
||||
* Returns all the placeholder identifiers registered by a given plugin.
|
||||
*
|
||||
* @param plugin the plugin to search for
|
||||
* @return a collection of placeholders registered by the plugin
|
||||
* @return a collection of placeholder identifiers registered by the plugin
|
||||
*/
|
||||
public static Collection<String> getRegisteredPlaceholders(Plugin plugin) {
|
||||
return BackendAPI.getImplementation().getRegisteredPlaceholders(plugin);
|
||||
@ -79,11 +68,10 @@ public class HologramsAPI {
|
||||
* Unregister a placeholder created by a plugin.
|
||||
*
|
||||
* @param plugin the plugin that owns the placeholder
|
||||
* @param textPlaceholder the placeholder to remove
|
||||
* @return true if found and removed, false otherwise
|
||||
* @param identifier the identifier of the placeholder to remove
|
||||
*/
|
||||
public static boolean unregisterPlaceholder(Plugin plugin, String textPlaceholder) {
|
||||
return BackendAPI.getImplementation().unregisterPlaceholder(plugin, textPlaceholder);
|
||||
public static void unregisterPlaceholder(Plugin plugin, String identifier) {
|
||||
BackendAPI.getImplementation().unregisterPlaceholder(plugin, identifier);
|
||||
}
|
||||
|
||||
|
||||
|
@ -33,11 +33,11 @@ public abstract class BackendAPI {
|
||||
|
||||
public abstract Collection<Hologram> getHolograms(Plugin plugin);
|
||||
|
||||
public abstract boolean registerPlaceholder(Plugin plugin, String textPlaceholder, double refreshRate, PlaceholderReplacer replacer);
|
||||
public abstract void registerPlaceholder(Plugin plugin, String identifier, int refreshIntervalTicks, PlaceholderReplacer replacer);
|
||||
|
||||
public abstract Collection<String> getRegisteredPlaceholders(Plugin plugin);
|
||||
|
||||
public abstract boolean unregisterPlaceholder(Plugin plugin, String textPlaceholder);
|
||||
public abstract void unregisterPlaceholder(Plugin plugin, String identifier);
|
||||
|
||||
public abstract void unregisterPlaceholders(Plugin plugin);
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.api.placeholder;
|
||||
|
||||
public interface Placeholder extends PlaceholderReplacer {
|
||||
|
||||
int getRefreshIntervalTicks();
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.api.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface PlaceholderFactory {
|
||||
|
||||
Placeholder getPlaceholder(@Nullable String argument);
|
||||
|
||||
}
|
@ -5,12 +5,23 @@
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.api.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Simple callback to provide a placeholder replacement.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PlaceholderReplacer {
|
||||
|
||||
/**
|
||||
* Called to update a placeholder's replacement.
|
||||
* @return the replacement
|
||||
* Callback for providing a placeholder replacement, given the argument of the placeholder (if present).
|
||||
* <p>
|
||||
* For example, the argument of {test} is null, the argument of {test: hello world} is the string "hello world".
|
||||
* <p>
|
||||
* <b>Warning</b>: this method should be performance efficient, as it may be invoked often.
|
||||
*
|
||||
* @return the placeholder replacement
|
||||
*/
|
||||
String update();
|
||||
String getReplacement(@Nullable String argument);
|
||||
|
||||
}
|
||||
|
@ -66,26 +66,6 @@ public class Utils {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String sanitize(String s) {
|
||||
return s != null ? s : "null";
|
||||
}
|
||||
|
||||
|
||||
public static boolean isThereNonNull(Object... objects) {
|
||||
if (objects == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Object object : objects) {
|
||||
if (object != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static String formatExceptionMessage(Throwable t) {
|
||||
return formatExceptionMessage(t.getMessage());
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ public interface StandardTextLine extends StandardTouchableLine {
|
||||
|
||||
String getText();
|
||||
|
||||
boolean isAllowPlaceholders();
|
||||
|
||||
Collection<RelativePlaceholder> getRelativePlaceholders();
|
||||
|
||||
NMSArmorStand getNMSArmorStand();
|
||||
|
@ -10,31 +10,37 @@ import org.bukkit.entity.Player;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
public abstract class RelativePlaceholder {
|
||||
public class RelativePlaceholder implements RelativePlaceholderReplacer {
|
||||
|
||||
private static final Collection<RelativePlaceholder> registry = new HashSet<>();
|
||||
|
||||
// The placeholder itself, something like {player}.
|
||||
private final String textPlaceholder;
|
||||
|
||||
public RelativePlaceholder(String textPlaceholder) {
|
||||
private final RelativePlaceholderReplacer replacer;
|
||||
|
||||
public RelativePlaceholder(String textPlaceholder, RelativePlaceholderReplacer replacer) {
|
||||
this.textPlaceholder = textPlaceholder;
|
||||
this.replacer = replacer;
|
||||
}
|
||||
|
||||
public String getTextPlaceholder() {
|
||||
return textPlaceholder;
|
||||
}
|
||||
|
||||
public abstract String getReplacement(Player player);
|
||||
@Override
|
||||
public String getReplacement(Player player) {
|
||||
return replacer.getReplacement(player);
|
||||
}
|
||||
|
||||
public static void register(RelativePlaceholder relativePlaceholder) {
|
||||
public static void register(String textPlaceholder, RelativePlaceholderReplacer replacer) {
|
||||
for (RelativePlaceholder existingPlaceholder : registry) {
|
||||
if (existingPlaceholder.getTextPlaceholder().equals(relativePlaceholder.getTextPlaceholder())) {
|
||||
if (existingPlaceholder.getTextPlaceholder().equals(textPlaceholder)) {
|
||||
throw new IllegalArgumentException("Relative placeholder already registered");
|
||||
}
|
||||
}
|
||||
|
||||
registry.add(relativePlaceholder);
|
||||
registry.add(new RelativePlaceholder(textPlaceholder, replacer));
|
||||
}
|
||||
|
||||
public static Collection<RelativePlaceholder> getRegistry() {
|
||||
@ -42,21 +48,8 @@ public abstract class RelativePlaceholder {
|
||||
}
|
||||
|
||||
static {
|
||||
register(new RelativePlaceholder("{player}") {
|
||||
|
||||
@Override
|
||||
public String getReplacement(Player player) {
|
||||
return player.getName();
|
||||
}
|
||||
});
|
||||
|
||||
register(new RelativePlaceholder("{displayname}") {
|
||||
|
||||
@Override
|
||||
public String getReplacement(Player player) {
|
||||
return player.getDisplayName();
|
||||
}
|
||||
});
|
||||
register("{player}", Player::getName);
|
||||
register("{displayname}", Player::getDisplayName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.core.placeholder;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface RelativePlaceholderReplacer {
|
||||
|
||||
String getReplacement(Player player);
|
||||
|
||||
}
|
@ -11,8 +11,7 @@ import me.filoghost.holographicdisplays.api.internal.BackendAPI;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||
import me.filoghost.holographicdisplays.core.nms.NMSManager;
|
||||
import me.filoghost.holographicdisplays.object.api.APIHologramManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.Placeholder;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholdersRegistry;
|
||||
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
@ -24,9 +23,9 @@ public class DefaultBackendAPI extends BackendAPI {
|
||||
|
||||
private final APIHologramManager apiHologramManager;
|
||||
private final NMSManager nmsManager;
|
||||
private final PlaceholdersRegistry placeholderRegistry;
|
||||
private final PlaceholderRegistry placeholderRegistry;
|
||||
|
||||
public DefaultBackendAPI(APIHologramManager apiHologramManager, NMSManager nmsManager, PlaceholdersRegistry placeholderRegistry) {
|
||||
public DefaultBackendAPI(APIHologramManager apiHologramManager, NMSManager nmsManager, PlaceholderRegistry placeholderRegistry) {
|
||||
this.apiHologramManager = apiHologramManager;
|
||||
this.nmsManager = nmsManager;
|
||||
this.placeholderRegistry = placeholderRegistry;
|
||||
@ -43,12 +42,12 @@ public class DefaultBackendAPI extends BackendAPI {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerPlaceholder(Plugin plugin, String textPlaceholder, double refreshRate, PlaceholderReplacer replacer) {
|
||||
Preconditions.notNull(textPlaceholder, "textPlaceholder");
|
||||
Preconditions.checkArgument(refreshRate >= 0, "refreshRate should be positive");
|
||||
public void registerPlaceholder(Plugin plugin, String identifier, int refreshIntervalTicks, PlaceholderReplacer replacer) {
|
||||
Preconditions.notNull(identifier, "identifier");
|
||||
Preconditions.checkArgument(refreshIntervalTicks >= 0, "refreshIntervalTicks should be positive");
|
||||
Preconditions.notNull(replacer, "replacer");
|
||||
|
||||
return placeholderRegistry.register(new Placeholder(plugin, textPlaceholder, refreshRate, replacer));
|
||||
placeholderRegistry.registerReplacer(plugin, identifier, refreshIntervalTicks, replacer);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -66,22 +65,20 @@ public class DefaultBackendAPI extends BackendAPI {
|
||||
@Override
|
||||
public Collection<String> getRegisteredPlaceholders(Plugin plugin) {
|
||||
Preconditions.notNull(plugin, "plugin");
|
||||
return placeholderRegistry.getTextPlaceholdersByPlugin(plugin);
|
||||
return placeholderRegistry.getRegisteredIdentifiers(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterPlaceholder(Plugin plugin, String textPlaceholder) {
|
||||
public void unregisterPlaceholder(Plugin plugin, String identifier) {
|
||||
Preconditions.notNull(plugin, "plugin");
|
||||
Preconditions.notNull(textPlaceholder, "textPlaceholder");
|
||||
return placeholderRegistry.unregister(plugin, textPlaceholder);
|
||||
Preconditions.notNull(identifier, "identifier");
|
||||
placeholderRegistry.unregister(plugin, identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterPlaceholders(Plugin plugin) {
|
||||
Preconditions.notNull(plugin, "plugin");
|
||||
for (String placeholder : getRegisteredPlaceholders(plugin)) {
|
||||
unregisterPlaceholder(plugin, placeholder);
|
||||
}
|
||||
placeholderRegistry.unregisterAll(plugin);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ 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.AnimationsRegistry;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.internal.AnimationRegistry;
|
||||
import me.filoghost.holographicdisplays.placeholder.internal.DefaultPlaceholders;
|
||||
import me.filoghost.holographicdisplays.util.NMSVersion;
|
||||
import org.bstats.bukkit.MetricsLite;
|
||||
import org.bukkit.Bukkit;
|
||||
@ -47,8 +47,8 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
||||
private InternalHologramManager internalHologramManager;
|
||||
private APIHologramManager apiHologramManager;
|
||||
private BungeeServerTracker bungeeServerTracker;
|
||||
private AnimationsRegistry animationRegistry;
|
||||
private PlaceholdersManager placeholderManager;
|
||||
private AnimationRegistry animationRegistry;
|
||||
private PlaceholderManager placeholderManager;
|
||||
|
||||
@Override
|
||||
public void onCheckedEnable() throws PluginEnableException {
|
||||
@ -91,8 +91,8 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
||||
}
|
||||
|
||||
bungeeServerTracker = new BungeeServerTracker(this);
|
||||
animationRegistry = new AnimationsRegistry();
|
||||
placeholderManager = new PlaceholdersManager(bungeeServerTracker, animationRegistry);
|
||||
animationRegistry = new AnimationRegistry();
|
||||
placeholderManager = new PlaceholderManager();
|
||||
configManager = new ConfigManager(getDataFolder().toPath());
|
||||
internalHologramManager = new InternalHologramManager(nmsManager, placeholderManager);
|
||||
apiHologramManager = new APIHologramManager(nmsManager, placeholderManager);
|
||||
@ -111,8 +111,7 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
||||
ProtocolLibHook.setup(this, nmsManager, this, errorCollector);
|
||||
|
||||
// Start repeating tasks.
|
||||
placeholderManager.startRefreshTask(this);
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new WorldPlayerCounterTask(), 0L, 3 * 20);
|
||||
placeholderManager.startUpdaterTask(this);
|
||||
|
||||
HologramCommandManager commandManager = new HologramCommandManager(configManager, internalHologramManager, nmsManager);
|
||||
commandManager.register(this);
|
||||
@ -124,7 +123,7 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
||||
registerListener(updateNotificationListener);
|
||||
|
||||
// Enable the API.
|
||||
BackendAPI.setImplementation(new DefaultBackendAPI(apiHologramManager, nmsManager, placeholderManager.getRegistry()));
|
||||
BackendAPI.setImplementation(new DefaultBackendAPI(apiHologramManager, nmsManager, placeholderManager.getPlaceholderRegistry()));
|
||||
|
||||
// Register bStats metrics
|
||||
int pluginID = 3123;
|
||||
@ -139,7 +138,8 @@ public class HolographicDisplays extends FCommonsPlugin implements ProtocolPacke
|
||||
}
|
||||
|
||||
public void load(boolean deferHologramsCreation, ErrorCollector errorCollector) {
|
||||
placeholderManager.untrackAll();
|
||||
DefaultPlaceholders.resetAndRegister(placeholderManager.getPlaceholderRegistry(), animationRegistry, bungeeServerTracker);
|
||||
|
||||
internalHologramManager.clearAll();
|
||||
|
||||
configManager.reloadCustomPlaceholders(errorCollector);
|
||||
|
@ -14,6 +14,7 @@ import me.filoghost.holographicdisplays.disk.Configuration;
|
||||
import me.filoghost.holographicdisplays.disk.ServerAddress;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
@ -47,7 +48,7 @@ public class BungeeServerTracker {
|
||||
this::runPeriodicUpdateTask, 1, timeUnit.toSeconds(updateInterval) * 20L);
|
||||
}
|
||||
|
||||
public ServerInfo getCurrentServerInfo(String serverName) {
|
||||
public ServerInfo getCurrentServerInfo(@NotNull String serverName) {
|
||||
// If it wasn't already tracked, send an update request instantly
|
||||
if (!Configuration.pingerEnabled && !trackedServers.containsKey(serverName)) {
|
||||
bungeeMessenger.sendPlayerCountRequest(serverName);
|
||||
|
@ -101,8 +101,11 @@ class PacketListener extends PacketAdapter {
|
||||
}
|
||||
|
||||
StandardTextLine textLine = (StandardTextLine) hologramLine;
|
||||
Collection<RelativePlaceholder> relativePlaceholders = textLine.getRelativePlaceholders();
|
||||
if (!textLine.isAllowPlaceholders()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<RelativePlaceholder> relativePlaceholders = textLine.getRelativePlaceholders();
|
||||
if (relativePlaceholders == null || relativePlaceholders.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import me.filoghost.holographicdisplays.api.line.TextLine;
|
||||
import me.filoghost.holographicdisplays.core.nms.NMSManager;
|
||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||
import me.filoghost.holographicdisplays.object.base.BaseHologram;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -31,7 +31,7 @@ public class APIHologram extends BaseHologram<APIHologramLine> implements Hologr
|
||||
|
||||
private boolean allowPlaceholders;
|
||||
|
||||
protected APIHologram(Location source, Plugin plugin, NMSManager nmsManager, APIHologramManager apiHologramManager, PlaceholdersManager placeholderManager) {
|
||||
protected APIHologram(Location source, Plugin plugin, NMSManager nmsManager, APIHologramManager apiHologramManager, PlaceholderManager placeholderManager) {
|
||||
super(source, nmsManager, placeholderManager);
|
||||
Preconditions.notNull(plugin, "plugin");
|
||||
this.plugin = plugin;
|
||||
|
@ -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.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@ -20,9 +20,9 @@ import java.util.List;
|
||||
public class APIHologramManager extends BaseHologramManager<APIHologram> {
|
||||
|
||||
private final NMSManager nmsManager;
|
||||
private final PlaceholdersManager placeholderManager;
|
||||
private final PlaceholderManager placeholderManager;
|
||||
|
||||
public APIHologramManager(NMSManager nmsManager, PlaceholdersManager placeholderManager) {
|
||||
public APIHologramManager(NMSManager nmsManager, PlaceholderManager placeholderManager) {
|
||||
this.nmsManager = nmsManager;
|
||||
this.placeholderManager = placeholderManager;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public class APITextLine extends BaseTextLine implements TextLine, APIHologramLi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAllowPlaceholders() {
|
||||
public boolean isAllowPlaceholders() {
|
||||
return parent.isAllowPlaceholders();
|
||||
}
|
||||
|
||||
|
@ -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.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
@ -22,13 +22,13 @@ import java.util.List;
|
||||
public abstract class BaseHologram<T extends StandardHologramLine> extends BaseHologramComponent implements StandardHologram {
|
||||
|
||||
private final NMSManager nmsManager;
|
||||
private final PlaceholdersManager placeholderManager;
|
||||
private final PlaceholderManager placeholderManager;
|
||||
private final List<T> lines;
|
||||
private final List<T> unmodifiableLinesView;
|
||||
|
||||
private boolean deleted;
|
||||
|
||||
public BaseHologram(Location location, NMSManager nmsManager, PlaceholdersManager placeholderManager) {
|
||||
public BaseHologram(Location location, NMSManager nmsManager, PlaceholderManager placeholderManager) {
|
||||
this.placeholderManager = placeholderManager;
|
||||
Preconditions.notNull(location, "location");
|
||||
this.setLocation(location);
|
||||
@ -41,7 +41,7 @@ public abstract class BaseHologram<T extends StandardHologramLine> extends BaseH
|
||||
return nmsManager;
|
||||
}
|
||||
|
||||
protected final PlaceholdersManager getPlaceholderManager() {
|
||||
protected final PlaceholderManager getPlaceholderManager() {
|
||||
return placeholderManager;
|
||||
}
|
||||
|
||||
|
@ -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.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import org.bukkit.World;
|
||||
|
||||
public abstract class BaseHologramLine extends BaseHologramComponent implements StandardHologramLine {
|
||||
@ -34,7 +34,7 @@ public abstract class BaseHologramLine extends BaseHologramComponent implements
|
||||
return hologram.getNMSManager();
|
||||
}
|
||||
|
||||
protected final PlaceholdersManager getPlaceholderManager() {
|
||||
protected final PlaceholderManager getPlaceholderManager() {
|
||||
return hologram.getPlaceholderManager();
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,11 @@ import org.bukkit.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseTextLine extends BaseTouchableLine implements StandardTextLine {
|
||||
|
||||
private final ArrayList<RelativePlaceholder> relativePlaceholders;
|
||||
private final List<RelativePlaceholder> relativePlaceholders;
|
||||
private String text;
|
||||
private NMSArmorStand textEntity;
|
||||
|
||||
@ -26,8 +27,6 @@ public abstract class BaseTextLine extends BaseTouchableLine implements Standard
|
||||
setText(text);
|
||||
}
|
||||
|
||||
protected abstract boolean isAllowPlaceholders();
|
||||
|
||||
@Override
|
||||
public String getText() {
|
||||
return text;
|
||||
@ -37,17 +36,8 @@ public abstract class BaseTextLine extends BaseTouchableLine implements Standard
|
||||
this.text = text;
|
||||
|
||||
if (textEntity != null) {
|
||||
if (text != null && !text.isEmpty()) {
|
||||
textEntity.setCustomNameNMS(text);
|
||||
if (isAllowPlaceholders()) {
|
||||
getPlaceholderManager().trackIfNecessary(this);
|
||||
}
|
||||
} else {
|
||||
textEntity.setCustomNameNMS(""); // It will not appear
|
||||
if (isAllowPlaceholders()) {
|
||||
getPlaceholderManager().untrack(this);
|
||||
}
|
||||
}
|
||||
textEntity.setCustomNameNMS(text != null ? text : "");
|
||||
getPlaceholderManager().updateTracking(this);
|
||||
}
|
||||
|
||||
relativePlaceholders.clear();
|
||||
@ -70,9 +60,7 @@ public abstract class BaseTextLine extends BaseTouchableLine implements Standard
|
||||
textEntity.setCustomNameNMS(text);
|
||||
}
|
||||
|
||||
if (isAllowPlaceholders()) {
|
||||
getPlaceholderManager().trackIfNecessary(this);
|
||||
}
|
||||
getPlaceholderManager().updateTracking(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -96,10 +84,6 @@ public abstract class BaseTextLine extends BaseTouchableLine implements Standard
|
||||
|
||||
@Override
|
||||
public Collection<RelativePlaceholder> getRelativePlaceholders() {
|
||||
if (!isAllowPlaceholders()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return relativePlaceholders;
|
||||
}
|
||||
|
||||
|
@ -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.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@ -18,7 +18,7 @@ public class InternalHologram extends BaseHologram<InternalHologramLine> {
|
||||
|
||||
private final String name;
|
||||
|
||||
protected InternalHologram(Location source, String name, NMSManager nmsManager, PlaceholdersManager placeholderManager) {
|
||||
protected InternalHologram(Location source, String name, NMSManager nmsManager, PlaceholderManager placeholderManager) {
|
||||
super(source, nmsManager, placeholderManager);
|
||||
this.name = name;
|
||||
}
|
||||
|
@ -7,15 +7,15 @@ 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.PlaceholdersManager;
|
||||
import me.filoghost.holographicdisplays.placeholder.PlaceholderManager;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class InternalHologramManager extends BaseHologramManager<InternalHologram> {
|
||||
|
||||
private final NMSManager nmsManager;
|
||||
private final PlaceholdersManager placeholderManager;
|
||||
private final PlaceholderManager placeholderManager;
|
||||
|
||||
public InternalHologramManager(NMSManager nmsManager, PlaceholdersManager placeholderManager) {
|
||||
public InternalHologramManager(NMSManager nmsManager, PlaceholderManager placeholderManager) {
|
||||
this.nmsManager = nmsManager;
|
||||
this.placeholderManager = placeholderManager;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public class InternalTextLine extends BaseTextLine implements InternalHologramLi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAllowPlaceholders() {
|
||||
public boolean isAllowPlaceholders() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder;
|
||||
|
||||
import me.filoghost.fcommons.Preconditions;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class DynamicLineData {
|
||||
|
||||
private final NMSArmorStand entity;
|
||||
private final String originalName;
|
||||
|
||||
private Set<Placeholder> placeholders;
|
||||
private final Map<String, Placeholder> animations;
|
||||
private final Map<String, PlaceholderReplacer> replacers;
|
||||
|
||||
public DynamicLineData(NMSArmorStand entity, String originalName) {
|
||||
Preconditions.notNull(entity, "entity");
|
||||
|
||||
this.entity = entity;
|
||||
this.originalName = originalName;
|
||||
placeholders = new HashSet<>();
|
||||
animations = new HashMap<>();
|
||||
replacers = new HashMap<>();
|
||||
}
|
||||
|
||||
public NMSArmorStand getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public String getOriginalName() {
|
||||
return originalName;
|
||||
}
|
||||
|
||||
public void setPlaceholders(Set<Placeholder> placeholders) {
|
||||
this.placeholders = placeholders;
|
||||
}
|
||||
|
||||
public Set<Placeholder> getPlaceholders() {
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
public Map<String, PlaceholderReplacer> getReplacers() {
|
||||
return replacers;
|
||||
}
|
||||
|
||||
public Map<String, Placeholder> getAnimations() {
|
||||
return animations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return entity.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
DynamicLineData other = (DynamicLineData) obj;
|
||||
return this.entity == other.entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -1,80 +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.PlaceholderReplacer;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class Placeholder {
|
||||
|
||||
// The plugin that owns this placeholder.
|
||||
private final Plugin owner;
|
||||
|
||||
// The placeholder itself, something like {onlinePlayers}. Case sensitive!
|
||||
private final String textPlaceholder;
|
||||
|
||||
// How many tenths of second between each refresh.
|
||||
private final int tenthsToRefresh;
|
||||
|
||||
// This is the current replacement for this placeholder.
|
||||
private String currentReplacement;
|
||||
|
||||
private final PlaceholderReplacer replacer;
|
||||
|
||||
public Placeholder(Plugin owner, String textPlaceholder, double refreshRate, PlaceholderReplacer replacer) {
|
||||
this.owner = owner;
|
||||
this.textPlaceholder = textPlaceholder;
|
||||
this.tenthsToRefresh = refreshRate <= 0.1 ? 1 : (int) (refreshRate * 10.0);
|
||||
this.replacer = replacer;
|
||||
this.currentReplacement = "";
|
||||
}
|
||||
|
||||
public Plugin getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public int getTenthsToRefresh() {
|
||||
return tenthsToRefresh;
|
||||
}
|
||||
|
||||
public String getTextPlaceholder() {
|
||||
return textPlaceholder;
|
||||
}
|
||||
|
||||
public String getCurrentReplacement() {
|
||||
return currentReplacement;
|
||||
}
|
||||
|
||||
public void setCurrentReplacement(String replacement) {
|
||||
this.currentReplacement = replacement != null ? replacement : "null";
|
||||
}
|
||||
|
||||
public void update() {
|
||||
setCurrentReplacement(replacer.update());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj instanceof Placeholder) {
|
||||
return ((Placeholder) obj).textPlaceholder.equals(this.textPlaceholder);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return textPlaceholder.hashCode();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class PlaceholderException extends Exception {
|
||||
|
||||
private final PlaceholderExpansion placeholderExpansion;
|
||||
|
||||
public PlaceholderException(Throwable cause, PlaceholderExpansion placeholderExpansion) {
|
||||
super(cause);
|
||||
this.placeholderExpansion = placeholderExpansion;
|
||||
}
|
||||
|
||||
public PlaceholderExpansion getPlaceholderExpansion() {
|
||||
return placeholderExpansion;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.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);
|
||||
}
|
||||
|
||||
}
|
@ -1,377 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder;
|
||||
|
||||
import me.filoghost.fcommons.Strings;
|
||||
import me.filoghost.fcommons.logging.Log;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||
import me.filoghost.holographicdisplays.bridge.bungeecord.ServerInfo;
|
||||
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker;
|
||||
import me.filoghost.holographicdisplays.core.Utils;
|
||||
import me.filoghost.holographicdisplays.core.hologram.StandardTextLine;
|
||||
import me.filoghost.holographicdisplays.core.nms.entity.NMSArmorStand;
|
||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||
import me.filoghost.holographicdisplays.task.WorldPlayerCounterTask;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PlaceholdersManager {
|
||||
|
||||
private static final String PINGER_NOT_ENABLED_ERROR = "[Please enable pinger]";
|
||||
|
||||
private static final Pattern BUNGEE_ONLINE_PATTERN = makePlaceholderWithArgsPattern("online");
|
||||
private static final Pattern BUNGEE_MAX_PATTERN = makePlaceholderWithArgsPattern("max_players");
|
||||
private static final Pattern BUNGEE_MOTD_PATTERN = makePlaceholderWithArgsPattern("motd");
|
||||
private static final Pattern BUNGEE_MOTD_2_PATTERN = makePlaceholderWithArgsPattern("motd2");
|
||||
private static final Pattern BUNGEE_STATUS_PATTERN = makePlaceholderWithArgsPattern("status");
|
||||
private static final Pattern ANIMATION_PATTERN = makePlaceholderWithArgsPattern("animation");
|
||||
private static final Pattern WORLD_PATTERN = makePlaceholderWithArgsPattern("world");
|
||||
|
||||
private final PlaceholdersRegistry placeholderRegistry;
|
||||
private final AnimationsRegistry animationRegistry;
|
||||
private final BungeeServerTracker bungeeServerTracker;
|
||||
protected final Set<DynamicLineData> linesToUpdate;
|
||||
private long elapsedTenthsOfSecond;
|
||||
|
||||
public PlaceholdersManager(BungeeServerTracker bungeeServerTracker, AnimationsRegistry animationRegistry) {
|
||||
this.placeholderRegistry = new PlaceholdersRegistry(this);
|
||||
this.animationRegistry = animationRegistry;
|
||||
this.bungeeServerTracker = bungeeServerTracker;
|
||||
this.linesToUpdate = new HashSet<>();
|
||||
|
||||
placeholderRegistry.addDefaultPlaceholders();
|
||||
}
|
||||
|
||||
public void startRefreshTask(Plugin plugin) {
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> {
|
||||
|
||||
for (Placeholder placeholder : placeholderRegistry.getPlaceholders()) {
|
||||
if (elapsedTenthsOfSecond % placeholder.getTenthsToRefresh() == 0) {
|
||||
try {
|
||||
placeholder.update();
|
||||
} catch (Throwable t) {
|
||||
Log.warning("The placeholder " + placeholder.getTextPlaceholder() + " registered by the plugin " + placeholder.getOwner().getName() + " generated an exception while updating. Please contact the author of " + placeholder.getOwner().getName(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Placeholder placeholder : animationRegistry.getAnimationsByFilename().values()) {
|
||||
if (elapsedTenthsOfSecond % placeholder.getTenthsToRefresh() == 0) {
|
||||
placeholder.update();
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<DynamicLineData> iter = linesToUpdate.iterator();
|
||||
DynamicLineData currentLineData;
|
||||
|
||||
while (iter.hasNext()) {
|
||||
currentLineData = iter.next();
|
||||
|
||||
if (currentLineData.getEntity().isDeadNMS()) {
|
||||
iter.remove();
|
||||
} else {
|
||||
updatePlaceholders(currentLineData);
|
||||
}
|
||||
}
|
||||
|
||||
elapsedTenthsOfSecond++;
|
||||
|
||||
}, 2L, 2L);
|
||||
}
|
||||
|
||||
|
||||
public void untrackAll() {
|
||||
linesToUpdate.clear();
|
||||
}
|
||||
|
||||
public void untrack(StandardTextLine line) {
|
||||
Iterator<DynamicLineData> iter = linesToUpdate.iterator();
|
||||
while (iter.hasNext()) {
|
||||
DynamicLineData data = iter.next();
|
||||
if (data.getEntity() == line.getNMSArmorStand()) {
|
||||
iter.remove();
|
||||
data.getEntity().setCustomNameNMS(data.getOriginalName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void trackIfNecessary(StandardTextLine line) {
|
||||
String text = line.getText();
|
||||
if (text == null || text.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NMSArmorStand entity = line.getNMSArmorStand();
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean updateText = false;
|
||||
|
||||
// Lazy initialization.
|
||||
Set<Placeholder> normalPlaceholders = null;
|
||||
Map<String, PlaceholderReplacer> bungeeReplacers = null;
|
||||
Map<String, PlaceholderReplacer> worldsOnlinePlayersReplacers = null;
|
||||
Map<String, Placeholder> animationsPlaceholders = null;
|
||||
|
||||
Matcher matcher;
|
||||
|
||||
for (Placeholder placeholder : placeholderRegistry.getPlaceholders()) {
|
||||
if (text.contains(placeholder.getTextPlaceholder())) {
|
||||
if (normalPlaceholders == null) {
|
||||
normalPlaceholders = new HashSet<>();
|
||||
}
|
||||
normalPlaceholders.add(placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Players in a world count pattern.
|
||||
matcher = WORLD_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (worldsOnlinePlayersReplacers == null) {
|
||||
worldsOnlinePlayersReplacers = new HashMap<>();
|
||||
}
|
||||
|
||||
final String worldsNames = extractArgumentFromPlaceholder(matcher);
|
||||
|
||||
if (worldsNames.contains(",")) {
|
||||
String[] split = worldsNames.split(",");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
split[i] = split[i].trim();
|
||||
}
|
||||
|
||||
final String[] worldsToTrack = split;
|
||||
|
||||
// Add it to tracked worlds.
|
||||
worldsOnlinePlayersReplacers.put(matcher.group(), () -> {
|
||||
return WorldPlayerCounterTask.getCount(worldsToTrack);
|
||||
});
|
||||
} else {
|
||||
// Normal, single tracked world.
|
||||
worldsOnlinePlayersReplacers.put(matcher.group(), () -> {
|
||||
return WorldPlayerCounterTask.getCount(worldsNames);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// BungeeCord online pattern.
|
||||
matcher = BUNGEE_ONLINE_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (bungeeReplacers == null) {
|
||||
bungeeReplacers = new HashMap<>();
|
||||
}
|
||||
|
||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||
|
||||
if (serverName.contains(",")) {
|
||||
String[] serversToTrack = Strings.splitAndTrim(serverName, ",");
|
||||
|
||||
// Add it to tracked servers.
|
||||
bungeeReplacers.put(matcher.group(), () -> {
|
||||
int count = 0;
|
||||
for (String serverToTrack : serversToTrack) {
|
||||
count += bungeeServerTracker.getCurrentServerInfo(serverToTrack).getOnlinePlayers();
|
||||
}
|
||||
return String.valueOf(count);
|
||||
});
|
||||
} else {
|
||||
// Normal, single tracked server.
|
||||
bungeeReplacers.put(matcher.group(), () -> {
|
||||
return String.valueOf(bungeeServerTracker.getCurrentServerInfo(serverName).getOnlinePlayers());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// BungeeCord max players pattern.
|
||||
matcher = BUNGEE_MAX_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (bungeeReplacers == null) {
|
||||
bungeeReplacers = new HashMap<>();
|
||||
}
|
||||
|
||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||
|
||||
// Add it to tracked servers.
|
||||
bungeeReplacers.put(matcher.group(), () -> {
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return String.valueOf(bungeeServerTracker.getCurrentServerInfo(serverName).getMaxPlayers());
|
||||
});
|
||||
}
|
||||
|
||||
// BungeeCord motd pattern.
|
||||
matcher = BUNGEE_MOTD_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (bungeeReplacers == null) {
|
||||
bungeeReplacers = new HashMap<>();
|
||||
}
|
||||
|
||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||
|
||||
// Add it to tracked servers.
|
||||
bungeeReplacers.put(matcher.group(), () -> {
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine1();
|
||||
});
|
||||
}
|
||||
|
||||
// BungeeCord motd (line 2) pattern.
|
||||
matcher = BUNGEE_MOTD_2_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (bungeeReplacers == null) {
|
||||
bungeeReplacers = new HashMap<>();
|
||||
}
|
||||
|
||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||
|
||||
// Add it to tracked servers.
|
||||
bungeeReplacers.put(matcher.group(), () -> {
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine2();
|
||||
});
|
||||
}
|
||||
|
||||
// BungeeCord status pattern.
|
||||
matcher = BUNGEE_STATUS_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
if (bungeeReplacers == null) {
|
||||
bungeeReplacers = new HashMap<>();
|
||||
}
|
||||
|
||||
final String serverName = extractArgumentFromPlaceholder(matcher);
|
||||
|
||||
// Add it to tracked servers.
|
||||
bungeeReplacers.put(matcher.group(), () -> {
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
ServerInfo serverInfo = bungeeServerTracker.getCurrentServerInfo(serverName);
|
||||
if (serverInfo.isOnline()) {
|
||||
return Configuration.pingerStatusOnline;
|
||||
} else {
|
||||
return Configuration.pingerStatusOffline;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Animation pattern.
|
||||
matcher = ANIMATION_PATTERN.matcher(text);
|
||||
while (matcher.find()) {
|
||||
String fileName = extractArgumentFromPlaceholder(matcher);
|
||||
Placeholder animation = animationRegistry.getAnimation(fileName);
|
||||
|
||||
// If exists...
|
||||
if (animation != null) {
|
||||
if (animationsPlaceholders == null) {
|
||||
animationsPlaceholders = new HashMap<>();
|
||||
}
|
||||
|
||||
animationsPlaceholders.put(matcher.group(), animation);
|
||||
|
||||
} else {
|
||||
text = text.replace(matcher.group(), "[Animation not found: " + fileName + "]");
|
||||
updateText = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Utils.isThereNonNull(normalPlaceholders, bungeeReplacers, worldsOnlinePlayersReplacers, animationsPlaceholders)) {
|
||||
DynamicLineData lineData = new DynamicLineData(entity, text);
|
||||
|
||||
if (normalPlaceholders != null) {
|
||||
lineData.setPlaceholders(normalPlaceholders);
|
||||
}
|
||||
|
||||
if (bungeeReplacers != null) {
|
||||
lineData.getReplacers().putAll(bungeeReplacers);
|
||||
}
|
||||
|
||||
if (worldsOnlinePlayersReplacers != null) {
|
||||
lineData.getReplacers().putAll(worldsOnlinePlayersReplacers);
|
||||
}
|
||||
|
||||
if (animationsPlaceholders != null) {
|
||||
lineData.getAnimations().putAll(animationsPlaceholders);
|
||||
}
|
||||
|
||||
// It could be already tracked!
|
||||
if (!linesToUpdate.add(lineData)) {
|
||||
linesToUpdate.remove(lineData);
|
||||
linesToUpdate.add(lineData);
|
||||
}
|
||||
|
||||
updatePlaceholders(lineData);
|
||||
|
||||
} else {
|
||||
|
||||
// The name needs to be updated anyways.
|
||||
if (updateText) {
|
||||
entity.setCustomNameNMS(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void updatePlaceholders(DynamicLineData lineData) {
|
||||
String oldCustomName = lineData.getEntity().getCustomNameStringNMS();
|
||||
String newCustomName = lineData.getOriginalName();
|
||||
|
||||
if (!lineData.getPlaceholders().isEmpty()) {
|
||||
for (Placeholder placeholder : lineData.getPlaceholders()) {
|
||||
newCustomName = newCustomName.replace(placeholder.getTextPlaceholder(), Utils.sanitize(placeholder.getCurrentReplacement()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineData.getReplacers().isEmpty()) {
|
||||
for (Entry<String, PlaceholderReplacer> entry : lineData.getReplacers().entrySet()) {
|
||||
newCustomName = newCustomName.replace(entry.getKey(), Utils.sanitize(entry.getValue().update()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!lineData.getAnimations().isEmpty()) {
|
||||
for (Entry<String, Placeholder> entry : lineData.getAnimations().entrySet()) {
|
||||
newCustomName = newCustomName.replace(entry.getKey(), Utils.sanitize(entry.getValue().getCurrentReplacement()));
|
||||
}
|
||||
}
|
||||
|
||||
// Update only if needed, don't send useless packets.
|
||||
if (!oldCustomName.equals(newCustomName)) {
|
||||
lineData.getEntity().setCustomNameNMS(newCustomName);
|
||||
}
|
||||
}
|
||||
|
||||
public PlaceholdersRegistry getRegistry() {
|
||||
return placeholderRegistry;
|
||||
}
|
||||
|
||||
private static Pattern makePlaceholderWithArgsPattern(String prefix) {
|
||||
return Pattern.compile("(\\{" + Pattern.quote(prefix) + ":)(.+?)(\\})");
|
||||
}
|
||||
|
||||
private static String extractArgumentFromPlaceholder(Matcher matcher) {
|
||||
return matcher.group(2).trim();
|
||||
}
|
||||
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder;
|
||||
|
||||
import me.filoghost.holographicdisplays.HolographicDisplays;
|
||||
import me.filoghost.holographicdisplays.core.Utils;
|
||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
public class PlaceholdersRegistry {
|
||||
|
||||
private final PlaceholdersManager placeholderManager;
|
||||
private final Set<Placeholder> placeholders;
|
||||
|
||||
public PlaceholdersRegistry(PlaceholdersManager placeholderManager) {
|
||||
this.placeholderManager = placeholderManager;
|
||||
this.placeholders = new HashSet<>();
|
||||
}
|
||||
|
||||
|
||||
public boolean register(Placeholder placeholder) {
|
||||
if (placeholders.contains(placeholder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
placeholders.add(placeholder);
|
||||
return true;
|
||||
}
|
||||
|
||||
public Set<String> getTextPlaceholdersByPlugin(Plugin plugin) {
|
||||
Set<String> found = new HashSet<>();
|
||||
|
||||
for (Placeholder placeholder : placeholders) {
|
||||
if (placeholder.getOwner().equals(plugin)) {
|
||||
found.add(placeholder.getTextPlaceholder());
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public boolean unregister(Plugin plugin, String textPlaceholder) {
|
||||
Iterator<Placeholder> iter = placeholders.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
Placeholder placeholder = iter.next();
|
||||
|
||||
if (placeholder.getOwner().equals(plugin) && placeholder.getTextPlaceholder().equals(textPlaceholder)) {
|
||||
iter.remove();
|
||||
|
||||
for (DynamicLineData data : placeholderManager.linesToUpdate) {
|
||||
data.getPlaceholders().remove(placeholder);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Set<Placeholder> getPlaceholders() {
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
public void addDefaultPlaceholders() {
|
||||
register(new Placeholder(HolographicDisplays.getInstance(), "{online}", 1.0, () -> {
|
||||
return String.valueOf(Bukkit.getOnlinePlayers().size());
|
||||
}));
|
||||
|
||||
register(new Placeholder(HolographicDisplays.getInstance(), "{max_players}", 10.0, () -> {
|
||||
return String.valueOf(Bukkit.getMaxPlayers());
|
||||
}));
|
||||
|
||||
register(new Placeholder(HolographicDisplays.getInstance(), "{motd}", 60.0, () -> {
|
||||
return Bukkit.getMotd();
|
||||
}));
|
||||
|
||||
register(new Placeholder(HolographicDisplays.getInstance(), "{time}", 0.9, () -> {
|
||||
return Configuration.timeFormat.format(Instant.now());
|
||||
}));
|
||||
|
||||
register(new Placeholder(HolographicDisplays.getInstance(), "&u", 0.2, new CyclicPlaceholderReplacer(Utils.toStringList(
|
||||
ChatColor.RED,
|
||||
ChatColor.GOLD,
|
||||
ChatColor.YELLOW,
|
||||
ChatColor.GREEN,
|
||||
ChatColor.AQUA,
|
||||
ChatColor.LIGHT_PURPLE
|
||||
))));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public 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;
|
||||
|
||||
public 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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 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 Map<StandardTextLine, TrackedLine> trackedLines;
|
||||
private final Map<PlaceholderExpansion, Long> lastErrorLogByPlaceholderExpansion;
|
||||
|
||||
private long currentTick;
|
||||
|
||||
public PlaceholdersUpdateTask(PlaceholdersReplacementTracker placeholdersReplacementTracker) {
|
||||
this.placeholdersReplacementTracker = placeholdersReplacementTracker;
|
||||
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.entity.isDeadNMS()) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
trackedLine.updateNameWithPlaceholders();
|
||||
}
|
||||
|
||||
currentTick++;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private 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(textLine, entity, textWithPlaceholders);
|
||||
}
|
||||
|
||||
private 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());
|
||||
}
|
||||
|
||||
|
||||
private class TrackedLine {
|
||||
|
||||
final StandardTextLine textLine;
|
||||
final NMSArmorStand entity;
|
||||
final StringWithPlaceholders nameWithPlaceholders;
|
||||
|
||||
TrackedLine(StandardTextLine textLine, NMSArmorStand entity, StringWithPlaceholders nameWithPlaceholders) {
|
||||
this.textLine = textLine;
|
||||
this.entity = entity;
|
||||
this.nameWithPlaceholders = nameWithPlaceholders;
|
||||
}
|
||||
|
||||
void updateNameWithPlaceholders() {
|
||||
String newName = nameWithPlaceholders.replacePlaceholders(PlaceholdersUpdateTask.this::getCurrentReplacement);
|
||||
entity.setCustomNameNMS(newName);
|
||||
}
|
||||
|
||||
void restoreOriginalName() {
|
||||
if (!entity.isDeadNMS()) {
|
||||
entity.setCustomNameNMS(textLine.getText() != null ? textLine.getText() : "");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -3,24 +3,30 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder;
|
||||
package me.filoghost.holographicdisplays.placeholder.internal;
|
||||
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CyclicPlaceholderReplacer implements PlaceholderReplacer {
|
||||
public class AnimationPlaceholder implements Placeholder {
|
||||
|
||||
private final int refreshIntervalTicks;
|
||||
private final List<String> frames;
|
||||
private int currentIndex;
|
||||
|
||||
public CyclicPlaceholderReplacer(List<String> frames) {
|
||||
public AnimationPlaceholder(int refreshIntervalTicks, List<String> frames) {
|
||||
this.frames = frames;
|
||||
currentIndex = 0;
|
||||
this.refreshIntervalTicks = refreshIntervalTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String update() {
|
||||
public int getRefreshIntervalTicks() {
|
||||
return refreshIntervalTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacement(String argument) {
|
||||
String result = frames.get(currentIndex);
|
||||
|
||||
currentIndex++;
|
@ -3,14 +3,15 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder;
|
||||
package me.filoghost.holographicdisplays.placeholder.internal;
|
||||
|
||||
import me.filoghost.fcommons.config.exception.ConfigSaveException;
|
||||
import me.filoghost.fcommons.logging.ErrorCollector;
|
||||
import me.filoghost.holographicdisplays.HolographicDisplays;
|
||||
import me.filoghost.holographicdisplays.core.DebugLogger;
|
||||
import me.filoghost.holographicdisplays.disk.ConfigManager;
|
||||
import me.filoghost.holographicdisplays.disk.StringConverter;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -20,7 +21,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AnimationsRegistry {
|
||||
public class AnimationRegistry implements PlaceholderFactory {
|
||||
|
||||
private static final String SPEED_PREFIX = "speed:";
|
||||
|
||||
@ -80,7 +81,8 @@ public class AnimationsRegistry {
|
||||
lines.set(i, StringConverter.toReadableFormat(lines.get(i)));
|
||||
}
|
||||
|
||||
animationsByFilename.put(fileName, new Placeholder(HolographicDisplays.getInstance(), fileName, speed, new CyclicPlaceholderReplacer(lines)));
|
||||
int refreshIntervalTicks = Math.min((int) (speed * 20.0), 1);
|
||||
animationsByFilename.put(fileName, new AnimationPlaceholder(refreshIntervalTicks, lines));
|
||||
DebugLogger.info("Successfully loaded animation \"" + fileName + "\", speed = " + speed + ".");
|
||||
|
||||
} catch (Exception e) {
|
||||
@ -88,13 +90,14 @@ public class AnimationsRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Placeholder> getAnimationsByFilename() {
|
||||
return animationsByFilename;
|
||||
}
|
||||
|
||||
public Placeholder getAnimation(String name) {
|
||||
return animationsByFilename.get(name);
|
||||
@Override
|
||||
public Placeholder getPlaceholder(String fileNameArgument) {
|
||||
Placeholder placeholder = animationsByFilename.get(fileNameArgument);
|
||||
if (placeholder != null) {
|
||||
return placeholder;
|
||||
} else {
|
||||
return new StaticPlaceholder("[Animation not found: " + fileNameArgument + "]");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.internal;
|
||||
|
||||
import me.filoghost.holographicdisplays.HolographicDisplays;
|
||||
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker;
|
||||
import me.filoghost.holographicdisplays.bridge.bungeecord.ServerInfo;
|
||||
import me.filoghost.holographicdisplays.core.Utils;
|
||||
import me.filoghost.holographicdisplays.disk.Configuration;
|
||||
import me.filoghost.holographicdisplays.placeholder.registry.PlaceholderRegistry;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public class DefaultPlaceholders {
|
||||
|
||||
private static final String PINGER_NOT_ENABLED_ERROR = "[Please enable pinger]";
|
||||
public static final String NO_SERVER_SPECIFIED_ERROR = "[No server specified]";
|
||||
|
||||
public static void resetAndRegister(PlaceholderRegistry placeholderRegistry, AnimationRegistry animationRegistry, BungeeServerTracker bungeeServerTracker) {
|
||||
HolographicDisplays plugin = HolographicDisplays.getInstance();
|
||||
placeholderRegistry.unregisterAll(plugin);
|
||||
|
||||
// TODO restore "&u"
|
||||
placeholderRegistry.register(plugin, "rainbow", new AnimationPlaceholder(4, Utils.toStringList(
|
||||
ChatColor.RED,
|
||||
ChatColor.GOLD,
|
||||
ChatColor.YELLOW,
|
||||
ChatColor.GREEN,
|
||||
ChatColor.AQUA,
|
||||
ChatColor.LIGHT_PURPLE
|
||||
)));
|
||||
|
||||
placeholderRegistry.registerReplacer(plugin, "time", 10, (argument) -> {
|
||||
return Configuration.timeFormat.format(Instant.now());
|
||||
});
|
||||
|
||||
placeholderRegistry.registerFactory(plugin, "animation", animationRegistry);
|
||||
|
||||
placeholderRegistry.registerFactory(plugin, "world", new WorldPlayersPlaceholderFactory());
|
||||
|
||||
placeholderRegistry.registerFactory(plugin, "online", new OnlinePlayersPlaceholderFactory(bungeeServerTracker));
|
||||
|
||||
placeholderRegistry.registerReplacer(plugin, "max_players", 20, (serverName) -> {
|
||||
if (serverName == null) {
|
||||
// No argument specified, return max players of this server
|
||||
return String.valueOf(Bukkit.getMaxPlayers());
|
||||
}
|
||||
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return String.valueOf(bungeeServerTracker.getCurrentServerInfo(serverName).getMaxPlayers());
|
||||
});
|
||||
|
||||
placeholderRegistry.registerReplacer(plugin, "status", 20, (serverName) -> {
|
||||
if (serverName == null) {
|
||||
return NO_SERVER_SPECIFIED_ERROR;
|
||||
}
|
||||
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
ServerInfo serverInfo = bungeeServerTracker.getCurrentServerInfo(serverName);
|
||||
if (serverInfo.isOnline()) {
|
||||
return Configuration.pingerStatusOnline;
|
||||
} else {
|
||||
return Configuration.pingerStatusOffline;
|
||||
}
|
||||
});
|
||||
|
||||
placeholderRegistry.registerReplacer(plugin, "motd", 20, (serverName) -> {
|
||||
if (serverName == null) {
|
||||
return NO_SERVER_SPECIFIED_ERROR;
|
||||
}
|
||||
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine1();
|
||||
});
|
||||
|
||||
placeholderRegistry.registerReplacer(plugin, "motd2", 20, (serverName) -> {
|
||||
if (serverName == null) {
|
||||
return NO_SERVER_SPECIFIED_ERROR;
|
||||
}
|
||||
|
||||
if (!Configuration.pingerEnabled) {
|
||||
return PINGER_NOT_ENABLED_ERROR;
|
||||
}
|
||||
|
||||
return bungeeServerTracker.getCurrentServerInfo(serverName).getMotdLine2();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.internal;
|
||||
|
||||
import me.filoghost.fcommons.Strings;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
|
||||
import me.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OnlinePlayersPlaceholderFactory implements PlaceholderFactory {
|
||||
|
||||
private final BungeeServerTracker bungeeServerTracker;
|
||||
|
||||
public OnlinePlayersPlaceholderFactory(BungeeServerTracker bungeeServerTracker) {
|
||||
this.bungeeServerTracker = bungeeServerTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholder getPlaceholder(@Nullable String argument) {
|
||||
if (argument == null) {
|
||||
// No argument specified, return online players in this server
|
||||
return new LocalOnlinePlayersPlaceholder();
|
||||
}
|
||||
|
||||
String[] serverNames = Strings.splitAndTrim(argument, ",");
|
||||
return new BungeeOnlinePlayersPlaceholder(serverNames, bungeeServerTracker);
|
||||
}
|
||||
|
||||
|
||||
private static class LocalOnlinePlayersPlaceholder implements Placeholder {
|
||||
|
||||
@Override
|
||||
public int getRefreshIntervalTicks() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacement(@Nullable String argument) {
|
||||
return String.valueOf(Bukkit.getOnlinePlayers().size());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class BungeeOnlinePlayersPlaceholder implements Placeholder {
|
||||
|
||||
private final String[] serverNames;
|
||||
private final BungeeServerTracker bungeeServerTracker;
|
||||
|
||||
public BungeeOnlinePlayersPlaceholder(String[] serverNames, BungeeServerTracker bungeeServerTracker) {
|
||||
this.serverNames = serverNames;
|
||||
this.bungeeServerTracker = bungeeServerTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshIntervalTicks() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacement(@Nullable String argument) {
|
||||
int count = 0;
|
||||
for (String serverName : serverNames) {
|
||||
count += bungeeServerTracker.getCurrentServerInfo(serverName).getOnlinePlayers();
|
||||
}
|
||||
|
||||
return String.valueOf(count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.internal;
|
||||
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
|
||||
public class StaticPlaceholder implements Placeholder {
|
||||
|
||||
private final String text;
|
||||
|
||||
public StaticPlaceholder(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshIntervalTicks() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacement(String argument) {
|
||||
return text;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.internal;
|
||||
|
||||
import me.filoghost.fcommons.Strings;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class WorldPlayersPlaceholderFactory implements PlaceholderFactory {
|
||||
|
||||
@Override
|
||||
public Placeholder getPlaceholder(@Nullable String argument) {
|
||||
if (argument == null) {
|
||||
return new StaticPlaceholder("[No world specified]");
|
||||
}
|
||||
|
||||
String[] worldNames = Strings.splitAndTrim(argument, ",");
|
||||
return new WorldPlayersPlaceholder(worldNames);
|
||||
}
|
||||
|
||||
|
||||
private static class WorldPlayersPlaceholder implements Placeholder {
|
||||
|
||||
private final String[] worldNames;
|
||||
|
||||
public WorldPlayersPlaceholder(String[] worldNames) {
|
||||
this.worldNames = worldNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshIntervalTicks() {
|
||||
return 20;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacement(@Nullable String argument) {
|
||||
int count = 0;
|
||||
|
||||
for (String worldName : worldNames) {
|
||||
World world = Bukkit.getWorld(worldName);
|
||||
if (world == null) {
|
||||
return "[World \"" + worldName + "\" not found]";
|
||||
}
|
||||
|
||||
for (Player player : world.getPlayers()) {
|
||||
if (!player.hasMetadata("NPC")) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return String.valueOf(count);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.parsing;
|
||||
|
||||
import me.filoghost.fcommons.collection.CaseInsensitiveString;
|
||||
|
||||
public class PlaceholderIdentifier {
|
||||
|
||||
private final CaseInsensitiveString identifier;
|
||||
|
||||
public PlaceholderIdentifier(String identifier) {
|
||||
this.identifier = new CaseInsensitiveString(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlaceholderIdentifier other = (PlaceholderIdentifier) obj;
|
||||
return this.identifier.equals(other.identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return identifier.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return identifier.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.parsing;
|
||||
|
||||
import me.filoghost.fcommons.Strings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class PlaceholderOccurrence {
|
||||
|
||||
private final PluginName pluginName;
|
||||
private final PlaceholderIdentifier identifier;
|
||||
private final String argument;
|
||||
|
||||
private final int hashCode; // Cached for performance reasons
|
||||
|
||||
private PlaceholderOccurrence(PluginName pluginName, PlaceholderIdentifier identifier, String argument) {
|
||||
this.pluginName = pluginName;
|
||||
this.identifier = identifier;
|
||||
this.argument = argument;
|
||||
this.hashCode = Objects.hash(pluginName, identifier, argument);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PluginName getPluginName() {
|
||||
return pluginName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PlaceholderIdentifier getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getArgument() {
|
||||
return argument;
|
||||
}
|
||||
|
||||
/*
|
||||
* Valid placeholder formats:
|
||||
* {identifier}
|
||||
* {identifier: argument}
|
||||
* {pluginName/identifier}
|
||||
* {pluginName/identifier: argument}
|
||||
*
|
||||
* identifier is required, pluginName and argument are optional
|
||||
*/
|
||||
public static PlaceholderOccurrence parse(String placeholderContent) {
|
||||
PluginName pluginName = null;
|
||||
String argument = null;
|
||||
String identifierString;
|
||||
|
||||
if (placeholderContent.contains(":")) {
|
||||
String[] parts = Strings.splitAndTrim(placeholderContent, ":", 2);
|
||||
identifierString = parts[0];
|
||||
argument = parts[1];
|
||||
} else {
|
||||
identifierString = placeholderContent;
|
||||
}
|
||||
|
||||
if (identifierString.contains("/")) {
|
||||
String[] parts = Strings.splitAndTrim(identifierString, "/", 2);
|
||||
pluginName = new PluginName(parts[0]);
|
||||
identifierString = parts[1];
|
||||
}
|
||||
|
||||
PlaceholderIdentifier identifier = new PlaceholderIdentifier(identifierString);
|
||||
return new PlaceholderOccurrence(pluginName, identifier, argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || this.getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlaceholderOccurrence other = (PlaceholderOccurrence) obj;
|
||||
return this.hashCode == other.hashCode
|
||||
&& Objects.equals(this.pluginName, other.pluginName)
|
||||
&& Objects.equals(this.identifier, other.identifier)
|
||||
&& Objects.equals(this.argument, other.argument);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.parsing;
|
||||
|
||||
import me.filoghost.fcommons.collection.CaseInsensitiveString;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class PluginName {
|
||||
|
||||
private final CaseInsensitiveString pluginName;
|
||||
|
||||
public PluginName(Plugin plugin) {
|
||||
this(plugin.getName());
|
||||
}
|
||||
|
||||
public PluginName(String pluginName) {
|
||||
this.pluginName = new CaseInsensitiveString(pluginName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginName other = (PluginName) obj;
|
||||
return this.pluginName.equals(other.pluginName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return pluginName.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return pluginName.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.parsing;
|
||||
|
||||
import me.filoghost.fcommons.collection.CollectionUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class StringWithPlaceholders {
|
||||
|
||||
private static final char PLACEHOLDER_END_CHAR = '}';
|
||||
private static final char PLACEHOLDER_START_CHAR = '{';
|
||||
|
||||
private final String string;
|
||||
private final List<PlaceholderMatch> placeholderMatches;
|
||||
|
||||
public StringWithPlaceholders(String string) {
|
||||
this.string = string;
|
||||
this.placeholderMatches = findPlaceholders(string);
|
||||
}
|
||||
|
||||
protected List<PlaceholderOccurrence> getPlaceholders() {
|
||||
return CollectionUtils.transform(placeholderMatches, match -> match.content);
|
||||
}
|
||||
|
||||
public boolean containsPlaceholders() {
|
||||
return placeholderMatches != null && !placeholderMatches.isEmpty();
|
||||
}
|
||||
|
||||
public String replacePlaceholders(Function<PlaceholderOccurrence, String> replaceFunction) {
|
||||
if (!containsPlaceholders()) {
|
||||
return string;
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
int lastAppendIndex = 0;
|
||||
|
||||
for (PlaceholderMatch match : placeholderMatches) {
|
||||
// Append leading text (if any)
|
||||
if (lastAppendIndex != match.startIndex) {
|
||||
output.append(string, lastAppendIndex, match.startIndex);
|
||||
}
|
||||
|
||||
String replacement = replaceFunction.apply(match.content);
|
||||
if (replacement != null) {
|
||||
// Append placeholder replacement
|
||||
output.append(replacement);
|
||||
lastAppendIndex = match.endIndex;
|
||||
} else {
|
||||
// If no replacement is provided, do not replace the occurrence
|
||||
output.append(match.unparsedString);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Append trailing text (if any)
|
||||
if (lastAppendIndex < string.length()) {
|
||||
output.append(string, lastAppendIndex, string.length());
|
||||
}
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<PlaceholderMatch> findPlaceholders(String input) {
|
||||
int currentIndex = 0;
|
||||
int placeholderStartIndex = -1;
|
||||
List<PlaceholderMatch> matches = null;
|
||||
|
||||
while (currentIndex < input.length()) {
|
||||
char currentChar = input.charAt(currentIndex);
|
||||
|
||||
if (placeholderStartIndex >= 0) {
|
||||
if (currentChar == PLACEHOLDER_END_CHAR) {
|
||||
int endIndex = currentIndex + 1;
|
||||
|
||||
// The unparsed string includes the opening and closing tags (e.g.: "{online: lobby}")
|
||||
String unparsedString = input.substring(placeholderStartIndex, endIndex);
|
||||
|
||||
// The content string does NOT include the opening and closing tags (e.g.: "online: lobby")
|
||||
String contentString = unparsedString.substring(1, unparsedString.length() - 1);
|
||||
PlaceholderOccurrence content = PlaceholderOccurrence.parse(contentString);
|
||||
|
||||
if (matches == null) {
|
||||
matches = new ArrayList<>();
|
||||
}
|
||||
matches.add(new PlaceholderMatch(
|
||||
content,
|
||||
unparsedString,
|
||||
placeholderStartIndex,
|
||||
endIndex));
|
||||
|
||||
placeholderStartIndex = -1;
|
||||
|
||||
} else if (currentChar == PLACEHOLDER_START_CHAR) {
|
||||
// Nested placeholder, ignore outer placeholder and update start index
|
||||
placeholderStartIndex = currentIndex;
|
||||
}
|
||||
} else {
|
||||
if (currentChar == PLACEHOLDER_START_CHAR) {
|
||||
placeholderStartIndex = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
private static class PlaceholderMatch {
|
||||
|
||||
private final PlaceholderOccurrence content;
|
||||
private final String unparsedString;
|
||||
private final int startIndex;
|
||||
private final int endIndex;
|
||||
|
||||
public PlaceholderMatch(PlaceholderOccurrence parsedContent, String unparsedString, int startIndex, int endIndex) {
|
||||
this.content = parsedContent;
|
||||
this.unparsedString = unparsedString;
|
||||
this.startIndex = startIndex;
|
||||
this.endIndex = endIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.PlaceholderFactory;
|
||||
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderIdentifier;
|
||||
import me.filoghost.holographicdisplays.placeholder.parsing.PluginName;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public 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");
|
||||
|
||||
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() {
|
||||
return pluginName;
|
||||
}
|
||||
|
||||
public PlaceholderIdentifier getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public PlaceholderFactory getPlaceholderFactory() {
|
||||
return placeholderFactory;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
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.Placeholder;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderIdentifier;
|
||||
import me.filoghost.holographicdisplays.placeholder.parsing.PlaceholderOccurrence;
|
||||
import me.filoghost.holographicdisplays.placeholder.parsing.PluginName;
|
||||
import me.filoghost.holographicdisplays.placeholder.util.SimplePlaceholder;
|
||||
import me.filoghost.holographicdisplays.placeholder.util.SingletonPlaceholderFactory;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PlaceholderRegistry {
|
||||
|
||||
private final Table<PlaceholderIdentifier, PluginName, PlaceholderExpansion> placeholderExpansions;
|
||||
private Runnable changeListener;
|
||||
|
||||
public PlaceholderRegistry() {
|
||||
this.placeholderExpansions = HashBasedTable.create();
|
||||
}
|
||||
|
||||
public void setChangeListener(Runnable changeListener) {
|
||||
this.changeListener = changeListener;
|
||||
}
|
||||
|
||||
public void registerReplacer(Plugin plugin, String identifier, int refreshIntervalTicks, PlaceholderReplacer placeholderReplacer) {
|
||||
register(plugin, identifier, new SimplePlaceholder(refreshIntervalTicks, placeholderReplacer));
|
||||
}
|
||||
|
||||
public void register(Plugin plugin, String identifier, Placeholder placeholder) {
|
||||
registerFactory(plugin, identifier, new SingletonPlaceholderFactory(placeholder));
|
||||
}
|
||||
|
||||
public void registerFactory(Plugin plugin, String identifier, PlaceholderFactory placeholderFactory) {
|
||||
PlaceholderExpansion expansion = new PlaceholderExpansion(plugin, identifier, placeholderFactory);
|
||||
placeholderExpansions.put(expansion.getIdentifier(), expansion.getPluginName(), expansion);
|
||||
|
||||
changeListener.run();
|
||||
}
|
||||
|
||||
public void unregisterAll(Plugin plugin) {
|
||||
placeholderExpansions.column(new PluginName(plugin)).clear();
|
||||
|
||||
changeListener.run();
|
||||
}
|
||||
|
||||
public void unregister(Plugin plugin, String identifier) {
|
||||
placeholderExpansions.remove(new PlaceholderIdentifier(identifier), new PluginName(plugin));
|
||||
|
||||
changeListener.run();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PlaceholderExpansion findBestMatch(PlaceholderOccurrence textOccurrence) {
|
||||
PluginName pluginName = textOccurrence.getPluginName();
|
||||
PlaceholderIdentifier identifier = textOccurrence.getIdentifier();
|
||||
|
||||
if (pluginName != null) {
|
||||
// Find exact entry if plugin name is specified
|
||||
return placeholderExpansions.get(identifier, pluginName);
|
||||
|
||||
} else {
|
||||
// Otherwise find any match with the given identifier
|
||||
return Iterables.getFirst(placeholderExpansions.row(identifier).values(), null);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getRegisteredIdentifiers(Plugin plugin) {
|
||||
PluginName pluginName = new PluginName(plugin);
|
||||
List<String> identifiers = new ArrayList<>();
|
||||
|
||||
for (PlaceholderExpansion expansion : placeholderExpansions.column(pluginName).values()) {
|
||||
identifiers.add(expansion.getIdentifier().toString());
|
||||
}
|
||||
|
||||
return identifiers;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.util;
|
||||
|
||||
import me.filoghost.fcommons.Preconditions;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer;
|
||||
|
||||
public class SimplePlaceholder implements Placeholder {
|
||||
|
||||
private final int refreshIntervalTicks;
|
||||
private final PlaceholderReplacer placeholderReplacer;
|
||||
|
||||
public SimplePlaceholder(int refreshIntervalTicks, PlaceholderReplacer placeholderReplacer) {
|
||||
Preconditions.checkArgument(refreshIntervalTicks >= 0, "refreshIntervalTicks cannot be negative");
|
||||
Preconditions.notNull(placeholderReplacer, "placeholderReplacer");
|
||||
this.refreshIntervalTicks = refreshIntervalTicks;
|
||||
this.placeholderReplacer = placeholderReplacer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshIntervalTicks() {
|
||||
return refreshIntervalTicks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReplacement(String argument) {
|
||||
return placeholderReplacer.getReplacement(argument);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.util;
|
||||
|
||||
import me.filoghost.fcommons.Preconditions;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
|
||||
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
|
||||
|
||||
public class SingletonPlaceholderFactory implements PlaceholderFactory {
|
||||
|
||||
private final Placeholder placeholder;
|
||||
|
||||
public SingletonPlaceholderFactory(Placeholder placeholder) {
|
||||
Preconditions.notNull(placeholder, "placeholder");
|
||||
this.placeholder = placeholder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholder getPlaceholder(String argument) {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.task;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WorldPlayerCounterTask implements Runnable {
|
||||
|
||||
private static final Map<String, Integer> worlds = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
worlds.clear();
|
||||
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
List<Player> players = world.getPlayers();
|
||||
int count = 0;
|
||||
|
||||
for (Player player : players) {
|
||||
if (!player.hasMetadata("NPC")) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
worlds.put(world.getName(), count);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getCount(String[] worldsNames) {
|
||||
int total = 0;
|
||||
for (String worldName : worldsNames) {
|
||||
Integer count = worlds.get(worldName);
|
||||
if (count == null) {
|
||||
return "[World \"" + worldName + "\" not found]";
|
||||
}
|
||||
|
||||
total += count;
|
||||
}
|
||||
|
||||
return String.valueOf(total);
|
||||
}
|
||||
|
||||
public static String getCount(String worldName) {
|
||||
Integer count = worlds.get(worldName);
|
||||
return count != null ? count.toString() : "[World \"" + worldName + "\" not found]";
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) filoghost and contributors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
package me.filoghost.holographicdisplays.placeholder.parsing;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
class StringWithPlaceholdersTest {
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0} -> {1}")
|
||||
@MethodSource("replacementsTestArguments")
|
||||
void replacements(String input, String expectedOutput) {
|
||||
StringWithPlaceholders s = new StringWithPlaceholders(input);
|
||||
assertThat(s.replacePlaceholders(occurrence -> "#")).isEqualTo(expectedOutput);
|
||||
}
|
||||
|
||||
static Stream<Arguments> replacementsTestArguments() {
|
||||
return Stream.of(
|
||||
Arguments.of("{}", "#"), // Empty placeholder should still be detected
|
||||
Arguments.of("{p}{p}", "##"),
|
||||
Arguments.of("{p} {p} {p}", "# # #"),
|
||||
Arguments.of("{{p}}", "{#}"), // Only the innermost placeholder should be replaced
|
||||
Arguments.of("{p abc", "{p abc"), // Placeholder without closing tag
|
||||
Arguments.of("abc p}", "abc p}") // Placeholder without opening tag
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest(name = "[{index}] {0} -> {1}, {2}, {3}")
|
||||
@MethodSource("parsingTestArguments")
|
||||
void parsing(String input, String expectedPluginName, String expectedIdentifier, String expectedArgument) {
|
||||
StringWithPlaceholders s = new StringWithPlaceholders(input);
|
||||
|
||||
List<PlaceholderOccurrence> placeholders = s.getPlaceholders();
|
||||
assertThat(placeholders).hasSize(1);
|
||||
|
||||
PlaceholderOccurrence placeholder = placeholders.get(0);
|
||||
|
||||
if (expectedPluginName != null) {
|
||||
assertThat(placeholder.getPluginName()).hasToString(expectedPluginName);
|
||||
} else {
|
||||
assertThat(placeholder.getPluginName()).isNull();
|
||||
}
|
||||
assertThat(placeholder.getIdentifier()).hasToString(expectedIdentifier);
|
||||
assertThat(placeholder.getArgument()).isEqualTo(expectedArgument);
|
||||
}
|
||||
|
||||
static Stream<Arguments> parsingTestArguments() {
|
||||
return Stream.of(
|
||||
Arguments.of("{}", null, "", null),
|
||||
Arguments.of("{identifier}", null, "identifier", null),
|
||||
Arguments.of("{plugin/identifier}", "plugin", "identifier", null),
|
||||
Arguments.of("{plugin/identifier: argument}", "plugin", "identifier", "argument"),
|
||||
Arguments.of("{identifier: argument}", null, "identifier", "argument"),
|
||||
Arguments.of("{plugin/identifier/nestedIdentifier}", "plugin", "identifier/nestedIdentifier", null),
|
||||
Arguments.of("{identifier: argument: nestedArgument}", null, "identifier", "argument: nestedArgument")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user