diff --git a/src/main/java/com/mcmiddleearth/guidebook/GuidebookPlugin.java b/src/main/java/com/mcmiddleearth/guidebook/GuidebookPlugin.java index 54b6cf8..7322ce8 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/GuidebookPlugin.java +++ b/src/main/java/com/mcmiddleearth/guidebook/GuidebookPlugin.java @@ -25,13 +25,22 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; +import java.util.List; + /** * @author Eriol_Eandur */ public class GuidebookPlugin extends JavaPlugin { + private static final long INITIAL_DELAY_TICKS = 0L; + private static final long INTERVAL_TICKS = 20L; + private static GuidebookPlugin pluginInstance; + public static GuidebookPlugin getPluginInstance() { + return pluginInstance; + } + @Override public void onEnable() { pluginInstance = this; @@ -47,28 +56,34 @@ public void onDisable() { PluginData.disable(); } - public static GuidebookPlugin getPluginInstance() { - return pluginInstance; - } - public void initializePlayerMoveRunnable() { - getServer().getScheduler().scheduleSyncRepeatingTask(this, new Runnable() { - @Override - public void run() { - for (Player player : Bukkit.getOnlinePlayers()) { - Location playerLocation = player.getLocation(); - for (String key : PluginData.getInfoAreas().keySet()) { - InfoArea area = PluginData.getInfoAreas().get(key); - if (area.isInside(playerLocation) && !area.isInformed(player) - && area.isEnable() && !PluginData.isExcluded(player)) { + getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> { + List enabledAreas = PluginData.getInfoAreas() + .values() + .stream() + .filter(InfoArea::isEnable) + .toList(); + + for (Player player : Bukkit.getOnlinePlayers()) { + Location playerLocation = player.getLocation(); + + for (InfoArea area : enabledAreas) { + boolean isInside = area.containsPlayer(player); + + if (!isInside) { + if (area.isInside(playerLocation)) { area.onRegionEnter(player); } - if (!area.isNear(playerLocation)) { - area.onRegionLeave(player); - } + continue; + } + + if (!area.isNear(playerLocation)) { + area.onRegionLeave(player); } } } - }, 0L, 20L); + }, INITIAL_DELAY_TICKS, INTERVAL_TICKS); } } + + diff --git a/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookCommandExecutor.java b/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookCommandExecutor.java index d680eee..c71f7ba 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookCommandExecutor.java +++ b/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookCommandExecutor.java @@ -91,16 +91,19 @@ public List onTabComplete(CommandSender sender, Command command, String // Filter the completions return completions.stream() - .filter(completion -> getMatchingSegments(completion, lastArg)) + // AreaName completions are structured -- + // So support partial matches + .filter(completion -> matchesAnySegment(completion, lastArg)) .collect(Collectors.toList()); } - private boolean getMatchingSegments(String input, String searchTerm) { + private boolean matchesAnySegment(String input, String searchTerm) { String[] parts = input.toLowerCase().split("-"); + int partsLength = parts.length; - for (int i = 0; i < parts.length; i++) { - // world-gondor-minas, gondor-minas, minas - String segment = String.join("-", Arrays.copyOfRange(parts, i, parts.length)); + // segment = world-gondor-minas, gondor-minas, minas + for (int i = 0; i < partsLength; i++) { + String segment = String.join("-", Arrays.copyOfRange(parts, i, partsLength)); if (segment.startsWith(searchTerm)) { return true; } diff --git a/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookSet.java b/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookSet.java index a16d708..d5291c5 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookSet.java +++ b/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookSet.java @@ -94,18 +94,18 @@ protected void execute(CommandSender cs, String... args) { // Determine if we're creating or moving a region if (area == null) { if (spherical) { - area = new SphericalInfoArea(location, radius); + area = new SphericalInfoArea(areaName, location, radius); } else { if (WERegion instanceof CuboidRegion) { - area = new CuboidInfoArea(location, (CuboidRegion) WERegion); + area = new CuboidInfoArea(areaName, location, (CuboidRegion) WERegion); } else { - area = new PrismoidInfoArea(location, (Polygonal2DRegion) WERegion); + area = new PrismoidInfoArea(areaName, location, (Polygonal2DRegion) WERegion); } } PluginData.addInfoArea(areaName, area); saveData(cs, area); - sendNewAreaMessage(cs); + sendNewAreaMessage(cs, areaName); } else { new ConfirmationFactory(GuidebookPlugin.getPluginInstance()).start( p, @@ -172,8 +172,8 @@ private void sendAreaMovedMessage(CommandSender cs) { PluginData.getMessageUtil().sendInfoMessage(cs, "Guidebook area was moved to your location and selection."); } - private void sendNewAreaMessage(CommandSender cs) { - PluginData.getMessageUtil().sendInfoMessage(cs, "New guidebook area created."); + private void sendNewAreaMessage(CommandSender cs, String areaName) { + PluginData.getMessageUtil().sendInfoMessage(cs, "Guidebook area '" + areaName + "' created."); } private void sendInvalidSelection(Player player) { diff --git a/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookShow.java b/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookShow.java index 9ea3705..f0f1c31 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookShow.java +++ b/src/main/java/com/mcmiddleearth/guidebook/command/GuidebookShow.java @@ -58,7 +58,7 @@ protected void execute(CommandSender cs, String... args) { public static void sendDescription(Player player, InfoArea area) throws MessageParseException { if (area.getDescription().isEmpty()) { PluginData.getMessageUtil().sendInfoMessage(player, "Welcome to " - + PluginData.getMessageUtil().STRESSED + area.getTitle() + + PluginData.getMessageUtil().STRESSED + area.getTitle() + " (" + area.getName() + ")" + PluginData.getMessageUtil().INFO + ". Unfortunately there is no further description for this area."); } else { diff --git a/src/main/java/com/mcmiddleearth/guidebook/data/CuboidInfoArea.java b/src/main/java/com/mcmiddleearth/guidebook/data/CuboidInfoArea.java index 990369c..55bf6b9 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/data/CuboidInfoArea.java +++ b/src/main/java/com/mcmiddleearth/guidebook/data/CuboidInfoArea.java @@ -29,12 +29,14 @@ */ public class CuboidInfoArea extends InfoArea { - public CuboidInfoArea(Location center, com.sk89q.worldedit.regions.CuboidRegion weRegion) { + public CuboidInfoArea(String areaName, Location center, com.sk89q.worldedit.regions.CuboidRegion weRegion) { + super(areaName); region = new CuboidRegion(center, weRegion); } - public CuboidInfoArea(ConfigurationSection config) { - super(config); + public CuboidInfoArea(String areaName, ConfigurationSection config) { + super(areaName, config); + if (config.contains("center")) { Location center = deserializeLocation(config.getConfigurationSection("center")); int sizeX = config.getInt("xSize"); @@ -82,5 +84,4 @@ private static Location deserializeLocation(ConfigurationSection data) { (Double) data.get("z")); } } - } diff --git a/src/main/java/com/mcmiddleearth/guidebook/data/InfoArea.java b/src/main/java/com/mcmiddleearth/guidebook/data/InfoArea.java index d17a6bf..1331211 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/data/InfoArea.java +++ b/src/main/java/com/mcmiddleearth/guidebook/data/InfoArea.java @@ -18,6 +18,7 @@ import com.mcmiddleearth.guidebook.GuidebookPlugin; import com.mcmiddleearth.guidebook.command.GuidebookShow; +import com.mcmiddleearth.guidebook.events.GuidebookSendEvent; import com.mcmiddleearth.guidebook.listener.PlayerListener; import com.mcmiddleearth.guidebook.util.DevUtil; import com.mcmiddleearth.guidebook.util.InputUtil; @@ -55,8 +56,14 @@ public abstract class InfoArea { protected Region region; + private final String areaName; + private final HashMap lastInformedTimes = new HashMap<>(); - private final Set informedPlayers = new HashSet<>(); + /** + * All players currently within the InfoArea + * Used to determine if a player has entered or left an InfoArea + */ + private final Set areaPlayers = new HashSet<>(); private boolean status; @@ -69,7 +76,9 @@ public abstract class InfoArea { private List description = new ArrayList<>(); - protected InfoArea() { + protected InfoArea(String areaName) { + this.areaName = areaName; + //scoreboard = Bukkit.getScoreboardManager().getNewScoreboard(); bossBar = Bukkit.getServer().createBossBar("unnamed Guidebook area", BarColor.YELLOW, BarStyle.SOLID); bossBar.setProgress(0); @@ -78,9 +87,9 @@ protected InfoArea() { status = true; } - public InfoArea(ConfigurationSection config) { - //this.center = deserializeLocation(config.getConfigurationSection("center")); - this(); + public InfoArea(String areaName, ConfigurationSection config) { + this(areaName); + if (config.contains("title")) { setTitle((String) config.get("title")); subtitle = (String) config.get("subtitle"); @@ -127,12 +136,11 @@ public void statusOff() { status = false; } - public boolean isInformed(Player player) { - return informedPlayers.contains(player.getUniqueId()); + public boolean containsPlayer(Player player) { + return areaPlayers.contains(player.getUniqueId()); } - - public void onRegionEnter(Player player) { + public final void onRegionEnter(Player player) { UUID playerId = player.getUniqueId(); Instant now = Instant.now(); @@ -141,31 +149,40 @@ public void onRegionEnter(Player player) { Instant lastNotified = lastInformedTimes.get(playerId); if (Duration.between(lastNotified, now).compareTo(COOLDOWN) < 0) { // Player has entered the region, but don't welcome them - informedPlayers.add(playerId); + areaPlayers.add(playerId); return; } } - lastInformedTimes.put(playerId, now); - informedPlayers.add(playerId); - welcomePlayer(player); + // Track that the player is in the region even if the Event is cancelled + // - otherwise onRegionEnter would keep being called! + areaPlayers.add(playerId); + + GuidebookSendEvent sendEvent = new GuidebookSendEvent(player, areaName); + sendEvent.callEvent(); + if (sendEvent.isCancelled()) return; + + boolean notificationsEnabled = !PluginData.isExcluded(player); + if (notificationsEnabled) { + welcomePlayer(player); + lastInformedTimes.put(playerId, now); + } } - public void onRegionLeave(Player player) { + public final void onRegionLeave(Player player) { UUID playerId = player.getUniqueId(); - informedPlayers.remove(playerId); + areaPlayers.remove(playerId); bossBar.removePlayer(player); } public void clearPlayer(Player player) { + onRegionLeave(player); UUID playerId = player.getUniqueId(); - bossBar.removePlayer(player); - informedPlayers.remove(playerId); lastInformedTimes.remove(playerId); } - public void clearInformedPlayers() { - for (UUID uuid : informedPlayers) { + public void clearPlayers() { + for (UUID uuid : areaPlayers) { Player player = Bukkit.getPlayer(uuid); if (player != null) { clearPlayer(player); @@ -291,4 +308,8 @@ private void debugString(String string) { Logger.getGlobal().info("i: " + string.charAt(i) + " " + Integer.parseInt(String.valueOf(string.charAt(i))) + " " + string.codePointAt(i)); } } + + public String getName() { + return this.areaName; + } } diff --git a/src/main/java/com/mcmiddleearth/guidebook/data/PluginData.java b/src/main/java/com/mcmiddleearth/guidebook/data/PluginData.java index 035444c..79e431a 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/data/PluginData.java +++ b/src/main/java/com/mcmiddleearth/guidebook/data/PluginData.java @@ -71,7 +71,7 @@ public static InfoArea addInfoArea(String name, InfoArea newArea) { public static boolean deleteInfoArea(String name) { InfoArea area = infoAreas.get(name); - area.clearInformedPlayers(); + area.clearPlayers(); boolean result = getDataFile(getWorldFolder(name), name).delete(); if (result) { infoAreas.remove(name); @@ -107,19 +107,18 @@ public static void saveExcluded() throws IOException { } public static void saveArea(InfoArea area) throws IOException { - for (String areaName : infoAreas.keySet()) { - if (infoAreas.get(areaName) == area) { - DevUtil.log("SaveData " + areaName); - FileConfiguration config = new YamlConfiguration(); - infoAreas.get(areaName).save(config); - File worldFolder = getWorldFolder(areaName); - if (!worldFolder.exists()) { - worldFolder.mkdir(); - } - File dataFile = getDataFile(worldFolder, areaName); - config.save(dataFile); - } + final String areaName = area.getName(); + DevUtil.log("SaveData " + areaName); + + FileConfiguration config = new YamlConfiguration(); + area.save(config); + + File worldFolder = getWorldFolder(areaName); + if (!worldFolder.exists()) { + worldFolder.mkdir(); } + File dataFile = getDataFile(worldFolder, areaName); + config.save(dataFile); } public static void loadData() { @@ -131,7 +130,7 @@ public static void loadData() { excludedPlayers.add(UUID.fromString(id)); } for (InfoArea area : infoAreas.values()) { - area.clearInformedPlayers(); + area.clearPlayers(); } infoAreas.clear(); File[] worldFolders = dataFolder.listFiles(FileUtil.getDirFilter()); @@ -144,14 +143,11 @@ public static void loadData() { try { config.load(dataFile); if (SphericalRegion.isValidConfig(config)) { - infoAreas.put(areaName, - new SphericalInfoArea(config)); + addInfoArea(areaName, new SphericalInfoArea(areaName, config)); } else if (PrismoidRegion.isValidConfig(config)) { - infoAreas.put(areaName, - new PrismoidInfoArea(config)); + addInfoArea(areaName, new PrismoidInfoArea(areaName, config)); } else if (CuboidRegion.isValidConfig(config) || config.contains("xSize")) { // xSize is to notice old data format - infoAreas.put(areaName, - new CuboidInfoArea(config)); + addInfoArea(areaName, new CuboidInfoArea(areaName, config)); } } catch (IOException | InvalidConfigurationException ex) { Logger.getLogger(PluginData.class.getName()).log(Level.SEVERE, null, ex); @@ -175,7 +171,7 @@ public static void disable() { Logger.getLogger(PluginData.class.getName()).log(Level.SEVERE, null, ex); } for (InfoArea area : infoAreas.values()) { - area.clearInformedPlayers(); + area.clearPlayers(); } } diff --git a/src/main/java/com/mcmiddleearth/guidebook/data/PrismoidInfoArea.java b/src/main/java/com/mcmiddleearth/guidebook/data/PrismoidInfoArea.java index 905b134..49f1f10 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/data/PrismoidInfoArea.java +++ b/src/main/java/com/mcmiddleearth/guidebook/data/PrismoidInfoArea.java @@ -26,12 +26,13 @@ */ public class PrismoidInfoArea extends InfoArea { - public PrismoidInfoArea(Location center, com.sk89q.worldedit.regions.Polygonal2DRegion weRegion) { + public PrismoidInfoArea(String areaName, Location center, com.sk89q.worldedit.regions.Polygonal2DRegion weRegion) { + super(areaName); region = new PrismoidRegion(center, weRegion); } - public PrismoidInfoArea(ConfigurationSection config) { - super(config); + public PrismoidInfoArea(String areaName, ConfigurationSection config) { + super(areaName, config); DevUtil.log("LoadCuboid " + config); region = PrismoidRegion.load(config); } diff --git a/src/main/java/com/mcmiddleearth/guidebook/data/SphericalInfoArea.java b/src/main/java/com/mcmiddleearth/guidebook/data/SphericalInfoArea.java index 50feb63..d602368 100644 --- a/src/main/java/com/mcmiddleearth/guidebook/data/SphericalInfoArea.java +++ b/src/main/java/com/mcmiddleearth/guidebook/data/SphericalInfoArea.java @@ -25,12 +25,13 @@ */ public class SphericalInfoArea extends InfoArea { - public SphericalInfoArea(Location center, int radius) { + public SphericalInfoArea(String areaName, Location center, int radius) { + super(areaName); region = new SphericalRegion(center, radius); } - public SphericalInfoArea(ConfigurationSection config) { - super(config); + public SphericalInfoArea(String areaName, ConfigurationSection config) { + super(areaName, config); region = SphericalRegion.load(config); } diff --git a/src/main/java/com/mcmiddleearth/guidebook/events/GuidebookSendEvent.java b/src/main/java/com/mcmiddleearth/guidebook/events/GuidebookSendEvent.java new file mode 100644 index 0000000..285bd5e --- /dev/null +++ b/src/main/java/com/mcmiddleearth/guidebook/events/GuidebookSendEvent.java @@ -0,0 +1,46 @@ +package com.mcmiddleearth.guidebook.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class GuidebookSendEvent extends Event implements Cancellable { + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private boolean cancelled; + private final Player player; + private final String guidebookName; + + public GuidebookSendEvent(Player player, String guidebookName) { + this.player = player; + this.guidebookName = guidebookName; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + public String getGuidebookName() { + return guidebookName; + } + + public Player getPlayer() { + return player; + } +}