Compare commits

...

16 Commits

Author SHA1 Message Date
a3a75bb8ee items: cheaper recipe, plus custom model data for future resource pack work
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2023-02-18 15:05:28 +01:00
61f4dc90dc Bump version 2022-09-18 15:03:27 +02:00
aafeb9fe34 geometry: verbose-- 2022-09-18 15:02:10 +02:00
6d8b291b2f ui: display banner instructions in UI 2022-09-18 15:01:58 +02:00
244bb32d70 plugin: handle failure to load more gracefully 2022-09-18 15:01:14 +02:00
d247953546 regionmanager: basic lands plugin integration 2022-09-18 14:11:43 +02:00
669fab22cd regionpostitemwatcher: rewrite region charge recipe to be cheaper 2022-09-18 12:31:41 +02:00
221539001e rip pl3xmap 2022-09-18 12:31:20 +02:00
61d0af1a05 pom: version bump 2022-09-18 12:26:11 +02:00
75087b23e1 region: jump cost to hub is always 1, but spokes are more expensive 2022-09-11 22:28:54 +02:00
8af0909110 playernotifier: fix 1.17 protocol support 2021-07-09 10:05:12 -07:00
9944a7bcdb dynmapeventrelay: fix method visibility 2021-07-08 07:56:53 -07:00
e21115cc65 geometry: bordermesh: fix bug with one-way routes, move route calculation into triangulate()
Fixes #19
2021-07-08 07:55:50 -07:00
51fdc80542 pl3xmap: use lantern icons for region posts 2021-07-08 07:49:45 -07:00
ff70dd255b plugin: Add some preliminary pl3xmap support 2021-07-08 00:34:12 -07:00
b5a28e40cf Release 0.3.0 2021-07-07 21:57:12 -07:00
12 changed files with 269 additions and 127 deletions

15
.woodpecker.yml Normal file
View File

@ -0,0 +1,15 @@
pipeline:
build:
image: maven:3-openjdk-18-slim
commands:
- mvn package
release:
image: plugins/gitea-release
settings:
api_key:
from_secret: GITEA_KEY
base_url: https://gitea.malloc.hackerbots.net/
files:
- ./target/*.jar
when:
event: tag

20
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>us.camin.regions</groupId> <groupId>us.camin.regions</groupId>
<artifactId>Regions</artifactId> <artifactId>Regions</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>0.2.99-rc6</version> <version>0.4.0</version>
<name>regions</name> <name>regions</name>
<url>http://maven.apache.org</url> <url>http://maven.apache.org</url>
<properties> <properties>
@ -20,7 +20,7 @@
<dependency> <dependency>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>4.6.0</version> <version>4.7.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -46,17 +46,17 @@
<version>2.0</version> <version>2.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.angeschossen</groupId>
<artifactId>LandsAPI</artifactId>
<version>6.15.0</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>commons-codec</groupId> <groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>
<version>1.4</version> <version>1.4</version>
</dependency> </dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
<extensions> <extensions>
@ -177,6 +177,10 @@
<id>papermc</id> <id>papermc</id>
<url>http://papermc.io/repo/repository/maven-public/</url> <url>http://papermc.io/repo/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository><id>dynmap-repo</id><url>http://repo.mikeprimm.com/</url></repository> <repository><id>dynmap-repo</id><url>http://repo.mikeprimm.com/</url></repository>
<repository><id>imagej</id><url>http://maven.imagej.net/content/repositories/public/</url></repository> <repository><id>imagej</id><url>http://maven.imagej.net/content/repositories/public/</url></repository>
</repositories> </repositories>

View File

@ -71,7 +71,7 @@ public class DynmapEventRelay implements Listener {
} }
} }
public void updatePolygons(World world) { private void updatePolygons(World world) {
List<GenericMarker> oldMarkers = m_borderMarkers.get(world); List<GenericMarker> oldMarkers = m_borderMarkers.get(world);
if (oldMarkers != null) { if (oldMarkers != null) {
for(GenericMarker marker : oldMarkers) { for(GenericMarker marker : oldMarkers) {

View File

@ -34,6 +34,7 @@ import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction; import com.comphenix.protocol.wrappers.EnumWrappers.TitleAction;
import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.utility.MinecraftProtocolVersion;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -49,9 +50,64 @@ public class PlayerNotifier implements Listener {
Logger log = Logger.getLogger("Regions.PlayerNotifier"); Logger log = Logger.getLogger("Regions.PlayerNotifier");
RegionManager m_manager; RegionManager m_manager;
Plugin m_plugin; Plugin m_plugin;
ProtocolManager m_protocolManager;
int m_protoVersion = 0;
public PlayerNotifier(Plugin plugin, RegionManager manager) { public PlayerNotifier(Plugin plugin, RegionManager manager) {
m_manager = manager; m_manager = manager;
m_plugin = plugin; m_plugin = plugin;
m_protocolManager = ProtocolLibrary.getProtocolManager();
if (m_protocolManager != null) {
m_protoVersion = MinecraftProtocolVersion.getCurrentVersion();
}
}
private void sendTitle(Player player, WrappedChatComponent title, WrappedChatComponent subtitle) {
// Title packet format changed in 1.17
if (m_protoVersion < 755) {
PacketContainer setTitle = m_protocolManager.createPacket(PacketType.Play.Server.TITLE);
setTitle.getChatComponents().write(0, title);
PacketContainer setSubtitle = m_protocolManager.createPacket(PacketType.Play.Server.TITLE);
setSubtitle.getChatComponents().write(0, subtitle);
setSubtitle.getTitleActions().write(0, TitleAction.SUBTITLE);
try {
m_protocolManager.sendServerPacket(player, setTitle);
m_protocolManager.sendServerPacket(player, setSubtitle);
} catch (Exception e) {
}
} else {
PacketContainer setTitle = m_protocolManager.createPacket(PacketType.Play.Server.SET_TITLE_TEXT);
setTitle.getChatComponents().write(0, title);
PacketContainer setSubtitle = m_protocolManager.createPacket(PacketType.Play.Server.SET_SUBTITLE_TEXT);
setSubtitle.getChatComponents().write(0, subtitle);
try {
m_protocolManager.sendServerPacket(player, setTitle);
m_protocolManager.sendServerPacket(player, setSubtitle);
} catch (Exception e) {
}
}
}
private void sendActionBar(Player player, WrappedChatComponent text) {
// Title packet format changed in 1.17
if (m_protoVersion < 755) {
PacketContainer setActionBar = m_protocolManager.createPacket(PacketType.Play.Server.TITLE);
setActionBar.getChatComponents().write(0, text);
setActionBar.getTitleActions().write(0, TitleAction.ACTIONBAR);
try {
m_protocolManager.sendServerPacket(player, setActionBar);
} catch (Exception e) {
}
} else {
PacketContainer setActionBar = m_protocolManager.createPacket(PacketType.Play.Server.SET_ACTION_BAR_TEXT);
setActionBar.getChatComponents().write(0, text);
try {
m_protocolManager.sendServerPacket(player, setActionBar);
} catch (Exception e) {
}
}
} }
@EventHandler @EventHandler
@ -71,30 +127,16 @@ public class PlayerNotifier implements Listener {
int pop = m_plugin.playerWatcher().playersInRegion(event.newRegion).size(); int pop = m_plugin.playerWatcher().playersInRegion(event.newRegion).size();
Location center = event.newRegion.location(); Location center = event.newRegion.location();
int altitude = center.getBlockY(); int altitude = center.getBlockY();
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); if (m_protocolManager != null) {
if (protocolManager != null) {
PacketContainer chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
String colorHex = "#" + String.format("%06X", event.newRegion.color().getColor().asRGB()); String colorHex = "#" + String.format("%06X", event.newRegion.color().getColor().asRGB());
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"" + event.newRegion.name() + "\", color: \""+colorHex+"\"}")); WrappedChatComponent titleComponent = WrappedChatComponent.fromJson("{text:\"" + event.newRegion.name() + "\", color: \""+colorHex+"\"}");
try { WrappedChatComponent subtitleComponent = WrappedChatComponent.fromJson("{text:\"Population: " + pop + " Altitude: "+ altitude + "\", color: \"#ffffff\"}");
protocolManager.sendServerPacket(event.player, chatMessage); WrappedChatComponent actionBarComponent = WrappedChatComponent.fromJson("{text:\"Now entering " + event.newRegion.name() + "\", color: \""+colorHex+"\"}");
} catch (Exception e) { sendTitle(event.player, titleComponent, subtitleComponent);
} sendActionBar(event.player, actionBarComponent);
chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"Population: " + pop + " Altitude: "+ altitude + "\", color: \"#ffffff\"}"));
chatMessage.getTitleActions().write(0, TitleAction.SUBTITLE);
try {
protocolManager.sendServerPacket(event.player, chatMessage);
} catch (Exception e) {
}
chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"Now entering " + event.newRegion.name() + "\", color: \""+colorHex+"\"}"));
chatMessage.getTitleActions().write(0, TitleAction.ACTIONBAR);
try {
protocolManager.sendServerPacket(event.player, chatMessage);
} catch (Exception e) {
}
} else { } else {
// Fallback to approximated colors
event.player.sendTitle(event.newRegion.coloredName(), "Population: " + pop + " Altitude: " + altitude); event.player.sendTitle(event.newRegion.coloredName(), "Population: " + pop + " Altitude: " + altitude);
} }
} }
@ -106,25 +148,16 @@ public class PlayerNotifier implements Listener {
if (event.region.markSeenByPlayer(event.player)) { if (event.region.markSeenByPlayer(event.player)) {
event.player.playSound(event.region.location(), Sound.UI_TOAST_CHALLENGE_COMPLETE, (float)1, (float)1); event.player.playSound(event.region.location(), Sound.UI_TOAST_CHALLENGE_COMPLETE, (float)1, (float)1);
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); if (m_protocolManager != null) {
if (protocolManager != null) {
PacketContainer chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
String colorHex = "#" + String.format("%06X", event.region.color().getColor().asRGB()); String colorHex = "#" + String.format("%06X", event.region.color().getColor().asRGB());
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"Region discovered\", color: \""+colorHex+"\"}")); WrappedChatComponent discoverTitle = WrappedChatComponent.fromJson("{text:\"Region discovered\", color: \""+colorHex+"\"}");
try { WrappedChatComponent discoverSubtitle = WrappedChatComponent.fromJson("{text:\"You discovered the region " + event.region.name() + "\", color: \""+colorHex+"\"}");
protocolManager.sendServerPacket(event.player, chatMessage); sendTitle(event.player, discoverTitle, discoverSubtitle);
} catch (Exception e) {
}
chatMessage = protocolManager.createPacket(PacketType.Play.Server.TITLE);
chatMessage.getChatComponents().write(0, WrappedChatComponent.fromJson("{text:\"You discovered the region " + event.region.name() + "\", color: \""+colorHex+"\"}"));
chatMessage.getTitleActions().write(0, TitleAction.SUBTITLE);
try {
protocolManager.sendServerPacket(event.player, chatMessage);
} catch (Exception e) {
}
} else { } else {
//FIXME: also show pop/alt subtitle // Fallback to approximated colors
// TODO: Apply region color to the rest of the title and subtitle
// text
event.player.sendTitle("Region Discovered", "You discovered the region " + event.region.coloredName()); event.player.sendTitle("Region Discovered", "You discovered the region " + event.region.coloredName());
} }

View File

@ -78,6 +78,7 @@ public class Plugin extends JavaPlugin {
if (markerAPI != null) { if (markerAPI != null) {
DynmapEventRelay regionHandler = new DynmapEventRelay (this, markerAPI); DynmapEventRelay regionHandler = new DynmapEventRelay (this, markerAPI);
getServer().getPluginManager().registerEvents(regionHandler, this); getServer().getPluginManager().registerEvents(regionHandler, this);
log.info("Dynmap support enabled.");
} else { } else {
log.info("Dynmap marker API not found. Disabling map support."); log.info("Dynmap marker API not found. Disabling map support.");
} }
@ -110,7 +111,11 @@ public class Plugin extends JavaPlugin {
this.getDataFolder().mkdir(); this.getDataFolder().mkdir();
File regionConfigFile = new File(this.getDataFolder(), "regions.yml"); File regionConfigFile = new File(this.getDataFolder(), "regions.yml");
Configuration regionConf = YamlConfiguration.loadConfiguration(regionConfigFile); Configuration regionConf = YamlConfiguration.loadConfiguration(regionConfigFile);
m_regions.loadRegions(regionConf); try {
m_regions.loadRegions(regionConf);
} catch (Exception e) {
log.log(Level.SEVERE, "Could not load regions config! You risk overwriting and losing data!", e);
}
} }
public void saveRegions() { public void saveRegions() {

View File

@ -151,8 +151,8 @@ public class Region {
public int getTravelCost(Region destination) { public int getTravelCost(Region destination) {
int baseCost = getBaseTravelCost(destination); int baseCost = getBaseTravelCost(destination);
if (destination.isHub()) { if (destination.isHub()) {
// Travel *to* a hub is 50% cheaper, before charges applied // Travel *to* a hub is always 1
baseCost /= 2; baseCost = 1;
} }
return Math.max(1, (int)(baseCost / (Math.min(4, m_charges + 1)))); return Math.max(1, (int)(baseCost / (Math.min(4, m_charges + 1))));
} }
@ -166,7 +166,7 @@ public class Region {
return 1; return 1;
} }
double distance = teleportLocation().distance(destination.teleportLocation()); double distance = teleportLocation().distance(destination.teleportLocation());
double blocksPerXP = 500; double blocksPerXP = 768;
return Math.max(1, (int)(distance / blocksPerXP)); return Math.max(1, (int)(distance / blocksPerXP));
} }

View File

@ -37,13 +37,72 @@ import us.camin.regions.events.RegionRemoveEvent;
import us.camin.regions.geometry.RegionSet; import us.camin.regions.geometry.RegionSet;
import java.util.logging.Level; import java.util.logging.Level;
import me.angeschossen.lands.api.land.Land;
import me.angeschossen.lands.api.land.Area;
import me.angeschossen.lands.api.flags.Flags;
import me.angeschossen.lands.api.integration.LandsIntegration;
public class RegionManager { public class RegionManager {
private final LandsIntegration m_lands;
public enum ValidationResult {
VALID,
UNKNOWN,
NO_PERMISSION,
TOO_CLOSE,
BAD_NAME,
}
public String validateRegionName(String name, Location location) {
Land thisLand = m_lands.getLand(location);
if (thisLand == null) {
return name;
} else {
return thisLand.getName();
}
}
public ValidationResult validateAnchorPoint(Player player, Location location, String proposedName) {
// Require basic permission
if (!player.hasPermission("regions.create")) {
return ValidationResult.NO_PERMISSION;
}
// Allow admins to create regions in unclaimed land
Area thisArea = m_lands.getAreaByLoc(location);
if (thisArea == null) {
if (proposedName == null) {
return ValidationResult.BAD_NAME;
} else if (player.hasPermission("regions.create.bypass")) {
return ValidationResult.VALID;
} else {
return ValidationResult.NO_PERMISSION;
}
}
// For claimed land, require claiming perms to place post
if (!thisArea.hasFlag(player, Flags.LAND_CLAIM, false)) {
return ValidationResult.NO_PERMISSION;
}
// Ensure this is the only region post for this region
for (Region region : regionsForWorld(location.getWorld())) {
Land thatLand = m_lands.getLand(region.interactLocation());
if (thatLand != null && thatLand.getId() == thisArea.getLand().getId()) {
return ValidationResult.TOO_CLOSE;
}
}
return ValidationResult.VALID;
}
Logger log = Logger.getLogger("Regions.RegionManager"); Logger log = Logger.getLogger("Regions.RegionManager");
private Map<String, RegionSet> m_regions; private Map<String, RegionSet> m_regions;
private Server m_server; private Server m_server;
public RegionManager(Plugin plugin, Server server) { public RegionManager(Plugin plugin, Server server) {
m_server = server; m_server = server;
m_lands = new LandsIntegration(plugin);
log.setLevel(Level.ALL); log.setLevel(Level.ALL);
clear(); clear();
} }

View File

@ -53,21 +53,19 @@ public class RegionPostItemWatcher implements Listener {
NamespacedKey chargeKey = new NamespacedKey(m_plugin, "region_post_charge"); NamespacedKey chargeKey = new NamespacedKey(m_plugin, "region_post_charge");
ShapedRecipe chargeRecipe = new ShapedRecipe(chargeKey, m_theChargeItem); ShapedRecipe chargeRecipe = new ShapedRecipe(chargeKey, m_theChargeItem);
chargeRecipe.shape("DDD", "DGD", "DDD"); chargeRecipe.shape("DDD", "DGD", "DDD");
chargeRecipe.setIngredient('D', Material.GLOWSTONE_DUST); chargeRecipe.setIngredient('D', Material.PURPUR_BLOCK);
chargeRecipe.setIngredient('G', Material.GHAST_TEAR); chargeRecipe.setIngredient('G', Material.QUARTZ);
NamespacedKey anchorKey = new NamespacedKey(m_plugin, "region_post_anchor"); NamespacedKey anchorKey = new NamespacedKey(m_plugin, "region_post_anchor");
ShapedRecipe anchorRecipe = new ShapedRecipe(anchorKey, m_theAnchor); ShapedRecipe anchorRecipe = new ShapedRecipe(anchorKey, m_theAnchor);
anchorRecipe.shape("DDD", "DGD", "DDD"); anchorRecipe.shape("DDD", "DGD", "DDD");
anchorRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem)); anchorRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem));
anchorRecipe.setIngredient('G', Material.LANTERN); anchorRecipe.setIngredient('G', Material.SOUL_LANTERN);
NamespacedKey compassKey = new NamespacedKey(m_plugin, "region_post_compass"); NamespacedKey compassKey = new NamespacedKey(m_plugin, "region_post_compass");
ShapedRecipe compassRecipe = new ShapedRecipe(compassKey, m_theCompass); ShapedRecipe compassRecipe = new ShapedRecipe(compassKey, m_theCompass);
// Uses four fewer charges, slightly cheaper. // Uses four fewer charges, slightly cheaper.
// TODO: Maybe we just want this to be glowstone instead of effectively 4 compassRecipe.shape(" D ", " G ", " ");
// ghast tears?
compassRecipe.shape(" D ", "DGD", " D ");
compassRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem)); compassRecipe.setIngredient('D', new RecipeChoice.ExactChoice(m_theChargeItem));
compassRecipe.setIngredient('G', Material.COMPASS); compassRecipe.setIngredient('G', Material.COMPASS);
@ -96,6 +94,7 @@ public class RegionPostItemWatcher implements Listener {
meta.setLore(lore); meta.setLore(lore);
meta.addEnchant(Enchantment.SOUL_SPEED, 1, true); meta.addEnchant(Enchantment.SOUL_SPEED, 1, true);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
meta.setCustomModelData(93199);
stack.setItemMeta(meta); stack.setItemMeta(meta);
return stack; return stack;
} }
@ -109,6 +108,7 @@ public class RegionPostItemWatcher implements Listener {
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS); meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
meta.addEnchant(Enchantment.SOUL_SPEED, 1, true); meta.addEnchant(Enchantment.SOUL_SPEED, 1, true);
meta.setDisplayName("Region Post Charge"); meta.setDisplayName("Region Post Charge");
meta.setCustomModelData(93197);
stack.setItemMeta(meta); stack.setItemMeta(meta);
return stack; return stack;
} }
@ -122,6 +122,7 @@ public class RegionPostItemWatcher implements Listener {
meta.addEnchant(Enchantment.SOUL_SPEED, 1, true); meta.addEnchant(Enchantment.SOUL_SPEED, 1, true);
meta.setLore(lore); meta.setLore(lore);
meta.setDisplayName("Region Post Anchor"); meta.setDisplayName("Region Post Anchor");
meta.setCustomModelData(93198);
stack.setItemMeta(meta); stack.setItemMeta(meta);
return stack; return stack;
} }
@ -185,28 +186,32 @@ public class RegionPostItemWatcher implements Listener {
ItemStack compassItem = createCompass(nearest); ItemStack compassItem = createCompass(nearest);
player.setItemInHand(compassItem); player.setItemInHand(compassItem);
player.sendMessage("Now tracking " + nearest.name()); player.sendMessage("Now tracking " + nearest.name());
} else if (!event.isCancelled() && isRegionCreateItem(handStack, player) && event.getAction() == Action.RIGHT_CLICK_BLOCK && !event.getClickedBlock().getType().isInteractable() && player.hasPermission("regions.create")) { } else if (!event.isCancelled() && isRegionCreateItem(handStack, player) && event.getAction() == Action.RIGHT_CLICK_BLOCK && !event.getClickedBlock().getType().isInteractable()) {
event.setUseItemInHand(Event.Result.DENY); event.setUseItemInHand(Event.Result.DENY);
event.setCancelled(true); event.setCancelled(true);
if (meta.getDisplayName().equals("") || meta.getDisplayName().equals("Region Post Anchor")) { String postName = meta.getDisplayName();
player.sendMessage("You must first give this item a name!"); if (postName.equals("") || postName.equals("Region Post Anchor")) {
postName = null;
}
postName = m_manager.validateRegionName(postName, event.getClickedBlock().getLocation());
RegionManager.ValidationResult result = m_manager.validateAnchorPoint(player, event.getClickedBlock().getLocation(), postName);
if (result == RegionManager.ValidationResult.VALID) {
Region r = new Region(postName, event.getClickedBlock().getRelative(event.getBlockFace()).getLocation());
m_plugin.getServer().getScheduler().runTask(m_plugin, () -> {
RegionPostBuilder builder = new RegionPostBuilder(r, m_plugin);
builder.build();
});
handStack.setAmount(handStack.getAmount()-1);
player.setItemInHand(handStack);
m_plugin.regionManager().addRegion(r);
m_plugin.saveRegions();
player.sendMessage("You established the region "+r.coloredName());
} else if (result == RegionManager.ValidationResult.TOO_CLOSE) {
player.sendMessage("You are too close to another region post!");
} else if (result == RegionManager.ValidationResult.BAD_NAME) {
player.sendMessage("You must first give this item a better name.");
} else { } else {
Region nearest = m_manager.nearestRegion(event.getClickedBlock().getLocation()); player.sendMessage("You aren't allowed to create a region here.");
if (nearest != null && event.getClickedBlock().getLocation().distance(nearest.interactLocation()) < 500) {
int distance = 500 - (int)event.getClickedBlock().getLocation().distance(nearest.interactLocation());
player.sendMessage("You are " + distance + " blocks too close to the region post for " + nearest.name() + ".");
} else {
Region r = new Region(meta.getDisplayName(), event.getClickedBlock().getRelative(event.getBlockFace()).getLocation());
m_plugin.getServer().getScheduler().runTask(m_plugin, () -> {
RegionPostBuilder builder = new RegionPostBuilder(r, m_plugin);
builder.build();
});
handStack.setAmount(handStack.getAmount()-1);
player.setItemInHand(handStack);
m_plugin.regionManager().addRegion(r);
m_plugin.saveRegions();
player.sendMessage("You established the region "+r.coloredName());
}
} }
} }
} }

View File

@ -24,35 +24,11 @@ public class BorderMesh {
Map<Region, Polygon> m_polygons; Map<Region, Polygon> m_polygons;
Map<Region, Set<Region>> m_neighbors; Map<Region, Set<Region>> m_neighbors;
// TODO: Probably need to cache the neighbors after doing all this
// intersection work! Should be generated during triangulate()
public Collection<Region> neighbors(Region region) { public Collection<Region> neighbors(Region region) {
Collection<Region> ret = new ArrayList<Region>();
if (m_neighbors.containsKey(region)) { if (m_neighbors.containsKey(region)) {
Collection<Region> allNeighbors = m_neighbors.get(region); return m_neighbors.get(region);
for(Region neighbor : allNeighbors) {
int crossings = 0;
Vector2D start = new Vector2D(region.location().getBlockX(), region.location().getBlockZ());
Vector2D neighborEnd = new Vector2D(neighbor.location().getBlockX(), neighbor.location().getBlockZ());
for(Region distantNeighbor : m_regions) {
if (distantNeighbor.equals(neighbor)) {
continue;
}
Polygon poly = m_polygons.get(distantNeighbor);
for(Polygon.Segment edge : poly.segments()) {
// Check if the line from region->neighbor intersects with
// any polygon
if (doIntersect(start, neighborEnd, edge.start, edge.end)) {
crossings++;
}
}
}
if (crossings == 1 || crossings == 0) {
ret.add(neighbor);
}
}
} }
return ret; return new ArrayList<Region>();
} }
public BorderMesh(Collection<Region> regions) { public BorderMesh(Collection<Region> regions) {
@ -155,7 +131,7 @@ public class BorderMesh {
DelaunayTriangulator mesh = new DelaunayTriangulator(regionPoints); DelaunayTriangulator mesh = new DelaunayTriangulator(regionPoints);
try { try {
mesh.triangulate(); mesh.triangulate();
log.info("Mesh triangulated!"); log.fine("Mesh triangulated!");
} catch (NullPointerException e) { } catch (NullPointerException e) {
log.warning("Got a null pointer when triangulating, skipping world."); log.warning("Got a null pointer when triangulating, skipping world.");
return false; return false;
@ -191,11 +167,13 @@ public class BorderMesh {
} }
} }
HashMap<Region, Set<Region>> allNeighborSet = new HashMap<Region, Set<Region>>();
// Now we go back through our region mesh to generate vornoi points // Now we go back through our region mesh to generate vornoi points
for(Region region : m_regions) { for(Region region : m_regions) {
ArrayList<Vector2D> points = new ArrayList<Vector2D>(); ArrayList<Vector2D> points = new ArrayList<Vector2D>();
HashSet<Region> neighbors = new HashSet<Region>(); HashSet<Region> allNeighbors = new HashSet<Region>();
log.info("Executing voronoi transform..."); log.fine("Executing voronoi transform...");
for(Triangle tri : triangleSoup) { for(Triangle tri : triangleSoup) {
if (tri.region == region) { if (tri.region == region) {
for (Region neighbor : m_regions) { for (Region neighbor : m_regions) {
@ -205,36 +183,58 @@ public class BorderMesh {
Location neighborLoc = neighbor.location(); Location neighborLoc = neighbor.location();
Vector2D neighborCenter = new Vector2D(neighborLoc.getBlockX(), neighborLoc.getBlockZ()); Vector2D neighborCenter = new Vector2D(neighborLoc.getBlockX(), neighborLoc.getBlockZ());
if (vecEquals(tri.a, neighborCenter) || vecEquals(tri.b, neighborCenter) || vecEquals(tri.c, neighborCenter)) { if (vecEquals(tri.a, neighborCenter) || vecEquals(tri.b, neighborCenter) || vecEquals(tri.c, neighborCenter)) {
neighbors.add(neighbor); allNeighbors.add(neighbor);
} }
} }
points.add(tri.circumcenter()); points.add(tri.circumcenter());
} }
} }
allNeighborSet.put(region, allNeighbors);
// Sort points into a renderable polygon based on direction to center // Sort points into a renderable polygon based on direction to center
Location loc = region.location(); Location loc = region.location();
Vector2D regionCenter = new Vector2D(loc.getBlockX(), loc.getBlockZ()); Vector2D regionCenter = new Vector2D(loc.getBlockX(), loc.getBlockZ());
points.sort((Vector2D a, Vector2D b) -> Double.compare(direction(a, regionCenter), direction(b, regionCenter))); points.sort((Vector2D a, Vector2D b) -> Double.compare(direction(a, regionCenter), direction(b, regionCenter)));
log.info("Border for " + region.name() + " is defined by " + points.size() + " points"); log.fine("Border for " + region.name() + " is defined by " + points.size() + " points");
Polygon polygon = new Polygon(points); Polygon polygon = new Polygon(points);
m_polygons.put(region, polygon); m_polygons.put(region, polygon);
m_neighbors.put(region, neighbors);
}
// Now that we have the borders, we generate valid routes that cross
// zero or one border lines at most.
for(Region region : m_regions) {
Set<Region> routedNeighbors = new HashSet<Region>();
for(Region neighbor : allNeighborSet.get(region)) {
int crossings = 0;
Vector2D start = new Vector2D(region.location().getBlockX(), region.location().getBlockZ());
Vector2D neighborEnd = new Vector2D(neighbor.location().getBlockX(), neighbor.location().getBlockZ());
for(Region distantNeighbor : m_regions) {
if (distantNeighbor.equals(neighbor) || distantNeighbor.equals(region)) {
continue;
}
Polygon poly = m_polygons.get(distantNeighbor);
for(Polygon.Segment edge : poly.segments()) {
// Check if the line from region->neighbor intersects with
// any polygon
if (doIntersect(start, neighborEnd, edge.start, edge.end)) {
log.fine("Intersect " + distantNeighbor.name());
crossings++;
}
}
}
log.fine("Route: " + region.name() + " -> " + neighbor.name() + "\t" + crossings);
if (crossings == 1 || crossings == 0) {
routedNeighbors.add(neighbor);
}
}
m_neighbors.put(region, routedNeighbors);
} }
return true; return true;
} }
/*public boolean validNeighbors(Region regionA, Region regionB) {
Polygon polyA = polygonForRegion(regionA);
Polygon polyB = polygonForRegion(regionA);
Location center = region.location();
if (poly.contains(center.getBlockX(), center.getBlockZ())) {
return false;
} else {
return true;
}
}*/
private boolean vecEquals(Vector2D a, Vector2D b) { private boolean vecEquals(Vector2D a, Vector2D b) {
return a.x == b.x && a.y == b.y; return a.x == b.x && a.y == b.y;
} }

View File

@ -92,13 +92,19 @@ public class PlayerInventoryTeleporter implements Listener {
if (event.getClickedInventory() == neighborInv) { if (event.getClickedInventory() == neighborInv) {
ItemMeta meta = event.getCurrentItem().getItemMeta(); ItemMeta meta = event.getCurrentItem().getItemMeta();
Material mat = event.getCurrentItem().getType(); Material mat = event.getCurrentItem().getType();
if (mat == Material.BEDROCK || mat == Material.COMPASS || mat == Material.LANTERN) { final String selectedName = meta.getDisplayName();
if (mat == Material.BEDROCK || mat == Material.COMPASS || mat == Material.LANTERN || mat == Material.SOUL_LANTERN) {
return; return;
} }
m_plugin.getServer().getScheduler().runTask(m_plugin, () -> event.getView().close());
Region nearest = m_manager.nearestRegion(player.getLocation()); Region nearest = m_manager.nearestRegion(player.getLocation());
final String selectedName = meta.getDisplayName(); if (selectedName == nearest.name()) {
return;
}
m_plugin.getServer().getScheduler().runTask(m_plugin, () -> event.getView().close());
final Optional<Region> destination = m_plugin.regionManager().regionsForWorld(player.getLocation().getWorld()) final Optional<Region> destination = m_plugin.regionManager().regionsForWorld(player.getLocation().getWorld())
.stream() .stream()
.filter((r) -> r.name().equals(selectedName)) .filter((r) -> r.name().equals(selectedName))
@ -213,7 +219,7 @@ public class PlayerInventoryTeleporter implements Listener {
} }
ItemStack chargesItem = new ItemStack(event.region.charges() == 0 ? Material.SOUL_LANTERN : Material.LANTERN); ItemStack chargesItem = new ItemStack(event.region.charges() == 0 ? Material.SOUL_LANTERN : Material.LANTERN);
ItemMeta meta = chargesItem.getItemMeta(); ItemMeta meta = chargesItem.getItemMeta();
meta.setDisplayName(ChatColor.BOLD + "Region Post Configuration"); meta.setDisplayName(ChatColor.BOLD + "Region Post Charges");
ArrayList<String> lore = new ArrayList<String>(); ArrayList<String> lore = new ArrayList<String>();
lore.add(ChatColor.WHITE + "Name: "+event.region.coloredName()); lore.add(ChatColor.WHITE + "Name: "+event.region.coloredName());
lore.add(ChatColor.WHITE + "Visits: " + ChatColor.YELLOW + event.region.visits()); lore.add(ChatColor.WHITE + "Visits: " + ChatColor.YELLOW + event.region.visits());
@ -223,7 +229,18 @@ public class PlayerInventoryTeleporter implements Listener {
meta.addEnchant(Enchantment.SOUL_SPEED, 1, true); meta.addEnchant(Enchantment.SOUL_SPEED, 1, true);
chargesItem.setItemMeta(meta); chargesItem.setItemMeta(meta);
// 22 Is the middle of the bottom row // 22 Is the middle of the bottom row
neighborInventory.setItem(22, chargesItem); neighborInventory.setItem(23, chargesItem);
ItemStack iconItem = event.region.icon();
meta = iconItem.getItemMeta();
meta.setDisplayName(event.region.name());
lore = new ArrayList<String>();
lore.add(ChatColor.BOLD + "This is the region's current banner icon.");
lore.add(ChatColor.ITALIC + "Right-click the region post with a banner to change it");
meta.setLore(lore);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
iconItem.setItemMeta(meta);
neighborInventory.setItem(21, iconItem);
event.player.openInventory(neighborInventory); event.player.openInventory(neighborInventory);
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -3,8 +3,8 @@ main: us.camin.regions.Plugin
author: Torrie Fischer <tdfischer@hackerbots.net> author: Torrie Fischer <tdfischer@hackerbots.net>
website: http://hackerbots.net/ website: http://hackerbots.net/
version: ${version} version: ${version}
api-version: 1.16 api-version: 1.19
softdepend: [dynmap, ProtocolLib] softdepend: [dynmap, ProtocolLib, Lands]
commands: commands:
regions: regions:
description: "List available regions." description: "List available regions."
@ -21,6 +21,7 @@ permissions:
description: Allows use of all regions permissions description: Allows use of all regions permissions
children: children:
regions.create: true regions.create: true
regions.create.bypass: true
regions.commands.*: true regions.commands.*: true
regions.regen.*: true regions.regen.*: true
regions.bypass.*: true regions.bypass.*: true
@ -37,6 +38,9 @@ permissions:
regions.create: regions.create:
default: true default: true
description: Create a region with a region item description: Create a region with a region item
regions.create.bypass:
default: op
description: Bypass anything preventing creation of a new region
regions.setbanner: regions.setbanner:
default: true default: true
description: Allows setting a region post banner description: Allows setting a region post banner