/*
* This file is part of HoloAPI.
*
* HoloAPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HoloAPI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HoloAPI. If not, see <http://www.gnu.org/licenses/>.
*/
package com.dsh105.holoapi.api;
import com.dsh105.commodus.GeneralUtil;
import com.dsh105.commodus.config.YAMLConfig;
import com.dsh105.holoapi.HoloAPI;
import com.dsh105.holoapi.api.events.*;
import com.dsh105.holoapi.api.touch.TouchAction;
import com.dsh105.holoapi.api.visibility.Visibility;
import com.dsh105.holoapi.config.ConfigType;
import com.dsh105.holoapi.config.Settings;
import com.dsh105.holoapi.image.AnimatedImageGenerator;
import com.dsh105.holoapi.image.AnimatedTextGenerator;
import com.dsh105.holoapi.image.Frame;
import com.dsh105.holoapi.image.ImageGenerator;
import com.dsh105.holoapi.util.TagIdGenerator;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.*;
public class SimpleHoloManager implements HoloManager {
private YAMLConfig config;
private HashMap<Hologram, Plugin> holograms = new HashMap<>();
public SimpleHoloManager() {
this.config = HoloAPI.getConfig(ConfigType.DATA);
new BukkitRunnable() {
@Override
public void run() {
if (!getAllHolograms().isEmpty()) {
for (Hologram hologram : getAllHolograms().keySet()) {
hologram.updateDisplay();
}
}
}
}.runTaskTimer(HoloAPI.getCore(), 0L, 20 * 60);
}
@Override
public Map<Hologram, Plugin> getAllHolograms() {
return Collections.unmodifiableMap(this.holograms);
}
@Override
public Map<Hologram, Plugin> getAllComplexHolograms() {
HashMap<Hologram, Plugin> map = new HashMap<>();
for (Map.Entry<Hologram, Plugin> entry : this.holograms.entrySet()) {
if (!entry.getKey().isSimple()) {
map.put(entry.getKey(), entry.getValue());
}
}
return Collections.unmodifiableMap(map);
}
@Override
public Map<Hologram, Plugin> getAllSimpleHolograms() {
HashMap<Hologram, Plugin> map = new HashMap<>();
for (Map.Entry<Hologram, Plugin> entry : this.holograms.entrySet()) {
if (entry.getKey().isSimple()) {
map.put(entry.getKey(), entry.getValue());
}
}
return Collections.unmodifiableMap(map);
}
public void clearAll() {
Iterator<Hologram> i = holograms.keySet().iterator();
while (i.hasNext()) {
Hologram h = i.next();
if (!h.isSimple()) {
this.saveToFile(h);
}
h.clearAllPlayerViews();
i.remove();
}
}
@Override
public List<Hologram> getHologramsFor(Plugin owningPlugin) {
ArrayList<Hologram> list = new ArrayList<>();
for (Map.Entry<Hologram, Plugin> entry : this.holograms.entrySet()) {
if (entry.getValue().equals(owningPlugin)) {
list.add(entry.getKey());
}
}
return Collections.unmodifiableList(list);
}
@Override
public Hologram getHologram(String hologramId) {
for (Hologram hologram : this.holograms.keySet()) {
if (hologram.getSaveId().equals(hologramId)) {
return hologram;
}
}
return null;
}
@Override
public void track(Hologram hologram, Plugin owningPlugin) {
this.holograms.put(hologram, owningPlugin);
if (!hologram.isSimple() && this.config.getConfigurationSection("holograms." + hologram.getSaveId()) == null) {
this.saveToFile(hologram);
}
if (hologram instanceof AnimatedHologram && !((AnimatedHologram) hologram).isAnimating()) {
((AnimatedHologram) hologram).animate();
}
HoloAPI.getCore().getServer().getPluginManager().callEvent(new HoloCreateEvent(hologram));
HoloAPI.getHoloUpdater().track(hologram);
}
@Override
public void remove(Hologram hologram) {
stopTracking(hologram);
}
@Override
public void remove(String hologramId) {
stopTracking(hologramId);
}
@Override
public void stopTracking(Hologram hologram) {
boolean removed = this.holograms.remove(hologram) != null;
if(!removed) return; // No need to go on if we weren't already tracking it...
hologram.clearAllPlayerViews();
if (hologram instanceof AnimatedHologram && ((AnimatedHologram) hologram).isAnimating()) {
((AnimatedHologram) hologram).cancelAnimation();
}
HoloAPI.getCore().getServer().getPluginManager().callEvent(new HoloDeleteEvent(hologram));
//this.clearFromFile(hologram);
HoloAPI.getHoloUpdater().remove(hologram);
}
@Override
public void stopTracking(String hologramId) {
Hologram hologram = this.getHologram(hologramId);
if (hologram != null) {
this.stopTracking(hologram);
}
}
@Override
public void saveToFile(String hologramId) {
Hologram hologram = this.getHologram(hologramId);
if (hologram != null) {
this.saveToFile(hologram);
}
}
@Override
public void saveToFile(Hologram hologram) {
if (!hologram.isSimple()) {
String path = "holograms." + hologram.getSaveId() + ".";
this.config.set(path + "worldName", hologram.getWorldName());
this.config.set(path + "x", hologram.getDefaultX());
this.config.set(path + "y", hologram.getDefaultY());
this.config.set(path + "z", hologram.getDefaultZ());
if (hologram instanceof AnimatedHologram) {
AnimatedHologram animatedHologram = (AnimatedHologram) hologram;
if (animatedHologram.isImageGenerated() && (HoloAPI.getAnimationLoader().exists(animatedHologram.getAnimationKey())) || HoloAPI.getAnimationLoader().existsAsUnloadedUrl(animatedHologram.getAnimationKey())) {
this.config.set(path + "animatedImage.image", true);
this.config.set(path + "animatedImage.key", animatedHologram.getAnimationKey());
} else {
this.config.set(path + "animatedImage.image", false);
int index = 0;
for (Frame f : animatedHologram.getFrames()) {
this.config.set(path + "animatedImage.frames." + index + ".delay", f.getDelay());
int tagIndex = 0;
for (String tag : f.getLines()) {
this.config.set(path + "animatedImage.frames." + index + "." + tagIndex, tag.replace(ChatColor.COLOR_CHAR, '&'));
tagIndex++;
}
index++;
}
}
} else {
int index = 0;
for (StoredTag tag : hologram.serialise()) {
this.config.set(path + "lines." + index + ".type", tag.isImage() ? "image" : "text");
this.config.set(path + "lines." + index + ".value", tag.getContent().replace(ChatColor.COLOR_CHAR, '&'));
index++;
}
}
for (TouchAction touch : hologram.getAllTouchActions()) {
if (touch.getSaveKey() != null) {
Map<String, Object> map = touch.getDataToSave();
if (map != null && !map.isEmpty()) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
// Let the developer implementing the API handle how data is saved and loaded to and from holograms
this.config.set(path + "touchactions." + touch.getSaveKey() + "." + entry.getKey(), entry.getValue());
}
}
}
}
Visibility visibility = hologram.getVisibility();
if (visibility != null && visibility.getSaveKey() != null) {
Map<String, Object> map = visibility.getDataToSave();
if (map != null && !map.isEmpty()) {
this.config.set(path + "visibility", null);
for (Map.Entry<String, Object> entry : map.entrySet()) {
// Let the developer implementing the API handle how data is saved and loaded to and from holograms
this.config.set(path + "visibility." + visibility.getSaveKey() + "." + entry.getKey(), entry.getValue());
}
}
}
this.config.saveConfig();
}
}
@Override
public void clearFromFile(String hologramId) {
this.config.set("holograms." + hologramId + "", null);
this.config.saveConfig();
}
@Override
public void clearFromFile(Hologram hologram) {
this.clearFromFile(hologram.getSaveId());
}
public ArrayList<String> loadFileData() {
ArrayList<String> unprepared = new ArrayList<>();
ConfigurationSection cs = config.getConfigurationSection("holograms");
if (cs != null) {
for (String key : cs.getKeys(false)) {
String path = "holograms." + key + ".";
String worldName = config.getString(path + "worldName");
double x = config.getDouble(path + "x");
double y = config.getDouble(path + "y");
double z = config.getDouble(path + "z");
if (config.get(path + "animatedImage.image") != null) {
if (config.getBoolean(path + "animatedImage.image")) {
unprepared.add(key);
} else {
ArrayList<Frame> frameList = new ArrayList<>();
ConfigurationSection frames = config.getConfigurationSection("holograms." + key + ".animatedImage.frames");
if (frames != null) {
for (String frameKey : frames.getKeys(false)) {
ConfigurationSection lines = config.getConfigurationSection("holograms." + key + ".animatedImage.frames." + frameKey);
if (lines != null) {
ArrayList<String> tagList = new ArrayList<>();
int delay = config.getInt("holograms." + key + ".animatedImage.frames." + frameKey + ".delay", 5);
for (String tagKey : lines.getKeys(false)) {
if (!tagKey.equalsIgnoreCase("delay")) {
tagList.add(config.getString("holograms." + key + ".animatedImage.frames." + frameKey + "." + tagKey));
}
}
if (!tagList.isEmpty()) {
frameList.add(new Frame(delay, tagList.toArray(new String[tagList.size()])));
}
}
}
}
if (!frameList.isEmpty()) {
this.loadExtraData(new AnimatedHologramFactory(HoloAPI.getCore())
.withSaveId(key)
.withText(new AnimatedTextGenerator(frameList.toArray(new Frame[frameList.size()])))
.withLocation(new Vector(x, y, z), worldName)
.build(),
key);
}
}
} else {
ConfigurationSection cs1 = config.getConfigurationSection("holograms." + key + ".lines");
boolean containsImage = false;
if (cs1 != null) {
//ArrayList<String> lines = new ArrayList<String>();
HologramFactory hf = new HologramFactory(HoloAPI.getCore());
for (String key1 : cs1.getKeys(false)) {
if (GeneralUtil.isInt(key1)) {
String type = config.getString(path + "lines." + key1 + ".type");
String value = config.getString(path + "lines." + key1 + ".value");
if (type.equalsIgnoreCase("image")) {
containsImage = true;
break;
} else {
hf.withText(value);
}
} else {
HoloAPI.LOG.warning("Failed to load line section of " + key1 + " for Hologram of ID " + key + ".");
}
}
if (containsImage) {
unprepared.add(key);
continue;
}
this.loadExtraData(hf.withSaveId(key).withLocation(new Vector(x, y, z), worldName).build(), key);
}
}
}
}
return unprepared;
}
public Hologram loadFromFile(String hologramId) {
String path = "holograms." + hologramId + ".";
String worldName = config.getString(path + "worldName");
double x = config.getDouble(path + "x");
double y = config.getDouble(path + "y");
double z = config.getDouble(path + "z");
Hologram finalHologram = null;
if (config.get(path + "animatedImage.image") != null) {
if (config.getBoolean(path + "animatedImage.image")) {
AnimatedImageGenerator generator = HoloAPI.getAnimationLoader().getGenerator(config.getString(path + "animatedImage.key"));
if (generator != null) {
finalHologram = new AnimatedHologramFactory(HoloAPI.getCore()).withSaveId(hologramId).withImage(generator).withLocation(new Vector(x, y, z), worldName).build();
}
}
} else {
ConfigurationSection cs1 = config.getConfigurationSection("holograms." + hologramId + ".lines");
HologramFactory hf = new HologramFactory(HoloAPI.getCore());
//ArrayList<String> lines = new ArrayList<String>();
for (String key1 : cs1.getKeys(false)) {
if (GeneralUtil.isInt(key1)) {
String type = config.getString(path + "lines." + key1 + ".type");
String value = config.getString(path + "lines." + key1 + ".value");
if (type.equalsIgnoreCase("image")) {
ImageGenerator generator = HoloAPI.getImageLoader().getGenerator(value);
if (generator != null) {
hf.withImage(generator);
}
} else {
hf.withText(value);
}
} else {
HoloAPI.LOG.warning("Failed to load line section of " + key1 + " for Hologram of ID " + hologramId + ".");
}
}
if (!hf.isEmpty()) {
finalHologram = hf.withSaveId(hologramId).withLocation(new Vector(x, y, z), worldName).build();
}
}
if (finalHologram != null) {
this.loadExtraData(finalHologram, hologramId);
}
return finalHologram;
}
private void loadExtraData(Hologram hologram, String hologramKey) {
String[] sections = new String[]{"touchactions", "visibility"};
for (String sectionKey : sections) {
ConfigurationSection section = this.config.getConfigurationSection("holograms." + hologramKey + "." + sectionKey);
if (section != null) {
for (String objKey : section.getKeys(true)) {
LinkedHashMap<String, Object> configMap = new LinkedHashMap<>();
ConfigurationSection objKeySection = this.config.getConfigurationSection("holograms." + hologramKey + "." + sectionKey + "." + objKey);
if (objKeySection != null) {
for (String fullKey : objKeySection.getKeys(true)) {
configMap.put(fullKey, objKeySection.get(fullKey));
}
}
this.callDataLoadEvent(sectionKey, hologram, objKey, configMap);
}
}
}
}
private void callDataLoadEvent(String sectionkey, Hologram hologram, String objKey, LinkedHashMap<String, Object> configMap) {
HoloDataLoadEvent event = new HoloDataLoadEvent(hologram, objKey, configMap);
if (sectionkey.equalsIgnoreCase("touchactions")) {
event = new HoloTouchActionLoadEvent(hologram, objKey, configMap);
} else if (sectionkey.equalsIgnoreCase("visibility")) {
event = new HoloVisibilityLoadEvent(hologram, objKey, configMap);
}
HoloAPI.getCore().getServer().getPluginManager().callEvent(event);
}
@Override
public Hologram copy(Hologram hologram, Location copyLocation) {
return hologram instanceof AnimatedHologram ? this.buildAnimatedCopy((AnimatedHologram) hologram, copyLocation).build() : this.buildCopy(hologram, copyLocation).build();
}
@Override
public Hologram setLineContent(Hologram original, String... newContent) {
if (original.getLines().length >= newContent.length) {
original.updateLines(newContent);
return original;
}
if (original instanceof AnimatedHologram) {
throw new IllegalArgumentException("Lines cannot be added to AnimatedHolograms.");
}
// Make preparations
// Don't need this one anymore, we can delete it now
HoloAPI.getManager().stopTracking(original);
HoloAPI.getManager().clearFromFile(original);
// Oh look, a new hologram!
HologramFactory factory = this.buildCopy(original, original.getDefaultLocation()).withSaveId(original.getSaveId()).clearContent().withText(newContent);
Hologram copy = factory.build();
HoloAPI.getManager().saveToFile(copy);
return copy;
}
@Override
public Hologram copyAndAddLineTo(Hologram original, String... linesToAdd) {
if (original instanceof AnimatedHologram) {
throw new IllegalArgumentException("Lines cannot be added to AnimatedHolograms.");
}
HoloAPI.getManager().stopTracking(original);
HoloAPI.getManager().clearFromFile(original);
HologramFactory factory = this.buildCopy(original, original.getDefaultLocation()).withSaveId(original.getSaveId());
for (String line : linesToAdd) {
factory.withText(line);
}
return factory.build();
}
private AnimatedHologramFactory buildAnimatedCopy(AnimatedHologram original, Location copyLocation) {
AnimatedHologramFactory animatedCopyFactory = new AnimatedHologramFactory(HoloAPI.getCore()).withLocation(copyLocation).withSimplicity(original.isSimple());
if (original.isImageGenerated() && (HoloAPI.getAnimationLoader().exists(original.getAnimationKey())) || HoloAPI.getAnimationLoader().existsAsUnloadedUrl(original.getAnimationKey())) {
animatedCopyFactory.withImage(HoloAPI.getAnimationLoader().getGenerator(original.getAnimationKey()));
} else {
ArrayList<Frame> frames = original.getFrames();
animatedCopyFactory.withText(new AnimatedTextGenerator(frames.toArray(new Frame[frames.size()])));
}
return animatedCopyFactory;
}
private HologramFactory buildCopy(Hologram original, Location copyLocation) {
HologramFactory copyFactory = new HologramFactory(HoloAPI.getCore()).withLocation(copyLocation).withSimplicity(original.isSimple());
for (StoredTag tag : original.serialise()) {
if (tag.isImage()) {
ImageGenerator generator = HoloAPI.getImageLoader().getGenerator(tag.getContent());
if (generator != null) {
copyFactory.withImage(generator);
}
} else {
copyFactory.withText(tag.getContent());
}
}
return copyFactory;
}
@Override
public Hologram createSimpleHologram(Location location, int secondsUntilRemoved, List<String> lines) {
return this.createSimpleHologram(location, secondsUntilRemoved, false, lines.toArray(new String[lines.size()]));
}
@Override
public Hologram createSimpleHologram(Location location, int secondsUntilRemoved, boolean rise, List<String> lines) {
return this.createSimpleHologram(location, secondsUntilRemoved, rise, lines.toArray(new String[lines.size()]));
}
@Override
public Hologram createSimpleHologram(Location location, int secondsUntilRemoved, String... lines) {
return this.createSimpleHologram(location, secondsUntilRemoved, false, lines);
}
@Override
public Hologram createSimpleHologram(Location location, int secondsUntilRemoved, boolean rise, String... lines) {
int simpleId = TagIdGenerator.next(lines.length);
final Hologram hologram = new HologramFactory(HoloAPI.getCore()).withFirstTagId(simpleId).withSaveId(simpleId + "").withText(lines).withLocation(location).withSimplicity(true).build();
for (Entity e : hologram.getDefaultLocation().getWorld().getEntities()) {
if (e instanceof Player) {
hologram.show((Player) e, true);
}
}
BukkitTask t = null;
if (rise) {
t = HoloAPI.getCore().getServer().getScheduler().runTaskTimer(HoloAPI.getCore(), new Runnable() {
@Override
public void run() {
Location l = hologram.getDefaultLocation();
l.add(0.0D, 0.02D, 0.0D);
hologram.move(l.toVector());
}
}, 1L, 1L);
}
new HologramRemoveTask(hologram, t).runTaskLater(HoloAPI.getCore(), secondsUntilRemoved * 20);
return hologram;
}
@Override
public Hologram createSimpleHologram(Location location, int secondsUntilRemoved, Vector velocity, List<String> lines) {
return this.createSimpleHologram(location, secondsUntilRemoved, velocity, lines.toArray(new String[lines.size()]));
}
@Override
public Hologram createSimpleHologram(Location location, int secondsUntilRemoved, final Vector velocity, String... lines) {
int simpleId = TagIdGenerator.next(lines.length);
final Hologram hologram = new HologramFactory(HoloAPI.getCore()).withFirstTagId(simpleId).withSaveId(simpleId + "").withText(lines).withLocation(location).withSimplicity(true).build();
for (Entity e : hologram.getDefaultLocation().getWorld().getEntities()) {
if (e instanceof Player) {
hologram.show((Player) e, true);
}
}
BukkitTask t = HoloAPI.getCore().getServer().getScheduler().runTaskTimer(HoloAPI.getCore(), new Runnable() {
@Override
public void run() {
Location l = hologram.getDefaultLocation();
l.add(velocity);
hologram.move(l.toVector());
}
}, 1L, 1L);
new HologramRemoveTask(hologram, t).runTaskLater(HoloAPI.getCore(), secondsUntilRemoved * 20);
return hologram;
}
class HologramRemoveTask extends BukkitRunnable {
BukkitTask task = null;
private Hologram hologram;
HologramRemoveTask(Hologram hologram, BukkitTask task) {
this.hologram = hologram;
this.task = task;
}
@Override
public void run() {
if (this.task != null) {
task.cancel();
}
stopTracking(hologram);
}
}
}