Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 32 additions & 17 deletions src/main/java/com/mcmiddleearth/guidebook/GuidebookPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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, () -> {
Copy link
Author

@Drayz64 Drayz64 Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor optimisations

  • Storing the filtered info areas before the Player loop
  • Only doing the isNear check if the player is known to be inside the area

Other than that the main change is moving PluginData.isExcluded(player) to be inside onRegionEnter

List<InfoArea> 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);
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,19 @@ public List<String> onTabComplete(CommandSender sender, Command command, String

// Filter the completions
return completions.stream()
.filter(completion -> getMatchingSegments(completion, lastArg))
// AreaName completions are structured <worldName>-<projectName>-<guidebookName>
// 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) {
Copy link
Author

@Drayz64 Drayz64 Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current name was misleading

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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -82,5 +84,4 @@ private static Location deserializeLocation(ConfigurationSection data) {
(Double) data.get("z"));
}
}

}
59 changes: 40 additions & 19 deletions src/main/java/com/mcmiddleearth/guidebook/data/InfoArea.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -55,8 +56,14 @@ public abstract class InfoArea {

protected Region region;

private final String areaName;

private final HashMap<UUID, Instant> lastInformedTimes = new HashMap<>();
private final Set<UUID> informedPlayers = new HashSet<>();
/**
* All players currently within the InfoArea
* Used to determine if a player has entered or left an InfoArea
*/
private final Set<UUID> areaPlayers = new HashSet<>();

private boolean status;

Expand All @@ -69,7 +76,9 @@ public abstract class InfoArea {

private List<String> description = new ArrayList<>();

protected InfoArea() {
protected InfoArea(String areaName) {
Copy link
Author

@Drayz64 Drayz64 Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the GuidebookSendEvent is called inside InfoArea the area needed to know its own name - I've then been able to use the areaName in a few unrelated places

this.areaName = areaName;

//scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
bossBar = Bukkit.getServer().createBossBar("unnamed Guidebook area", BarColor.YELLOW, BarStyle.SOLID);
bossBar.setProgress(0);
Expand All @@ -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");
Expand Down Expand Up @@ -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();

Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
}
38 changes: 17 additions & 21 deletions src/main/java/com/mcmiddleearth/guidebook/data/PluginData.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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() {
Expand All @@ -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());
Expand All @@ -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);
Expand All @@ -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();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading