Use new YML format for animations

This commit is contained in:
filoghost 2021-08-02 10:00:23 +02:00
parent de3366a8a2
commit 016de63f93
9 changed files with 137 additions and 131 deletions

View File

@ -31,7 +31,7 @@ import me.filoghost.holographicdisplays.plugin.listener.UpdateNotificationListen
import me.filoghost.holographicdisplays.plugin.log.PrintableErrorCollector;
import me.filoghost.holographicdisplays.plugin.placeholder.TickClock;
import me.filoghost.holographicdisplays.plugin.placeholder.TickingTask;
import me.filoghost.holographicdisplays.plugin.placeholder.internal.AnimationRegistry;
import me.filoghost.holographicdisplays.plugin.placeholder.internal.AnimationPlaceholderFactory;
import me.filoghost.holographicdisplays.plugin.placeholder.internal.DefaultPlaceholders;
import me.filoghost.holographicdisplays.plugin.placeholder.registry.PlaceholderRegistry;
import me.filoghost.holographicdisplays.plugin.placeholder.tracking.PlaceholderTracker;
@ -42,7 +42,6 @@ import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class HolographicDisplays extends FCommonsPlugin {
@ -53,7 +52,6 @@ public class HolographicDisplays extends FCommonsPlugin {
private ConfigManager configManager;
private InternalHologramManager internalHologramManager;
private BungeeServerTracker bungeeServerTracker;
private AnimationRegistry animationRegistry;
private PlaceholderRegistry placeholderRegistry;
private LineTrackerManager lineTrackerManager;
@ -97,7 +95,6 @@ public class HolographicDisplays extends FCommonsPlugin {
configManager = new ConfigManager(getDataFolder().toPath());
bungeeServerTracker = new BungeeServerTracker(this);
animationRegistry = new AnimationRegistry();
placeholderRegistry = new PlaceholderRegistry();
TickClock tickClock = new TickClock();
PlaceholderTracker placeholderTracker = new PlaceholderTracker(placeholderRegistry, tickClock);
@ -160,21 +157,17 @@ public class HolographicDisplays extends FCommonsPlugin {
}
public void load(boolean deferHologramsCreation, ErrorCollector errorCollector) {
DefaultPlaceholders.resetAndRegister(this, placeholderRegistry, animationRegistry, bungeeServerTracker);
internalHologramManager.clearAll();
configManager.reloadStaticReplacements(errorCollector);
configManager.reloadMainSettings(errorCollector);
HologramDatabase hologramDatabase = configManager.loadHologramDatabase(errorCollector);
try {
animationRegistry.loadAnimations(configManager, errorCollector);
} catch (IOException | ConfigException e) {
errorCollector.add(e, "failed to load animation files");
}
AnimationPlaceholderFactory animationPlaceholderFactory = configManager.loadAnimations(errorCollector);
DefaultPlaceholders.resetAndRegister(this, placeholderRegistry, animationPlaceholderFactory, bungeeServerTracker);
bungeeServerTracker.restart(Settings.bungeeRefreshSeconds, TimeUnit.SECONDS);
HologramDatabase hologramDatabase = configManager.loadHologramDatabase(errorCollector);
if (deferHologramsCreation) {
// For the initial load: holograms are loaded later, when the worlds are ready
Bukkit.getScheduler().runTask(this, () -> hologramDatabase.createHolograms(internalHologramManager, errorCollector));

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.config;
import me.filoghost.fcommons.config.mapped.MappedConfig;
import me.filoghost.fcommons.config.mapped.Path;
import java.util.List;
public class AnimationConfig implements MappedConfig {
@Path("interval-seconds")
private double intervalSeconds;
@Path("animation-frames")
private List<String> frames;
public int getIntervalTicks() {
return Math.max((int) (intervalSeconds * 20.0), 1);
}
public List<String> getFrames() {
return frames;
}
}

View File

@ -11,12 +11,21 @@ import me.filoghost.fcommons.config.ConfigErrors;
import me.filoghost.fcommons.config.ConfigLoader;
import me.filoghost.fcommons.config.FileConfig;
import me.filoghost.fcommons.config.exception.ConfigException;
import me.filoghost.fcommons.config.exception.ConfigLoadException;
import me.filoghost.fcommons.config.exception.ConfigSaveException;
import me.filoghost.fcommons.config.mapped.MappedConfigLoader;
import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.fcommons.logging.Log;
import me.filoghost.holographicdisplays.plugin.hologram.internal.InternalHologramManager;
import me.filoghost.holographicdisplays.plugin.placeholder.internal.AnimationPlaceholder;
import me.filoghost.holographicdisplays.plugin.placeholder.internal.AnimationPlaceholderFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class ConfigManager extends BaseConfigManager {
@ -67,6 +76,45 @@ public class ConfigManager extends BaseConfigManager {
}
}
public AnimationPlaceholderFactory loadAnimations(ErrorCollector errorCollector) {
Map<String, AnimationPlaceholder> animationsByFileName = new HashMap<>();
Path animationsFolder = getAnimationsFolder();
try {
if (!Files.isDirectory(animationsFolder)) {
Files.createDirectories(animationsFolder);
try {
Path exampleAnimationFile = animationsFolder.resolve("example.yml");
getConfigLoader(exampleAnimationFile).createDefault();
} catch (ConfigSaveException e) {
errorCollector.add(e, "could not add example animation file");
}
}
try (Stream<Path> animationFiles = Files.list(animationsFolder)) {
animationFiles.filter(this::isYamlFile).forEach(file -> {
try {
String fileName = file.getFileName().toString();
AnimationPlaceholder animationPlaceholder = loadAnimation(file);
animationsByFileName.put(fileName, animationPlaceholder);
} catch (ConfigLoadException e) {
logConfigInitException(errorCollector, file, e);
}
});
}
} catch (IOException e) {
errorCollector.add(e, "error loading animation files");
}
return new AnimationPlaceholderFactory(animationsByFileName);
}
private AnimationPlaceholder loadAnimation(Path animationFile) throws ConfigLoadException {
MappedConfigLoader<AnimationConfig> animationConfigLoader = getMappedConfigLoader(animationFile, AnimationConfig.class);
AnimationConfig animationConfig = animationConfigLoader.load();
return new AnimationPlaceholder(animationConfig.getIntervalTicks(), animationConfig.getFrames());
}
public void reloadStaticReplacements(ErrorCollector errorCollector) {
FileConfig staticReplacementsConfig;
@ -84,12 +132,16 @@ public class ConfigManager extends BaseConfigManager {
return getRootDataFolder().resolve("animations");
}
public ConfigLoader getExampleAnimationLoader() {
return getConfigLoader(getAnimationsFolder().resolve("example.txt"));
private boolean isYamlFile(Path file) {
if (!Files.isRegularFile(file)) {
return false;
}
String fileName = file.getFileName().toString().toLowerCase();
return fileName.endsWith(".yml") || fileName.endsWith(".yaml");
}
private void logConfigInitException(ErrorCollector errorCollector, Path file, ConfigException e) {
errorCollector.add(e, "error while initializing config file \"" + formatPath(file) + "\"");
errorCollector.add(e, "error while loading config file \"" + formatPath(file) + "\"");
}
private String formatPath(Path path) {

View File

@ -5,6 +5,8 @@
*/
package me.filoghost.holographicdisplays.plugin.placeholder.internal;
import com.google.common.collect.ImmutableList;
import me.filoghost.fcommons.Preconditions;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import java.util.List;
@ -12,12 +14,13 @@ import java.util.List;
public class AnimationPlaceholder implements Placeholder {
private final int refreshIntervalTicks;
private final List<String> frames;
private final ImmutableList<String> frames;
private int currentIndex;
public AnimationPlaceholder(int refreshIntervalTicks, List<String> frames) {
this.frames = frames;
Preconditions.notEmpty(frames, "frames");
this.refreshIntervalTicks = refreshIntervalTicks;
this.frames = ImmutableList.copyOf(frames);
}
@Override

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.placeholder.internal;
import me.filoghost.fcommons.collection.CaseInsensitiveHashMap;
import me.filoghost.fcommons.collection.CaseInsensitiveMap;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
import java.util.Map;
public class AnimationPlaceholderFactory implements PlaceholderFactory {
private final CaseInsensitiveMap<Placeholder> animationsByFileName;
public AnimationPlaceholderFactory(Map<String, AnimationPlaceholder> animationsByFileName) {
this.animationsByFileName = new CaseInsensitiveHashMap<>();
this.animationsByFileName.putAllString(animationsByFileName);
}
@Override
public Placeholder getPlaceholder(String fileNameArgument) {
Placeholder placeholder = animationsByFileName.get(fileNameArgument);
if (placeholder != null) {
return placeholder;
} else {
return new StaticPlaceholder("[Animation not found: " + fileNameArgument + "]");
}
}
}

View File

@ -1,106 +0,0 @@
/*
* Copyright (C) filoghost and contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package me.filoghost.holographicdisplays.plugin.placeholder.internal;
import me.filoghost.fcommons.config.exception.ConfigSaveException;
import me.filoghost.fcommons.logging.ErrorCollector;
import me.filoghost.holographicdisplays.api.placeholder.Placeholder;
import me.filoghost.holographicdisplays.api.placeholder.PlaceholderFactory;
import me.filoghost.holographicdisplays.common.DebugLogger;
import me.filoghost.holographicdisplays.plugin.config.ConfigManager;
import me.filoghost.holographicdisplays.plugin.format.DisplayFormat;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
public class AnimationRegistry implements PlaceholderFactory {
private static final String SPEED_PREFIX = "speed:";
private final Map<String, Placeholder> animationsByFilename = new HashMap<>();
public void loadAnimations(ConfigManager configManager, ErrorCollector errorCollector) throws IOException, ConfigSaveException {
animationsByFilename.clear();
Path animationFolder = configManager.getAnimationsFolder();
if (!Files.isDirectory(animationFolder)) {
Files.createDirectories(animationFolder);
configManager.getExampleAnimationLoader().createDefault();
return;
}
try (Stream<Path> animationFiles = Files.list(animationFolder)) {
animationFiles.forEach(file -> readAnimationFile(file, errorCollector));
}
}
private void readAnimationFile(Path file, ErrorCollector errorCollector) {
String fileName = file.getFileName().toString();
try {
List<String> lines = Files.readAllLines(file);
if (lines.size() == 0) {
return;
}
double speed = 0.5;
boolean validSpeedFound = false;
String firstLine = lines.get(0).trim();
if (firstLine.toLowerCase().startsWith(SPEED_PREFIX)) {
// Do not consider it
lines.remove(0);
firstLine = firstLine.substring(SPEED_PREFIX.length()).trim();
try {
speed = Double.parseDouble(firstLine);
validSpeedFound = true;
} catch (NumberFormatException ignored) {}
}
if (!validSpeedFound) {
errorCollector.add("could not find a valid \"" + SPEED_PREFIX + " <number>\""
+ " in the first line of the file \"" + fileName + "\","
+ " default speed of 0.5 seconds will be used");
}
if (lines.isEmpty()) {
lines.add("[No lines: " + fileName + "]");
errorCollector.add("could not find any line in \"" + fileName + "\" (excluding the speed),"
+ " you should add at least one more line");
}
// Add colors and formatting to lines
for (int i = 0; i < lines.size(); i++) {
lines.set(i, DisplayFormat.apply(lines.get(i)));
}
int refreshIntervalTicks = Math.max((int) (speed * 20.0), 1);
animationsByFilename.put(fileName, new AnimationPlaceholder(refreshIntervalTicks, lines));
DebugLogger.info("Successfully loaded animation \"" + fileName + "\", speed = " + speed + ".");
} catch (Exception e) {
errorCollector.add(e, "couldn't load the animation file \"" + fileName + "\"");
}
}
@Override
public Placeholder getPlaceholder(String fileNameArgument) {
Placeholder placeholder = animationsByFilename.get(fileNameArgument);
if (placeholder != null) {
return placeholder;
} else {
return new StaticPlaceholder("[Animation not found: " + fileNameArgument + "]");
}
}
}

View File

@ -26,7 +26,7 @@ public class DefaultPlaceholders {
public static void resetAndRegister(
Plugin plugin,
PlaceholderRegistry placeholderRegistry,
AnimationRegistry animationRegistry,
AnimationPlaceholderFactory animationPlaceholderFactory,
BungeeServerTracker bungeeServerTracker) {
placeholderRegistry.unregisterAll(plugin);
@ -47,7 +47,7 @@ public class DefaultPlaceholders {
return Settings.timeFormat.format(Instant.now());
});
placeholderRegistry.registerGlobalPlaceholderFactory(plugin, "animation", animationRegistry);
placeholderRegistry.registerGlobalPlaceholderFactory(plugin, "animation", animationPlaceholderFactory);
placeholderRegistry.registerGlobalPlaceholderFactory(plugin, "world", new WorldPlayersPlaceholderFactory());

View File

@ -1,6 +0,0 @@
Speed: 0.5
This is an example.
This line will change every 0.5 seconds...
because it's an animation.
You just have to put "{animation: example.txt}" in a hologram...
to see this animated text.

View File

@ -0,0 +1,7 @@
interval-seconds: 0.5
animation-frames:
- "This is an example."
- "This line will change every 0.5 seconds..."
- "because it's an animation."
- "You just have to put {animation: example.yml} in a hologram..."
- "to see this animated text."