Reformated + Update to support 1.15+

This commit is contained in:
PryPurity 2020-06-21 04:44:35 -05:00
parent 44f388f3ba
commit 35f4af67c2
51 changed files with 4520 additions and 5227 deletions

191
pom.xml
View File

@ -1,100 +1,101 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wimbli.WorldBorder</groupId>
<artifactId>WorldBorder</artifactId>
<version>1.9.10 (beta)</version>
<name>WorldBorder</name>
<url>https://github.com/Brettflan/WorldBorder</url>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/Brettflan/WorldBorder/issues</url>
</issueManagement>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.wimbli.WorldBorder</groupId>
<artifactId>WorldBorder</artifactId>
<version>1.9.10 (beta)</version>
<name>WorldBorder</name>
<url>https://github.com/Brettflan/WorldBorder</url>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/Brettflan/WorldBorder/issues</url>
</issueManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>dynmap-repo</id>
<url>https://repo.mikeprimm.com/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
</repositories>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>dynmap-repo</id>
<url>https://repo.mikeprimm.com/</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<!--Spigot-API-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.15.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Bukkit API-->
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.15.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Dynmap API-->
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>dynmap-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencies>
<!--Spigot-API-->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.14-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Bukkit API-->
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>1.14-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!--Dynmap API-->
<dependency>
<groupId>us.dynmap</groupId>
<artifactId>dynmap-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>clean install</defaultGoal>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
<relocations>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>com.wimbli.WorldBorder.paperlib</shadedPattern> <!-- Replace this -->
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<build>
<defaultGoal>clean install</defaultGoal>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml
</dependencyReducedPomLocation>
<relocations>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>com.wimbli.WorldBorder.paperlib</shadedPattern> <!-- Replace this -->
</relocation>
</relocations>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -9,27 +9,23 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
public class BlockPlaceListener implements Listener
{
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onBlockPlace(BlockPlaceEvent event)
{
Location loc = event.getBlockPlaced().getLocation();
if (loc == null) return;
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (!border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
{
event.setCancelled(true);
}
}
public class BlockPlaceListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onBlockPlace(BlockPlaceEvent event) {
Location loc = event.getBlockPlaced().getLocation();
if (loc == null) return;
public void unregister()
{
HandlerList.unregisterAll(this);
}
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (!border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound())) {
event.setCancelled(true);
}
}
public void unregister() {
HandlerList.unregisterAll(this);
}
}

View File

@ -1,178 +1,157 @@
package com.wimbli.WorldBorder;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import org.bukkit.World;
import java.util.*;
public class BorderCheckTask implements Runnable
{
@Override
public void run()
{
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
public class BorderCheckTask implements Runnable {
// track players who are being handled (moved back inside the border) already; needed since Bukkit is sometimes sending teleport events with the old (now incorrect) location still indicated, which can lead to a loop when we then teleport them thinking they're outside the border, triggering event again, etc.
private static Set<String> handlingPlayers = Collections.synchronizedSet(new LinkedHashSet<String>());
Collection<Player> players = ImmutableList.copyOf(Bukkit.getServer().getOnlinePlayers());
// set targetLoc only if not current player location; set returnLocationOnly to true to have new Location returned if they need to be moved to one, instead of directly handling it
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly, boolean notify) {
if (player == null || !player.isOnline()) return null;
for (Player player : players)
{
checkPlayer(player, null, false, true);
}
}
Location loc = (targetLoc == null) ? player.getLocation().clone() : targetLoc;
if (loc == null) return null;
// track players who are being handled (moved back inside the border) already; needed since Bukkit is sometimes sending teleport events with the old (now incorrect) location still indicated, which can lead to a loop when we then teleport them thinking they're outside the border, triggering event again, etc.
private static Set<String> handlingPlayers = Collections.synchronizedSet(new LinkedHashSet<String>());
World world = loc.getWorld();
if (world == null) return null;
BorderData border = Config.Border(world.getName());
if (border == null) return null;
// set targetLoc only if not current player location; set returnLocationOnly to true to have new Location returned if they need to be moved to one, instead of directly handling it
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly, boolean notify)
{
if (player == null || !player.isOnline()) return null;
if (border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
return null;
Location loc = (targetLoc == null) ? player.getLocation().clone() : targetLoc;
if (loc == null) return null;
// if player is in bypass list (from bypass command), allow them beyond border; also ignore players currently being handled already
if (Config.isPlayerBypassing(player.getUniqueId()) || handlingPlayers.contains(player.getName().toLowerCase()))
return null;
World world = loc.getWorld();
if (world == null) return null;
BorderData border = Config.Border(world.getName());
if (border == null) return null;
// tag this player as being handled so we can't get stuck in a loop due to Bukkit currently sometimes repeatedly providing incorrect location through teleport event
handlingPlayers.add(player.getName().toLowerCase());
if (border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
return null;
Location newLoc = newLocation(player, loc, border, notify);
boolean handlingVehicle = false;
// if player is in bypass list (from bypass command), allow them beyond border; also ignore players currently being handled already
if (Config.isPlayerBypassing(player.getUniqueId()) || handlingPlayers.contains(player.getName().toLowerCase()))
return null;
/*
* since we need to forcibly eject players who are inside vehicles, that fires a teleport event (go figure) and
* so would effectively double trigger for us, so we need to handle it here to prevent sending two messages and
* two log entries etc.
* after players are ejected we can wait a few ticks (long enough for their client to receive new entity location)
* and then set them as passenger of the vehicle again
*/
if (player.isInsideVehicle()) {
Entity ride = player.getVehicle();
player.leaveVehicle();
if (ride != null) { // vehicles need to be offset vertically and have velocity stopped
double vertOffset = (ride instanceof LivingEntity) ? 0 : ride.getLocation().getY() - loc.getY();
Location rideLoc = newLoc.clone();
rideLoc.setY(newLoc.getY() + vertOffset);
if (Config.Debug())
Config.logWarn("Player was riding a \"" + ride.toString() + "\".");
// tag this player as being handled so we can't get stuck in a loop due to Bukkit currently sometimes repeatedly providing incorrect location through teleport event
handlingPlayers.add(player.getName().toLowerCase());
ride.setVelocity(new Vector(0, 0, 0));
ride.teleport(rideLoc, TeleportCause.PLUGIN);
Location newLoc = newLocation(player, loc, border, notify);
boolean handlingVehicle = false;
if (Config.RemountTicks() > 0) {
setPassengerDelayed(ride, player, player.getName(), Config.RemountTicks());
handlingVehicle = true;
}
}
}
/*
* since we need to forcibly eject players who are inside vehicles, that fires a teleport event (go figure) and
* so would effectively double trigger for us, so we need to handle it here to prevent sending two messages and
* two log entries etc.
* after players are ejected we can wait a few ticks (long enough for their client to receive new entity location)
* and then set them as passenger of the vehicle again
*/
if (player.isInsideVehicle())
{
Entity ride = player.getVehicle();
player.leaveVehicle();
if (ride != null)
{ // vehicles need to be offset vertically and have velocity stopped
double vertOffset = (ride instanceof LivingEntity) ? 0 : ride.getLocation().getY() - loc.getY();
Location rideLoc = newLoc.clone();
rideLoc.setY(newLoc.getY() + vertOffset);
if (Config.Debug())
Config.logWarn("Player was riding a \"" + ride.toString() + "\".");
// check if player has something (a pet, maybe?) riding them; only possible through odd plugins.
// it can prevent all teleportation of the player completely, so it's very much not good and needs handling
List<Entity> passengers = player.getPassengers();
if (!passengers.isEmpty()) {
player.eject();
for (Entity rider : passengers) {
rider.teleport(newLoc, TeleportCause.PLUGIN);
if (Config.Debug())
Config.logWarn("Player had a passenger riding on them: " + rider.getType());
}
player.sendMessage("Your passenger" + ((passengers.size() > 1) ? "s have" : " has") + " been ejected.");
}
ride.setVelocity(new Vector(0, 0, 0));
ride.teleport(rideLoc, TeleportCause.PLUGIN);
// give some particle and sound effects where the player was beyond the border, if "whoosh effect" is enabled
Config.showWhooshEffect(loc);
if (Config.RemountTicks() > 0)
{
setPassengerDelayed(ride, player, player.getName(), Config.RemountTicks());
handlingVehicle = true;
}
}
}
if (!returnLocationOnly)
player.teleport(newLoc, TeleportCause.PLUGIN);
// check if player has something (a pet, maybe?) riding them; only possible through odd plugins.
// it can prevent all teleportation of the player completely, so it's very much not good and needs handling
List<Entity> passengers = player.getPassengers();
if (!passengers.isEmpty())
{
player.eject();
for (Entity rider : passengers)
{
rider.teleport(newLoc, TeleportCause.PLUGIN);
if (Config.Debug())
Config.logWarn("Player had a passenger riding on them: " + rider.getType());
}
player.sendMessage("Your passenger" + ((passengers.size() > 1) ? "s have" : " has") + " been ejected.");
}
if (!handlingVehicle)
handlingPlayers.remove(player.getName().toLowerCase());
// give some particle and sound effects where the player was beyond the border, if "whoosh effect" is enabled
Config.showWhooshEffect(loc);
if (returnLocationOnly)
return newLoc;
if (!returnLocationOnly)
player.teleport(newLoc, TeleportCause.PLUGIN);
return null;
}
if (!handlingVehicle)
handlingPlayers.remove(player.getName().toLowerCase());
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly) {
return checkPlayer(player, targetLoc, returnLocationOnly, true);
}
if (returnLocationOnly)
return newLoc;
private static Location newLocation(Player player, Location loc, BorderData border, boolean notify) {
if (Config.Debug()) {
Config.logWarn((notify ? "Border crossing" : "Check was run") + " in \"" + loc.getWorld().getName() + "\". Border " + border.toString());
Config.logWarn("Player position X: " + Config.coord.format(loc.getX()) + " Y: " + Config.coord.format(loc.getY()) + " Z: " + Config.coord.format(loc.getZ()));
}
return null;
}
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly)
{
return checkPlayer(player, targetLoc, returnLocationOnly, true);
}
Location newLoc = border.correctedPosition(loc, Config.ShapeRound(), player.isFlying());
private static Location newLocation(Player player, Location loc, BorderData border, boolean notify)
{
if (Config.Debug())
{
Config.logWarn((notify ? "Border crossing" : "Check was run") + " in \"" + loc.getWorld().getName() + "\". Border " + border.toString());
Config.logWarn("Player position X: " + Config.coord.format(loc.getX()) + " Y: " + Config.coord.format(loc.getY()) + " Z: " + Config.coord.format(loc.getZ()));
}
// it's remotely possible (such as in the Nether) a suitable location isn't available, in which case...
if (newLoc == null) {
if (Config.Debug())
Config.logWarn("Target new location unviable, using spawn or killing player.");
if (Config.getIfPlayerKill()) {
player.setHealth(0.0D);
return null;
}
newLoc = player.getWorld().getSpawnLocation();
}
Location newLoc = border.correctedPosition(loc, Config.ShapeRound(), player.isFlying());
if (Config.Debug())
Config.logWarn("New position in world \"" + newLoc.getWorld().getName() + "\" at X: " + Config.coord.format(newLoc.getX()) + " Y: " + Config.coord.format(newLoc.getY()) + " Z: " + Config.coord.format(newLoc.getZ()));
// it's remotely possible (such as in the Nether) a suitable location isn't available, in which case...
if (newLoc == null)
{
if (Config.Debug())
Config.logWarn("Target new location unviable, using spawn or killing player.");
if (Config.getIfPlayerKill())
{
player.setHealth(0.0D);
return null;
}
newLoc = player.getWorld().getSpawnLocation();
}
if (notify)
player.sendMessage(Config.Message());
if (Config.Debug())
Config.logWarn("New position in world \"" + newLoc.getWorld().getName() + "\" at X: " + Config.coord.format(newLoc.getX()) + " Y: " + Config.coord.format(newLoc.getY()) + " Z: " + Config.coord.format(newLoc.getZ()));
return newLoc;
}
if (notify)
player.sendMessage(Config.Message());
private static void setPassengerDelayed(final Entity vehicle, final Player player, final String playerName, long delay) {
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() {
@Override
public void run() {
handlingPlayers.remove(playerName.toLowerCase());
if (vehicle == null || player == null)
return;
return newLoc;
}
vehicle.addPassenger(player);
}
}, delay);
}
private static void setPassengerDelayed(final Entity vehicle, final Player player, final String playerName, long delay)
{
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable()
{
@Override
public void run()
{
handlingPlayers.remove(playerName.toLowerCase());
if (vehicle == null || player == null)
return;
@Override
public void run() {
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
vehicle.addPassenger(player);
}
}, delay);
}
Collection<Player> players = ImmutableList.copyOf(Bukkit.getServer().getOnlinePlayers());
for (Player player : players) {
checkPlayer(player, null, false, true);
}
}
}

View File

@ -1,493 +1,461 @@
package com.wimbli.WorldBorder;
import java.util.EnumSet;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
public class BorderData
{
// the main data interacted with
private double x = 0;
private double z = 0;
private int radiusX = 0;
private int radiusZ = 0;
private Boolean shapeRound = null;
private boolean wrapping = false;
// some extra data kept handy for faster border checks
private double maxX;
private double minX;
private double maxZ;
private double minZ;
private double radiusXSquared;
private double radiusZSquared;
private double DefiniteRectangleX;
private double DefiniteRectangleZ;
private double radiusSquaredQuotient;
public BorderData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound, boolean wrap)
{
setData(x, z, radiusX, radiusZ, shapeRound, wrap);
}
public BorderData(double x, double z, int radiusX, int radiusZ)
{
setData(x, z, radiusX, radiusZ, null);
}
public BorderData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound)
{
setData(x, z, radiusX, radiusZ, shapeRound);
}
public BorderData(double x, double z, int radius)
{
setData(x, z, radius, null);
}
public BorderData(double x, double z, int radius, Boolean shapeRound)
{
setData(x, z, radius, shapeRound);
}
public final void setData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound, boolean wrap)
{
this.x = x;
this.z = z;
this.shapeRound = shapeRound;
this.wrapping = wrap;
this.setRadiusX(radiusX);
this.setRadiusZ(radiusZ);
}
public final void setData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound)
{
setData(x, z, radiusX, radiusZ, shapeRound, false);
}
public final void setData(double x, double z, int radius, Boolean shapeRound)
{
setData(x, z, radius, radius, shapeRound, false);
}
public BorderData copy()
{
return new BorderData(x, z, radiusX, radiusZ, shapeRound, wrapping);
}
public double getX()
{
return x;
}
public void setX(double x)
{
this.x = x;
this.maxX = x + radiusX;
this.minX = x - radiusX;
}
public double getZ()
{
return z;
}
public void setZ(double z)
{
this.z = z;
this.maxZ = z + radiusZ;
this.minZ = z - radiusZ;
}
public int getRadiusX()
{
return radiusX;
}
public int getRadiusZ()
{
return radiusZ;
}
public void setRadiusX(int radiusX)
{
this.radiusX = radiusX;
this.maxX = x + radiusX;
this.minX = x - radiusX;
this.radiusXSquared = (double)radiusX * (double)radiusX;
this.radiusSquaredQuotient = this.radiusXSquared / this.radiusZSquared;
this.DefiniteRectangleX = Math.sqrt(.5 * this.radiusXSquared);
}
public void setRadiusZ(int radiusZ)
{
this.radiusZ = radiusZ;
this.maxZ = z + radiusZ;
this.minZ = z - radiusZ;
this.radiusZSquared = (double)radiusZ * (double)radiusZ;
this.radiusSquaredQuotient = this.radiusXSquared / this.radiusZSquared;
this.DefiniteRectangleZ = Math.sqrt(.5 * this.radiusZSquared);
}
import java.util.EnumSet;
// backwards-compatible methods from before elliptical/rectangular shapes were supported
/**
* @deprecated Replaced by {@link #getRadiusX()} and {@link #getRadiusZ()};
* this method now returns an average of those two values and is thus imprecise
*/
public int getRadius()
{
return (radiusX + radiusZ) / 2; // average radius; not great, but probably best for backwards compatibility
}
public void setRadius(int radius)
{
setRadiusX(radius);
setRadiusZ(radius);
}
public class BorderData {
//these material IDs are acceptable for places to teleport player; breathable blocks and water
public static final EnumSet<Material> safeOpenBlocks = EnumSet.noneOf(Material.class);
//these material IDs are ones we don't want to drop the player onto, like cactus or lava or fire or activated Ender portal
public static final EnumSet<Material> painfulBlocks = EnumSet.noneOf(Material.class);
private static final int limBot = 0;
static {
safeOpenBlocks.add(Material.AIR);
safeOpenBlocks.add(Material.CAVE_AIR);
safeOpenBlocks.add(Material.OAK_SAPLING);
safeOpenBlocks.add(Material.SPRUCE_SAPLING);
safeOpenBlocks.add(Material.BIRCH_SAPLING);
safeOpenBlocks.add(Material.JUNGLE_SAPLING);
safeOpenBlocks.add(Material.ACACIA_SAPLING);
safeOpenBlocks.add(Material.DARK_OAK_SAPLING);
safeOpenBlocks.add(Material.WATER);
safeOpenBlocks.add(Material.RAIL);
safeOpenBlocks.add(Material.POWERED_RAIL);
safeOpenBlocks.add(Material.DETECTOR_RAIL);
safeOpenBlocks.add(Material.ACTIVATOR_RAIL);
safeOpenBlocks.add(Material.COBWEB);
safeOpenBlocks.add(Material.GRASS);
safeOpenBlocks.add(Material.FERN);
safeOpenBlocks.add(Material.DEAD_BUSH);
safeOpenBlocks.add(Material.DANDELION);
safeOpenBlocks.add(Material.POPPY);
safeOpenBlocks.add(Material.BLUE_ORCHID);
safeOpenBlocks.add(Material.ALLIUM);
safeOpenBlocks.add(Material.AZURE_BLUET);
safeOpenBlocks.add(Material.RED_TULIP);
safeOpenBlocks.add(Material.ORANGE_TULIP);
safeOpenBlocks.add(Material.WHITE_TULIP);
safeOpenBlocks.add(Material.PINK_TULIP);
safeOpenBlocks.add(Material.OXEYE_DAISY);
safeOpenBlocks.add(Material.BROWN_MUSHROOM);
safeOpenBlocks.add(Material.RED_MUSHROOM);
safeOpenBlocks.add(Material.TORCH);
safeOpenBlocks.add(Material.WALL_TORCH);
safeOpenBlocks.add(Material.REDSTONE_WIRE);
safeOpenBlocks.add(Material.WHEAT);
safeOpenBlocks.add(Material.LADDER);
safeOpenBlocks.add(Material.LEVER);
safeOpenBlocks.add(Material.LIGHT_WEIGHTED_PRESSURE_PLATE);
safeOpenBlocks.add(Material.HEAVY_WEIGHTED_PRESSURE_PLATE);
safeOpenBlocks.add(Material.STONE_PRESSURE_PLATE);
safeOpenBlocks.add(Material.OAK_PRESSURE_PLATE);
safeOpenBlocks.add(Material.SPRUCE_PRESSURE_PLATE);
safeOpenBlocks.add(Material.BIRCH_PRESSURE_PLATE);
safeOpenBlocks.add(Material.JUNGLE_PRESSURE_PLATE);
safeOpenBlocks.add(Material.ACACIA_PRESSURE_PLATE);
safeOpenBlocks.add(Material.DARK_OAK_PRESSURE_PLATE);
safeOpenBlocks.add(Material.REDSTONE_TORCH);
safeOpenBlocks.add(Material.REDSTONE_WALL_TORCH);
safeOpenBlocks.add(Material.STONE_BUTTON);
safeOpenBlocks.add(Material.SNOW);
safeOpenBlocks.add(Material.SUGAR_CANE);
safeOpenBlocks.add(Material.REPEATER);
safeOpenBlocks.add(Material.COMPARATOR);
safeOpenBlocks.add(Material.OAK_TRAPDOOR);
safeOpenBlocks.add(Material.SPRUCE_TRAPDOOR);
safeOpenBlocks.add(Material.BIRCH_TRAPDOOR);
safeOpenBlocks.add(Material.JUNGLE_TRAPDOOR);
safeOpenBlocks.add(Material.ACACIA_TRAPDOOR);
safeOpenBlocks.add(Material.DARK_OAK_TRAPDOOR);
safeOpenBlocks.add(Material.MELON_STEM);
safeOpenBlocks.add(Material.ATTACHED_MELON_STEM);
safeOpenBlocks.add(Material.PUMPKIN_STEM);
safeOpenBlocks.add(Material.ATTACHED_PUMPKIN_STEM);
safeOpenBlocks.add(Material.VINE);
safeOpenBlocks.add(Material.NETHER_WART);
safeOpenBlocks.add(Material.TRIPWIRE);
safeOpenBlocks.add(Material.TRIPWIRE_HOOK);
safeOpenBlocks.add(Material.CARROTS);
safeOpenBlocks.add(Material.POTATOES);
safeOpenBlocks.add(Material.OAK_BUTTON);
safeOpenBlocks.add(Material.SPRUCE_BUTTON);
safeOpenBlocks.add(Material.BIRCH_BUTTON);
safeOpenBlocks.add(Material.JUNGLE_BUTTON);
safeOpenBlocks.add(Material.ACACIA_BUTTON);
safeOpenBlocks.add(Material.DARK_OAK_BUTTON);
safeOpenBlocks.add(Material.SUNFLOWER);
safeOpenBlocks.add(Material.LILAC);
safeOpenBlocks.add(Material.ROSE_BUSH);
safeOpenBlocks.add(Material.PEONY);
safeOpenBlocks.add(Material.TALL_GRASS);
safeOpenBlocks.add(Material.LARGE_FERN);
safeOpenBlocks.add(Material.BEETROOTS);
try { // signs in 1.14 can be different wood types
safeOpenBlocks.add(Material.ACACIA_SIGN);
safeOpenBlocks.add(Material.ACACIA_WALL_SIGN);
safeOpenBlocks.add(Material.BIRCH_SIGN);
safeOpenBlocks.add(Material.BIRCH_WALL_SIGN);
safeOpenBlocks.add(Material.DARK_OAK_SIGN);
safeOpenBlocks.add(Material.DARK_OAK_WALL_SIGN);
safeOpenBlocks.add(Material.JUNGLE_SIGN);
safeOpenBlocks.add(Material.JUNGLE_WALL_SIGN);
safeOpenBlocks.add(Material.OAK_SIGN);
safeOpenBlocks.add(Material.OAK_WALL_SIGN);
safeOpenBlocks.add(Material.SPRUCE_SIGN);
safeOpenBlocks.add(Material.SPRUCE_WALL_SIGN);
} catch (NoSuchFieldError ex) {
}
}
static {
painfulBlocks.add(Material.LAVA);
painfulBlocks.add(Material.FIRE);
painfulBlocks.add(Material.CACTUS);
painfulBlocks.add(Material.END_PORTAL);
painfulBlocks.add(Material.MAGMA_BLOCK);
}
// the main data interacted with
private double x = 0;
private double z = 0;
private int radiusX = 0;
private int radiusZ = 0;
private Boolean shapeRound = null;
private boolean wrapping = false;
// some extra data kept handy for faster border checks
private double maxX;
private double minX;
private double maxZ;
private double minZ;
private double radiusXSquared;
private double radiusZSquared;
private double DefiniteRectangleX;
private double DefiniteRectangleZ;
private double radiusSquaredQuotient;
public BorderData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound, boolean wrap) {
setData(x, z, radiusX, radiusZ, shapeRound, wrap);
}
public BorderData(double x, double z, int radiusX, int radiusZ) {
setData(x, z, radiusX, radiusZ, null);
}
public BorderData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound) {
setData(x, z, radiusX, radiusZ, shapeRound);
}
public BorderData(double x, double z, int radius) {
setData(x, z, radius, null);
}
public BorderData(double x, double z, int radius, Boolean shapeRound) {
setData(x, z, radius, shapeRound);
}
public final void setData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound, boolean wrap) {
this.x = x;
this.z = z;
this.shapeRound = shapeRound;
this.wrapping = wrap;
this.setRadiusX(radiusX);
this.setRadiusZ(radiusZ);
}
public final void setData(double x, double z, int radiusX, int radiusZ, Boolean shapeRound) {
setData(x, z, radiusX, radiusZ, shapeRound, false);
}
public final void setData(double x, double z, int radius, Boolean shapeRound) {
setData(x, z, radius, radius, shapeRound, false);
}
public BorderData copy() {
return new BorderData(x, z, radiusX, radiusZ, shapeRound, wrapping);
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
this.maxX = x + radiusX;
this.minX = x - radiusX;
}
public double getZ() {
return z;
}
public Boolean getShape()
{
return shapeRound;
}
public void setShape(Boolean shapeRound)
{
this.shapeRound = shapeRound;
}
// backwards-compatible methods from before elliptical/rectangular shapes were supported
public void setZ(double z) {
this.z = z;
this.maxZ = z + radiusZ;
this.minZ = z - radiusZ;
}
public int getRadiusX() {
return radiusX;
}
public void setRadiusX(int radiusX) {
this.radiusX = radiusX;
this.maxX = x + radiusX;
this.minX = x - radiusX;
this.radiusXSquared = (double) radiusX * (double) radiusX;
this.radiusSquaredQuotient = this.radiusXSquared / this.radiusZSquared;
this.DefiniteRectangleX = Math.sqrt(.5 * this.radiusXSquared);
}
public int getRadiusZ() {
return radiusZ;
}
public void setRadiusZ(int radiusZ) {
this.radiusZ = radiusZ;
this.maxZ = z + radiusZ;
this.minZ = z - radiusZ;
this.radiusZSquared = (double) radiusZ * (double) radiusZ;
this.radiusSquaredQuotient = this.radiusXSquared / this.radiusZSquared;
this.DefiniteRectangleZ = Math.sqrt(.5 * this.radiusZSquared);
}
/**
* @deprecated Replaced by {@link #getRadiusX()} and {@link #getRadiusZ()};
* this method now returns an average of those two values and is thus imprecise
*/
public int getRadius() {
return (radiusX + radiusZ) / 2; // average radius; not great, but probably best for backwards compatibility
}
public void setRadius(int radius) {
setRadiusX(radius);
setRadiusZ(radius);
}
public Boolean getShape() {
return shapeRound;
}
public void setShape(Boolean shapeRound) {
this.shapeRound = shapeRound;
}
public boolean getWrapping() {
return wrapping;
}
public void setWrapping(boolean wrap) {
this.wrapping = wrap;
}
@Override
public String toString() {
return "radius " + ((radiusX == radiusZ) ? radiusX : radiusX + "x" + radiusZ) + " at X: " + Config.coord.format(x) + " Z: " + Config.coord.format(z) + (shapeRound != null ? (" (shape override: " + Config.ShapeName(shapeRound.booleanValue()) + ")") : "") + (wrapping ? (" (wrapping)") : "");
}
// This algorithm of course needs to be fast, since it will be run very frequently
public boolean insideBorder(double xLoc, double zLoc, boolean round) {
// if this border has a shape override set, use it
if (shapeRound != null)
round = shapeRound.booleanValue();
// square border
if (!round)
return !(xLoc < minX || xLoc > maxX || zLoc < minZ || zLoc > maxZ);
// round border
else {
// elegant round border checking algorithm is from rBorder by Reil with almost no changes, all credit to him for it
double X = Math.abs(x - xLoc);
double Z = Math.abs(z - zLoc);
if (X < DefiniteRectangleX && Z < DefiniteRectangleZ)
return true; // Definitely inside
else if (X >= radiusX || Z >= radiusZ)
return false; // Definitely outside
else if (X * X + Z * Z * radiusSquaredQuotient < radiusXSquared)
return true; // After further calculation, inside
else
return false; // Apparently outside, then
}
}
public boolean insideBorder(double xLoc, double zLoc) {
return insideBorder(xLoc, zLoc, Config.ShapeRound());
}
public boolean insideBorder(Location loc) {
return insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound());
}
public boolean insideBorder(CoordXZ coord, boolean round) {
return insideBorder(coord.x, coord.z, round);
}
public boolean insideBorder(CoordXZ coord) {
return insideBorder(coord.x, coord.z, Config.ShapeRound());
}
public Location correctedPosition(Location loc, boolean round, boolean flying) {
// if this border has a shape override set, use it
if (shapeRound != null)
round = shapeRound.booleanValue();
double xLoc = loc.getX();
double zLoc = loc.getZ();
double yLoc = loc.getY();
// square border
if (!round) {
if (wrapping) {
if (xLoc <= minX)
xLoc = maxX - Config.KnockBack();
else if (xLoc >= maxX)
xLoc = minX + Config.KnockBack();
if (zLoc <= minZ)
zLoc = maxZ - Config.KnockBack();
else if (zLoc >= maxZ)
zLoc = minZ + Config.KnockBack();
} else {
if (xLoc <= minX)
xLoc = minX + Config.KnockBack();
else if (xLoc >= maxX)
xLoc = maxX - Config.KnockBack();
if (zLoc <= minZ)
zLoc = minZ + Config.KnockBack();
else if (zLoc >= maxZ)
zLoc = maxZ - Config.KnockBack();
}
}
// round border
else {
// algorithm originally from: http://stackoverflow.com/questions/300871/best-way-to-find-a-point-on-a-circle-closest-to-a-given-point
// modified by Lang Lukas to support elliptical border shape
//Transform the ellipse to a circle with radius 1 (we need to transform the point the same way)
double dX = xLoc - x;
double dZ = zLoc - z;
double dU = Math.sqrt(dX * dX + dZ * dZ); //distance of the untransformed point from the center
double dT = Math.sqrt(dX * dX / radiusXSquared + dZ * dZ / radiusZSquared); //distance of the transformed point from the center
double f = (1 / dT - Config.KnockBack() / dU); //"correction" factor for the distances
if (wrapping) {
xLoc = x - dX * f;
zLoc = z - dZ * f;
} else {
xLoc = x + dX * f;
zLoc = z + dZ * f;
}
}
int ixLoc = Location.locToBlock(xLoc);
int izLoc = Location.locToBlock(zLoc);
// Make sure the chunk we're checking in is actually loaded
Chunk tChunk = loc.getWorld().getChunkAt(CoordXZ.blockToChunk(ixLoc), CoordXZ.blockToChunk(izLoc));
if (!tChunk.isLoaded())
tChunk.load();
yLoc = getSafeY(loc.getWorld(), ixLoc, Location.locToBlock(yLoc), izLoc, flying);
if (yLoc == -1)
return null;
return new Location(loc.getWorld(), Math.floor(xLoc) + 0.5, yLoc, Math.floor(zLoc) + 0.5, loc.getYaw(), loc.getPitch());
}
public Location correctedPosition(Location loc, boolean round) {
return correctedPosition(loc, round, false);
}
public Location correctedPosition(Location loc) {
return correctedPosition(loc, Config.ShapeRound(), false);
}
// check if a particular spot consists of 2 breathable blocks over something relatively solid
private boolean isSafeSpot(World world, int X, int Y, int Z, boolean flying) {
boolean safe = safeOpenBlocks.contains(world.getBlockAt(X, Y, Z).getType()) // target block open and safe
&& safeOpenBlocks.contains(world.getBlockAt(X, Y + 1, Z).getType()); // above target block open and safe
if (!safe || flying)
return safe;
Material below = world.getBlockAt(X, Y - 1, Z).getType();
return (safe
&& (!safeOpenBlocks.contains(below) || below == Material.WATER) // below target block not open/breathable (so presumably solid), or is water
&& !painfulBlocks.contains(below) // below target block not painful
);
}
// find closest safe Y position from the starting position
private double getSafeY(World world, int X, int Y, int Z, boolean flying) {
// artificial height limit of 127 added for Nether worlds since CraftBukkit still incorrectly returns 255 for their max height, leading to players sent to the "roof" of the Nether
final boolean isNether = world.getEnvironment() == World.Environment.NETHER;
int limTop = isNether ? 125 : world.getMaxHeight() - 2;
final int highestBlockBoundary = Math.min(world.getHighestBlockYAt(X, Z) + 1, limTop);
// if Y is larger than the world can be and user can fly, return Y - Unless we are in the Nether, we might not want players on the roof
if (flying && Y > limTop && !isNether)
return (double) Y;
// make sure Y values are within the boundaries of the world.
if (Y > limTop) {
if (isNether)
Y = limTop; // because of the roof, the nether can not rely on highestBlockBoundary, so limTop has to be used
else {
if (flying)
Y = limTop;
else
Y = highestBlockBoundary; // there will never be a save block to stand on for Y values > highestBlockBoundary
}
}
if (Y < limBot)
Y = limBot;
// for non Nether worlds we don't need to check upwards to the world-limit, it is enough to check up to the highestBlockBoundary, unless player is flying
if (!isNether && !flying)
limTop = highestBlockBoundary;
// Expanding Y search method adapted from Acru's code in the Nether plugin
for (int y1 = Y, y2 = Y; (y1 > limBot) || (y2 < limTop); y1--, y2++) {
// Look below.
if (y1 > limBot) {
if (isSafeSpot(world, X, y1, Z, flying))
return (double) y1;
}
// Look above.
if (y2 < limTop && y2 != y1) {
if (isSafeSpot(world, X, y2, Z, flying))
return (double) y2;
}
}
return -1.0; // no safe Y location?!?!? Must be a rare spot in a Nether world or something
}
public boolean getWrapping()
{
return wrapping;
}
public void setWrapping(boolean wrap)
{
this.wrapping = wrap;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
else if (obj == null || obj.getClass() != this.getClass())
return false;
BorderData test = (BorderData) obj;
return test.x == this.x && test.z == this.z && test.radiusX == this.radiusX && test.radiusZ == this.radiusZ;
}
@Override
public String toString()
{
return "radius " + ((radiusX == radiusZ) ? radiusX : radiusX + "x" + radiusZ) + " at X: " + Config.coord.format(x) + " Z: " + Config.coord.format(z) + (shapeRound != null ? (" (shape override: " + Config.ShapeName(shapeRound.booleanValue()) + ")") : "") + (wrapping ? (" (wrapping)") : "");
}
// This algorithm of course needs to be fast, since it will be run very frequently
public boolean insideBorder(double xLoc, double zLoc, boolean round)
{
// if this border has a shape override set, use it
if (shapeRound != null)
round = shapeRound.booleanValue();
// square border
if (!round)
return !(xLoc < minX || xLoc > maxX || zLoc < minZ || zLoc > maxZ);
// round border
else
{
// elegant round border checking algorithm is from rBorder by Reil with almost no changes, all credit to him for it
double X = Math.abs(x - xLoc);
double Z = Math.abs(z - zLoc);
if (X < DefiniteRectangleX && Z < DefiniteRectangleZ)
return true; // Definitely inside
else if (X >= radiusX || Z >= radiusZ)
return false; // Definitely outside
else if (X * X + Z * Z * radiusSquaredQuotient < radiusXSquared)
return true; // After further calculation, inside
else
return false; // Apparently outside, then
}
}
public boolean insideBorder(double xLoc, double zLoc)
{
return insideBorder(xLoc, zLoc, Config.ShapeRound());
}
public boolean insideBorder(Location loc)
{
return insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound());
}
public boolean insideBorder(CoordXZ coord, boolean round)
{
return insideBorder(coord.x, coord.z, round);
}
public boolean insideBorder(CoordXZ coord)
{
return insideBorder(coord.x, coord.z, Config.ShapeRound());
}
public Location correctedPosition(Location loc, boolean round, boolean flying)
{
// if this border has a shape override set, use it
if (shapeRound != null)
round = shapeRound.booleanValue();
double xLoc = loc.getX();
double zLoc = loc.getZ();
double yLoc = loc.getY();
// square border
if (!round)
{
if (wrapping)
{
if (xLoc <= minX)
xLoc = maxX - Config.KnockBack();
else if (xLoc >= maxX)
xLoc = minX + Config.KnockBack();
if (zLoc <= minZ)
zLoc = maxZ - Config.KnockBack();
else if (zLoc >= maxZ)
zLoc = minZ + Config.KnockBack();
}
else
{
if (xLoc <= minX)
xLoc = minX + Config.KnockBack();
else if (xLoc >= maxX)
xLoc = maxX - Config.KnockBack();
if (zLoc <= minZ)
zLoc = minZ + Config.KnockBack();
else if (zLoc >= maxZ)
zLoc = maxZ - Config.KnockBack();
}
}
// round border
else
{
// algorithm originally from: http://stackoverflow.com/questions/300871/best-way-to-find-a-point-on-a-circle-closest-to-a-given-point
// modified by Lang Lukas to support elliptical border shape
//Transform the ellipse to a circle with radius 1 (we need to transform the point the same way)
double dX = xLoc - x;
double dZ = zLoc - z;
double dU = Math.sqrt(dX *dX + dZ * dZ); //distance of the untransformed point from the center
double dT = Math.sqrt(dX *dX / radiusXSquared + dZ * dZ / radiusZSquared); //distance of the transformed point from the center
double f = (1 / dT - Config.KnockBack() / dU); //"correction" factor for the distances
if (wrapping)
{
xLoc = x - dX * f;
zLoc = z - dZ * f;
} else {
xLoc = x + dX * f;
zLoc = z + dZ * f;
}
}
int ixLoc = Location.locToBlock(xLoc);
int izLoc = Location.locToBlock(zLoc);
// Make sure the chunk we're checking in is actually loaded
Chunk tChunk = loc.getWorld().getChunkAt(CoordXZ.blockToChunk(ixLoc), CoordXZ.blockToChunk(izLoc));
if (!tChunk.isLoaded())
tChunk.load();
yLoc = getSafeY(loc.getWorld(), ixLoc, Location.locToBlock(yLoc), izLoc, flying);
if (yLoc == -1)
return null;
return new Location(loc.getWorld(), Math.floor(xLoc) + 0.5, yLoc, Math.floor(zLoc) + 0.5, loc.getYaw(), loc.getPitch());
}
public Location correctedPosition(Location loc, boolean round)
{
return correctedPosition(loc, round, false);
}
public Location correctedPosition(Location loc)
{
return correctedPosition(loc, Config.ShapeRound(), false);
}
//these material IDs are acceptable for places to teleport player; breathable blocks and water
public static final EnumSet<Material> safeOpenBlocks = EnumSet.noneOf(Material.class);
static
{
safeOpenBlocks.add(Material.AIR);
safeOpenBlocks.add(Material.CAVE_AIR);
safeOpenBlocks.add(Material.OAK_SAPLING);
safeOpenBlocks.add(Material.SPRUCE_SAPLING);
safeOpenBlocks.add(Material.BIRCH_SAPLING);
safeOpenBlocks.add(Material.JUNGLE_SAPLING);
safeOpenBlocks.add(Material.ACACIA_SAPLING);
safeOpenBlocks.add(Material.DARK_OAK_SAPLING);
safeOpenBlocks.add(Material.WATER);
safeOpenBlocks.add(Material.RAIL);
safeOpenBlocks.add(Material.POWERED_RAIL);
safeOpenBlocks.add(Material.DETECTOR_RAIL);
safeOpenBlocks.add(Material.ACTIVATOR_RAIL);
safeOpenBlocks.add(Material.COBWEB);
safeOpenBlocks.add(Material.GRASS);
safeOpenBlocks.add(Material.FERN);
safeOpenBlocks.add(Material.DEAD_BUSH);
safeOpenBlocks.add(Material.DANDELION);
safeOpenBlocks.add(Material.POPPY);
safeOpenBlocks.add(Material.BLUE_ORCHID);
safeOpenBlocks.add(Material.ALLIUM);
safeOpenBlocks.add(Material.AZURE_BLUET);
safeOpenBlocks.add(Material.RED_TULIP);
safeOpenBlocks.add(Material.ORANGE_TULIP);
safeOpenBlocks.add(Material.WHITE_TULIP);
safeOpenBlocks.add(Material.PINK_TULIP);
safeOpenBlocks.add(Material.OXEYE_DAISY);
safeOpenBlocks.add(Material.BROWN_MUSHROOM);
safeOpenBlocks.add(Material.RED_MUSHROOM);
safeOpenBlocks.add(Material.TORCH);
safeOpenBlocks.add(Material.WALL_TORCH);
safeOpenBlocks.add(Material.REDSTONE_WIRE);
safeOpenBlocks.add(Material.WHEAT);
safeOpenBlocks.add(Material.LADDER);
safeOpenBlocks.add(Material.LEVER);
safeOpenBlocks.add(Material.LIGHT_WEIGHTED_PRESSURE_PLATE);
safeOpenBlocks.add(Material.HEAVY_WEIGHTED_PRESSURE_PLATE);
safeOpenBlocks.add(Material.STONE_PRESSURE_PLATE);
safeOpenBlocks.add(Material.OAK_PRESSURE_PLATE);
safeOpenBlocks.add(Material.SPRUCE_PRESSURE_PLATE);
safeOpenBlocks.add(Material.BIRCH_PRESSURE_PLATE);
safeOpenBlocks.add(Material.JUNGLE_PRESSURE_PLATE);
safeOpenBlocks.add(Material.ACACIA_PRESSURE_PLATE);
safeOpenBlocks.add(Material.DARK_OAK_PRESSURE_PLATE);
safeOpenBlocks.add(Material.REDSTONE_TORCH);
safeOpenBlocks.add(Material.REDSTONE_WALL_TORCH);
safeOpenBlocks.add(Material.STONE_BUTTON);
safeOpenBlocks.add(Material.SNOW);
safeOpenBlocks.add(Material.SUGAR_CANE);
safeOpenBlocks.add(Material.REPEATER);
safeOpenBlocks.add(Material.COMPARATOR);
safeOpenBlocks.add(Material.OAK_TRAPDOOR);
safeOpenBlocks.add(Material.SPRUCE_TRAPDOOR);
safeOpenBlocks.add(Material.BIRCH_TRAPDOOR);
safeOpenBlocks.add(Material.JUNGLE_TRAPDOOR);
safeOpenBlocks.add(Material.ACACIA_TRAPDOOR);
safeOpenBlocks.add(Material.DARK_OAK_TRAPDOOR);
safeOpenBlocks.add(Material.MELON_STEM);
safeOpenBlocks.add(Material.ATTACHED_MELON_STEM);
safeOpenBlocks.add(Material.PUMPKIN_STEM);
safeOpenBlocks.add(Material.ATTACHED_PUMPKIN_STEM);
safeOpenBlocks.add(Material.VINE);
safeOpenBlocks.add(Material.NETHER_WART);
safeOpenBlocks.add(Material.TRIPWIRE);
safeOpenBlocks.add(Material.TRIPWIRE_HOOK);
safeOpenBlocks.add(Material.CARROTS);
safeOpenBlocks.add(Material.POTATOES);
safeOpenBlocks.add(Material.OAK_BUTTON);
safeOpenBlocks.add(Material.SPRUCE_BUTTON);
safeOpenBlocks.add(Material.BIRCH_BUTTON);
safeOpenBlocks.add(Material.JUNGLE_BUTTON);
safeOpenBlocks.add(Material.ACACIA_BUTTON);
safeOpenBlocks.add(Material.DARK_OAK_BUTTON);
safeOpenBlocks.add(Material.SUNFLOWER);
safeOpenBlocks.add(Material.LILAC);
safeOpenBlocks.add(Material.ROSE_BUSH);
safeOpenBlocks.add(Material.PEONY);
safeOpenBlocks.add(Material.TALL_GRASS);
safeOpenBlocks.add(Material.LARGE_FERN);
safeOpenBlocks.add(Material.BEETROOTS);
try
{ // signs in 1.14 can be different wood types
safeOpenBlocks.add(Material.ACACIA_SIGN);
safeOpenBlocks.add(Material.ACACIA_WALL_SIGN);
safeOpenBlocks.add(Material.BIRCH_SIGN);
safeOpenBlocks.add(Material.BIRCH_WALL_SIGN);
safeOpenBlocks.add(Material.DARK_OAK_SIGN);
safeOpenBlocks.add(Material.DARK_OAK_WALL_SIGN);
safeOpenBlocks.add(Material.JUNGLE_SIGN);
safeOpenBlocks.add(Material.JUNGLE_WALL_SIGN);
safeOpenBlocks.add(Material.OAK_SIGN);
safeOpenBlocks.add(Material.OAK_WALL_SIGN);
safeOpenBlocks.add(Material.SPRUCE_SIGN);
safeOpenBlocks.add(Material.SPRUCE_WALL_SIGN);
}
catch (NoSuchFieldError ex) {}
}
//these material IDs are ones we don't want to drop the player onto, like cactus or lava or fire or activated Ender portal
public static final EnumSet<Material> painfulBlocks = EnumSet.noneOf(Material.class);
static
{
painfulBlocks.add(Material.LAVA);
painfulBlocks.add(Material.FIRE);
painfulBlocks.add(Material.CACTUS);
painfulBlocks.add(Material.END_PORTAL);
painfulBlocks.add(Material.MAGMA_BLOCK);
}
// check if a particular spot consists of 2 breathable blocks over something relatively solid
private boolean isSafeSpot(World world, int X, int Y, int Z, boolean flying)
{
boolean safe = safeOpenBlocks.contains(world.getBlockAt(X, Y, Z).getType()) // target block open and safe
&& safeOpenBlocks.contains(world.getBlockAt(X, Y + 1, Z).getType()); // above target block open and safe
if (!safe || flying)
return safe;
Material below = world.getBlockAt(X, Y - 1, Z).getType();
return (safe
&& (!safeOpenBlocks.contains(below) || below == Material.WATER) // below target block not open/breathable (so presumably solid), or is water
&& !painfulBlocks.contains(below) // below target block not painful
);
}
private static final int limBot = 0;
// find closest safe Y position from the starting position
private double getSafeY(World world, int X, int Y, int Z, boolean flying)
{
// artificial height limit of 127 added for Nether worlds since CraftBukkit still incorrectly returns 255 for their max height, leading to players sent to the "roof" of the Nether
final boolean isNether = world.getEnvironment() == World.Environment.NETHER;
int limTop = isNether ? 125 : world.getMaxHeight() - 2;
final int highestBlockBoundary = Math.min(world.getHighestBlockYAt(X, Z) + 1, limTop);
// if Y is larger than the world can be and user can fly, return Y - Unless we are in the Nether, we might not want players on the roof
if (flying && Y > limTop && !isNether)
return (double) Y;
// make sure Y values are within the boundaries of the world.
if (Y > limTop)
{
if (isNether)
Y = limTop; // because of the roof, the nether can not rely on highestBlockBoundary, so limTop has to be used
else
{
if (flying)
Y = limTop;
else
Y = highestBlockBoundary; // there will never be a save block to stand on for Y values > highestBlockBoundary
}
}
if (Y < limBot)
Y = limBot;
// for non Nether worlds we don't need to check upwards to the world-limit, it is enough to check up to the highestBlockBoundary, unless player is flying
if (!isNether && !flying)
limTop = highestBlockBoundary;
// Expanding Y search method adapted from Acru's code in the Nether plugin
for(int y1 = Y, y2 = Y; (y1 > limBot) || (y2 < limTop); y1--, y2++){
// Look below.
if(y1 > limBot)
{
if (isSafeSpot(world, X, y1, Z, flying))
return (double)y1;
}
// Look above.
if(y2 < limTop && y2 != y1)
{
if (isSafeSpot(world, X, y2, Z, flying))
return (double)y2;
}
}
return -1.0; // no safe Y location?!?!? Must be a rare spot in a Nether world or something
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
else if (obj == null || obj.getClass() != this.getClass())
return false;
BorderData test = (BorderData)obj;
return test.x == this.x && test.z == this.z && test.radiusX == this.radiusX && test.radiusZ == this.radiusZ;
}
@Override
public int hashCode()
{
return (((int)(this.x * 10) << 4) + (int)this.z + (this.radiusX << 2) + (this.radiusZ << 3));
}
@Override
public int hashCode() {
return (((int) (this.x * 10) << 4) + (int) this.z + (this.radiusX << 2) + (this.radiusZ << 3));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,58 +2,54 @@ package com.wimbli.WorldBorder;
// simple storage class for chunk x/z values
public class CoordXZ
{
public int x, z;
public CoordXZ(int x, int z)
{
this.x = x;
this.z = z;
}
public class CoordXZ {
public int x, z;
// transform values between block, chunk, and region
// bit-shifting is used because it's mucho rapido
public static int blockToChunk(int blockVal)
{ // 1 chunk is 16x16 blocks
return blockVal >> 4; // ">>4" == "/16"
}
public static int blockToRegion(int blockVal)
{ // 1 region is 512x512 blocks
return blockVal >> 9; // ">>9" == "/512"
}
public static int chunkToRegion(int chunkVal)
{ // 1 region is 32x32 chunks
return chunkVal >> 5; // ">>5" == "/32"
}
public static int chunkToBlock(int chunkVal)
{
return chunkVal << 4; // "<<4" == "*16"
}
public static int regionToBlock(int regionVal)
{
return regionVal << 9; // "<<9" == "*512"
}
public static int regionToChunk(int regionVal)
{
return regionVal << 5; // "<<5" == "*32"
}
public CoordXZ(int x, int z) {
this.x = x;
this.z = z;
}
// transform values between block, chunk, and region
// bit-shifting is used because it's mucho rapido
public static int blockToChunk(int blockVal) { // 1 chunk is 16x16 blocks
return blockVal >> 4; // ">>4" == "/16"
}
public static int blockToRegion(int blockVal) { // 1 region is 512x512 blocks
return blockVal >> 9; // ">>9" == "/512"
}
public static int chunkToRegion(int chunkVal) { // 1 region is 32x32 chunks
return chunkVal >> 5; // ">>5" == "/32"
}
public static int chunkToBlock(int chunkVal) {
return chunkVal << 4; // "<<4" == "*16"
}
public static int regionToBlock(int regionVal) {
return regionVal << 9; // "<<9" == "*512"
}
public static int regionToChunk(int regionVal) {
return regionVal << 5; // "<<5" == "*32"
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
else if (obj == null || obj.getClass() != this.getClass())
return false;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
else if (obj == null || obj.getClass() != this.getClass())
return false;
CoordXZ test = (CoordXZ)obj;
return test.x == this.x && test.z == this.z;
}
CoordXZ test = (CoordXZ) obj;
return test.x == this.x && test.z == this.z;
}
@Override
public int hashCode()
{
return (this.x << 9) + this.z;
}
@Override
public int hashCode() {
return (this.x << 9) + this.z;
}
}

View File

@ -1,251 +1,217 @@
package com.wimbli.WorldBorder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.dynmap.DynmapAPI;
import org.dynmap.markers.AreaMarker;
import org.dynmap.markers.CircleMarker;
import org.dynmap.markers.MarkerAPI;
import org.dynmap.markers.MarkerSet;
public class DynMapFeatures
{
private static DynmapAPI api;
private static MarkerAPI markApi;
private static MarkerSet markSet;
private static int lineWeight = 3;
private static double lineOpacity = 1.0;
private static int lineColor = 0xFF0000;
// Whether re-rendering functionality is available
public static boolean renderEnabled()
{
return api != null;
}
// Whether circular border markers are available
public static boolean borderEnabled()
{
return markApi != null;
}
public static void setup()
{
Plugin test = Bukkit.getServer().getPluginManager().getPlugin("dynmap");
if (test == null || !test.isEnabled()) return;
api = (DynmapAPI)test;
// make sure DynMap version is new enough to include circular markers
try
{
Class.forName("org.dynmap.markers.CircleMarker");
// for version 0.35 of DynMap, CircleMarkers had just been introduced and were bugged (center position always 0,0)
if (api.getDynmapVersion().startsWith("0.35-"))
throw new ClassNotFoundException();
}
catch (ClassNotFoundException ex)
{
Config.logConfig("DynMap is available, but border display is currently disabled: you need DynMap v0.36 or newer.");
return;
}
catch (NullPointerException ex)
{
Config.logConfig("DynMap is present, but an NPE (type 1) was encountered while trying to integrate. Border display disabled.");
return;
}
try
{
markApi = api.getMarkerAPI();
if (markApi == null) return;
}
catch (NullPointerException ex)
{
Config.logConfig("DynMap is present, but an NPE (type 2) was encountered while trying to integrate. Border display disabled.");
return;
}
// go ahead and show borders for all worlds
showAllBorders();
Config.logConfig("Successfully hooked into DynMap for the ability to display borders.");
}
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/*
* Re-rendering methods, used for updating trimmed chunks to show them as gone
* Sadly, not currently working. Might not even be possible to make it work.
*/
public static void renderRegion(String worldName, CoordXZ coord)
{
if (!renderEnabled()) return;
World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255;
int x = CoordXZ.regionToBlock(coord.x);
int z = CoordXZ.regionToBlock(coord.z);
api.triggerRenderOfVolume(worldName, x, 0, z, x+511, y, z+511);
}
public static void renderChunks(String worldName, List<CoordXZ> coords)
{
if (!renderEnabled()) return;
World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255;
for (CoordXZ coord : coords)
{
renderChunk(worldName, coord, y);
}
}
public static void renderChunk(String worldName, CoordXZ coord, int maxY)
{
if (!renderEnabled()) return;
int x = CoordXZ.chunkToBlock(coord.x);
int z = CoordXZ.chunkToBlock(coord.z);
api.triggerRenderOfVolume(worldName, x, 0, z, x+15, maxY, z+15);
}
/*
* Methods for displaying our borders on DynMap's world maps
*/
public class DynMapFeatures {
private static DynmapAPI api;
private static MarkerAPI markApi;
private static MarkerSet markSet;
private static int lineWeight = 3;
private static double lineOpacity = 1.0;
private static int lineColor = 0xFF0000;
private static Map<String, CircleMarker> roundBorders = new HashMap<String, CircleMarker>();
private static Map<String, AreaMarker> squareBorders = new HashMap<String, AreaMarker>();
public static void showAllBorders()
{
if (!borderEnabled()) return;
// Whether re-rendering functionality is available
public static boolean renderEnabled() {
return api != null;
}
// in case any borders are already shown
removeAllBorders();
if (!Config.DynmapBorderEnabled())
{
// don't want to show the marker set in DynMap if our integration is disabled
if (markSet != null)
markSet.deleteMarkerSet();
markSet = null;
return;
}
/*
* Re-rendering methods, used for updating trimmed chunks to show them as gone
* Sadly, not currently working. Might not even be possible to make it work.
*/
// make sure the marker set is initialized
markSet = markApi.getMarkerSet("worldborder.markerset");
if(markSet == null)
markSet = markApi.createMarkerSet("worldborder.markerset", "WorldBorder", null, false);
else
markSet.setMarkerSetLabel("WorldBorder");
markSet.setLayerPriority(Config.DynmapPriority());
markSet.setHideByDefault(Config.DynmapHideByDefault());
Map<String, BorderData> borders = Config.getBorders();
for(Entry<String, BorderData> stringBorderDataEntry : borders.entrySet())
{
String worldName = stringBorderDataEntry.getKey();
BorderData border = stringBorderDataEntry.getValue();
showBorder(worldName, border);
}
}
// Whether circular border markers are available
public static boolean borderEnabled() {
return markApi != null;
}
public static void showBorder(String worldName, BorderData border)
{
if (!borderEnabled()) return;
public static void setup() {
Plugin test = Bukkit.getServer().getPluginManager().getPlugin("dynmap");
if (test == null || !test.isEnabled()) return;
if (!Config.DynmapBorderEnabled()) return;
api = (DynmapAPI) test;
if ((border.getShape() == null) ? Config.ShapeRound() : border.getShape())
showRoundBorder(worldName, border);
else
showSquareBorder(worldName, border);
}
// make sure DynMap version is new enough to include circular markers
try {
Class.forName("org.dynmap.markers.CircleMarker");
private static void showRoundBorder(String worldName, BorderData border)
{
if (squareBorders.containsKey(worldName))
removeBorder(worldName);
// for version 0.35 of DynMap, CircleMarkers had just been introduced and were bugged (center position always 0,0)
if (api.getDynmapVersion().startsWith("0.35-"))
throw new ClassNotFoundException();
} catch (ClassNotFoundException ex) {
Config.logConfig("DynMap is available, but border display is currently disabled: you need DynMap v0.36 or newer.");
return;
} catch (NullPointerException ex) {
Config.logConfig("DynMap is present, but an NPE (type 1) was encountered while trying to integrate. Border display disabled.");
return;
}
World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255;
try {
markApi = api.getMarkerAPI();
if (markApi == null) return;
} catch (NullPointerException ex) {
Config.logConfig("DynMap is present, but an NPE (type 2) was encountered while trying to integrate. Border display disabled.");
return;
}
CircleMarker marker = roundBorders.get(worldName);
if (marker == null)
{
marker = markSet.createCircleMarker("worldborder_"+worldName, Config.DynmapMessage(), false, worldName, border.getX(), y, border.getZ(), border.getRadiusX(), border.getRadiusZ(), true);
marker.setLineStyle(lineWeight, lineOpacity, lineColor);
marker.setFillStyle(0.0, 0x000000);
roundBorders.put(worldName, marker);
}
else
{
marker.setCenter(worldName, border.getX(), y, border.getZ());
marker.setRadius(border.getRadiusX(), border.getRadiusZ());
}
}
// go ahead and show borders for all worlds
showAllBorders();
private static void showSquareBorder(String worldName, BorderData border)
{
if (roundBorders.containsKey(worldName))
removeBorder(worldName);
Config.logConfig("Successfully hooked into DynMap for the ability to display borders.");
}
// corners of the square border
double[] xVals = {border.getX() - border.getRadiusX(), border.getX() + border.getRadiusX()};
double[] zVals = {border.getZ() - border.getRadiusZ(), border.getZ() + border.getRadiusZ()};
public static void renderRegion(String worldName, CoordXZ coord) {
if (!renderEnabled()) return;
AreaMarker marker = squareBorders.get(worldName);
if (marker == null)
{
marker = markSet.createAreaMarker("worldborder_"+worldName, Config.DynmapMessage(), false, worldName, xVals, zVals, true);
marker.setLineStyle(3, 1.0, 0xFF0000);
marker.setFillStyle(0.0, 0x000000);
squareBorders.put(worldName, marker);
}
else
{
marker.setCornerLocations(xVals, zVals);
}
}
World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255;
int x = CoordXZ.regionToBlock(coord.x);
int z = CoordXZ.regionToBlock(coord.z);
api.triggerRenderOfVolume(worldName, x, 0, z, x + 511, y, z + 511);
}
public static void removeAllBorders()
{
if (!borderEnabled()) return;
for(CircleMarker marker : roundBorders.values())
{
marker.deleteMarker();
}
roundBorders.clear();
/*
* Methods for displaying our borders on DynMap's world maps
*/
for(AreaMarker marker : squareBorders.values())
{
marker.deleteMarker();
}
squareBorders.clear();
}
public static void renderChunks(String worldName, List<CoordXZ> coords) {
if (!renderEnabled()) return;
public static void removeBorder(String worldName)
{
if (!borderEnabled()) return;
World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255;
CircleMarker marker = roundBorders.remove(worldName);
if (marker != null)
marker.deleteMarker();
for (CoordXZ coord : coords) {
renderChunk(worldName, coord, y);
}
}
AreaMarker marker2 = squareBorders.remove(worldName);
if (marker2 != null)
marker2.deleteMarker();
}
public static void renderChunk(String worldName, CoordXZ coord, int maxY) {
if (!renderEnabled()) return;
int x = CoordXZ.chunkToBlock(coord.x);
int z = CoordXZ.chunkToBlock(coord.z);
api.triggerRenderOfVolume(worldName, x, 0, z, x + 15, maxY, z + 15);
}
public static void showAllBorders() {
if (!borderEnabled()) return;
// in case any borders are already shown
removeAllBorders();
if (!Config.DynmapBorderEnabled()) {
// don't want to show the marker set in DynMap if our integration is disabled
if (markSet != null)
markSet.deleteMarkerSet();
markSet = null;
return;
}
// make sure the marker set is initialized
markSet = markApi.getMarkerSet("worldborder.markerset");
if (markSet == null)
markSet = markApi.createMarkerSet("worldborder.markerset", "WorldBorder", null, false);
else
markSet.setMarkerSetLabel("WorldBorder");
markSet.setLayerPriority(Config.DynmapPriority());
markSet.setHideByDefault(Config.DynmapHideByDefault());
Map<String, BorderData> borders = Config.getBorders();
for (Entry<String, BorderData> stringBorderDataEntry : borders.entrySet()) {
String worldName = stringBorderDataEntry.getKey();
BorderData border = stringBorderDataEntry.getValue();
showBorder(worldName, border);
}
}
public static void showBorder(String worldName, BorderData border) {
if (!borderEnabled()) return;
if (!Config.DynmapBorderEnabled()) return;
if ((border.getShape() == null) ? Config.ShapeRound() : border.getShape())
showRoundBorder(worldName, border);
else
showSquareBorder(worldName, border);
}
private static void showRoundBorder(String worldName, BorderData border) {
if (squareBorders.containsKey(worldName))
removeBorder(worldName);
World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255;
CircleMarker marker = roundBorders.get(worldName);
if (marker == null) {
marker = markSet.createCircleMarker("worldborder_" + worldName, Config.DynmapMessage(), false, worldName, border.getX(), y, border.getZ(), border.getRadiusX(), border.getRadiusZ(), true);
marker.setLineStyle(lineWeight, lineOpacity, lineColor);
marker.setFillStyle(0.0, 0x000000);
roundBorders.put(worldName, marker);
} else {
marker.setCenter(worldName, border.getX(), y, border.getZ());
marker.setRadius(border.getRadiusX(), border.getRadiusZ());
}
}
private static void showSquareBorder(String worldName, BorderData border) {
if (roundBorders.containsKey(worldName))
removeBorder(worldName);
// corners of the square border
double[] xVals = {border.getX() - border.getRadiusX(), border.getX() + border.getRadiusX()};
double[] zVals = {border.getZ() - border.getRadiusZ(), border.getZ() + border.getRadiusZ()};
AreaMarker marker = squareBorders.get(worldName);
if (marker == null) {
marker = markSet.createAreaMarker("worldborder_" + worldName, Config.DynmapMessage(), false, worldName, xVals, zVals, true);
marker.setLineStyle(3, 1.0, 0xFF0000);
marker.setFillStyle(0.0, 0x000000);
squareBorders.put(worldName, marker);
} else {
marker.setCornerLocations(xVals, zVals);
}
}
public static void removeAllBorders() {
if (!borderEnabled()) return;
for (CircleMarker marker : roundBorders.values()) {
marker.deleteMarker();
}
roundBorders.clear();
for (AreaMarker marker : squareBorders.values()) {
marker.deleteMarker();
}
squareBorders.clear();
}
public static void removeBorder(String worldName) {
if (!borderEnabled()) return;
CircleMarker marker = roundBorders.remove(worldName);
if (marker != null)
marker.deleteMarker();
AreaMarker marker2 = squareBorders.remove(worldName);
if (marker2 != null)
marker2.deleteMarker();
}
}

View File

@ -1,42 +1,36 @@
package com.wimbli.WorldBorder.Events;
import org.bukkit.World;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.World;
/**
* Created by timafh on 04.09.2015.
*/
public class WorldBorderFillFinishedEvent extends Event
{
private static final HandlerList handlers = new HandlerList();
private World world;
private long totalChunks;
public class WorldBorderFillFinishedEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private World world;
private long totalChunks;
public WorldBorderFillFinishedEvent(World world, long totalChunks)
{
this.world = world;
this.totalChunks = totalChunks;
}
public WorldBorderFillFinishedEvent(World world, long totalChunks) {
this.world = world;
this.totalChunks = totalChunks;
}
@Override
public HandlerList getHandlers()
{
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public static HandlerList getHandlerList()
{
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public World getWorld()
{
return world;
}
public World getWorld() {
return world;
}
public long getTotalChunks()
{
return totalChunks;
}
public long getTotalChunks() {
return totalChunks;
}
}

View File

@ -1,36 +1,31 @@
package com.wimbli.WorldBorder.Events;
import com.wimbli.WorldBorder.WorldFillTask;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import com.wimbli.WorldBorder.WorldFillTask;
/**
* Created by Maximvdw on 12.01.2016.
*/
public class WorldBorderFillStartEvent extends Event
{
private static final HandlerList handlers = new HandlerList();
private WorldFillTask fillTask;
public class WorldBorderFillStartEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private WorldFillTask fillTask;
public WorldBorderFillStartEvent(WorldFillTask worldFillTask)
{
this.fillTask = worldFillTask;
}
public WorldBorderFillStartEvent(WorldFillTask worldFillTask) {
this.fillTask = worldFillTask;
}
@Override
public HandlerList getHandlers()
{
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public static HandlerList getHandlerList()
{
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public WorldFillTask getFillTask(){
return this.fillTask;
}
public WorldFillTask getFillTask() {
return this.fillTask;
}
}

View File

@ -1,42 +1,36 @@
package com.wimbli.WorldBorder.Events;
import org.bukkit.World;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.World;
/**
* Created by timafh on 04.09.2015.
*/
public class WorldBorderTrimFinishedEvent extends Event
{
private static final HandlerList handlers = new HandlerList();
private World world;
private long totalChunks;
public class WorldBorderTrimFinishedEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private World world;
private long totalChunks;
public WorldBorderTrimFinishedEvent(World world, long totalChunks)
{
this.world = world;
this.totalChunks = totalChunks;
}
public WorldBorderTrimFinishedEvent(World world, long totalChunks) {
this.world = world;
this.totalChunks = totalChunks;
}
@Override
public HandlerList getHandlers()
{
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public static HandlerList getHandlerList()
{
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public World getWorld()
{
return world;
}
public World getWorld() {
return world;
}
public long getTotalChunks()
{
return totalChunks;
}
public long getTotalChunks() {
return totalChunks;
}
}

View File

@ -1,36 +1,31 @@
package com.wimbli.WorldBorder.Events;
import com.wimbli.WorldBorder.WorldTrimTask;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import com.wimbli.WorldBorder.WorldTrimTask;
/**
* Created by Maximvdw on 12.01.2016.
*/
public class WorldBorderTrimStartEvent extends Event
{
private static final HandlerList handlers = new HandlerList();
private WorldTrimTask trimTask;
public class WorldBorderTrimStartEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private WorldTrimTask trimTask;
public WorldBorderTrimStartEvent(WorldTrimTask trimTask)
{
this.trimTask = trimTask;
}
public WorldBorderTrimStartEvent(WorldTrimTask trimTask) {
this.trimTask = trimTask;
}
@Override
public HandlerList getHandlers()
{
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
public static HandlerList getHandlerList()
{
return handlers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public WorldTrimTask getTrimTask(){
return this.trimTask;
}
public WorldTrimTask getTrimTask() {
return this.trimTask;
}
}

View File

@ -9,27 +9,23 @@ import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
public class MobSpawnListener implements Listener
{
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreatureSpawn(CreatureSpawnEvent event)
{
Location loc = event.getEntity().getLocation();
if (loc == null) return;
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (!border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
{
event.setCancelled(true);
}
}
public class MobSpawnListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onCreatureSpawn(CreatureSpawnEvent event) {
Location loc = event.getEntity().getLocation();
if (loc == null) return;
public void unregister()
{
HandlerList.unregisterAll(this);
}
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (!border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound())) {
event.setCancelled(true);
}
}
public void unregister() {
HandlerList.unregisterAll(this);
}
}

View File

@ -4,6 +4,9 @@
package com.wimbli.WorldBorder.UUID;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
@ -16,134 +19,127 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class UUIDFetcher {
/**
* Date when name changes were introduced
* @see UUIDFetcher#getUUIDAt(String, long)
*/
public static final long FEBRUARY_2015 = 1422748800000L;
private static Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
private static final String UUID_URL = "https://api.mojang.com/users/profiles/minecraft/%s?at=%d";
private static final String NAME_URL = "https://api.mojang.com/user/profiles/%s/names";
private static Map<String, UUID> uuidCache = new HashMap<String, UUID>();
private static Map<UUID, String> nameCache = new HashMap<UUID, String>();
/**
* Date when name changes were introduced
*
* @see UUIDFetcher#getUUIDAt(String, long)
*/
public static final long FEBRUARY_2015 = 1422748800000L;
private static final String UUID_URL = "https://api.mojang.com/users/profiles/minecraft/%s?at=%d";
private static final String NAME_URL = "https://api.mojang.com/user/profiles/%s/names";
private static Gson gson = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDTypeAdapter()).create();
private static Map<String, UUID> uuidCache = new HashMap<String, UUID>();
private static Map<UUID, String> nameCache = new HashMap<UUID, String>();
private static ExecutorService pool = Executors.newCachedThreadPool();
private String name;
private UUID id;
/**
* Fetches the uuid asynchronously and passes it to the consumer
*
* @param name The name
* @param action Do what you want to do with the uuid her
*/
public static void getUUID(String name, Consumer<UUID> action) {
pool.execute(() -> action.accept(getUUID(name)));
}
/**
* Fetches the uuid synchronously and returns it
*
* @param name The name
* @return The uuid
*/
public static UUID getUUID(String name) {
return getUUIDAt(name, System.currentTimeMillis());
}
/**
* Fetches the uuid synchronously for a specified name and time and passes the result to the consumer
*
* @param name The name
* @param timestamp Time when the player had this name in milliseconds
* @param action Do what you want to do with the uuid her
*/
public static void getUUIDAt(String name, long timestamp, Consumer<UUID> action) {
pool.execute(() -> action.accept(getUUIDAt(name, timestamp)));
}
/**
* Fetches the uuid synchronously for a specified name and time
*
* @param name The name
* @param timestamp Time when the player had this name in milliseconds
* @see UUIDFetcher#FEBRUARY_2015
*/
public static UUID getUUIDAt(String name, long timestamp) {
name = name.toLowerCase();
if (uuidCache.containsKey(name)) {
return uuidCache.get(name);
}
try {
HttpURLConnection connection = (HttpURLConnection) new URL(String.format(UUID_URL, name, timestamp/1000)).openConnection();
connection.setReadTimeout(5000);
UUIDFetcher data = gson.fromJson(new BufferedReader(new InputStreamReader(connection.getInputStream())), UUIDFetcher.class);
uuidCache.put(name, data.id);
nameCache.put(data.id, data.name);
private static ExecutorService pool = Executors.newCachedThreadPool();
return data.id;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Fetches the name asynchronously and passes it to the consumer
*
* @param uuid The uuid
* @param action Do what you want to do with the name her
*/
public static void getName(UUID uuid, Consumer<String> action) {
pool.execute(() -> action.accept(getName(uuid)));
}
private String name;
private UUID id;
/**
* Fetches the name synchronously and returns it
*
* @param uuid The uuid
* @return The name
*/
public static String getName(UUID uuid) {
if (nameCache.containsKey(uuid)) {
return nameCache.get(uuid);
}
try {
HttpURLConnection connection = (HttpURLConnection) new URL(String.format(NAME_URL, UUIDTypeAdapter.fromUUID(uuid))).openConnection();
connection.setReadTimeout(5000);
UUIDFetcher[] nameHistory = gson.fromJson(new BufferedReader(new InputStreamReader(connection.getInputStream())), UUIDFetcher[].class);
UUIDFetcher currentNameData = nameHistory[nameHistory.length - 1];
/**
* Fetches the uuid asynchronously and passes it to the consumer
*
* @param name The name
* @param action Do what you want to do with the uuid her
*/
public static void getUUID(String name, Consumer<UUID> action) {
pool.execute(() -> action.accept(getUUID(name)));
}
uuidCache.put(currentNameData.name.toLowerCase(), uuid);
nameCache.put(uuid, currentNameData.name);
/**
* Fetches the uuid synchronously and returns it
*
* @param name The name
* @return The uuid
*/
public static UUID getUUID(String name) {
return getUUIDAt(name, System.currentTimeMillis());
}
return currentNameData.name;
} catch (Exception e) {
e.printStackTrace();
}
/**
* Fetches the uuid synchronously for a specified name and time and passes the result to the consumer
*
* @param name The name
* @param timestamp Time when the player had this name in milliseconds
* @param action Do what you want to do with the uuid her
*/
public static void getUUIDAt(String name, long timestamp, Consumer<UUID> action) {
pool.execute(() -> action.accept(getUUIDAt(name, timestamp)));
}
return null;
}
/**
* Fetches the uuid synchronously for a specified name and time
*
* @param name The name
* @param timestamp Time when the player had this name in milliseconds
* @see UUIDFetcher#FEBRUARY_2015
*/
public static UUID getUUIDAt(String name, long timestamp) {
name = name.toLowerCase();
if (uuidCache.containsKey(name)) {
return uuidCache.get(name);
}
try {
HttpURLConnection connection = (HttpURLConnection) new URL(String.format(UUID_URL, name, timestamp / 1000)).openConnection();
connection.setReadTimeout(5000);
UUIDFetcher data = gson.fromJson(new BufferedReader(new InputStreamReader(connection.getInputStream())), UUIDFetcher.class);
public static Map<UUID, String> getNameList(ArrayList<UUID> uuids) {
Map<UUID, String> uuidStringMap = new HashMap<>();
for (UUID uuid: uuids)
{
uuidStringMap.put(uuid, getName(uuid));
}
return uuidStringMap;
}
uuidCache.put(name, data.id);
nameCache.put(data.id, data.name);
return data.id;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Fetches the name asynchronously and passes it to the consumer
*
* @param uuid The uuid
* @param action Do what you want to do with the name her
*/
public static void getName(UUID uuid, Consumer<String> action) {
pool.execute(() -> action.accept(getName(uuid)));
}
/**
* Fetches the name synchronously and returns it
*
* @param uuid The uuid
* @return The name
*/
public static String getName(UUID uuid) {
if (nameCache.containsKey(uuid)) {
return nameCache.get(uuid);
}
try {
HttpURLConnection connection = (HttpURLConnection) new URL(String.format(NAME_URL, UUIDTypeAdapter.fromUUID(uuid))).openConnection();
connection.setReadTimeout(5000);
UUIDFetcher[] nameHistory = gson.fromJson(new BufferedReader(new InputStreamReader(connection.getInputStream())), UUIDFetcher[].class);
UUIDFetcher currentNameData = nameHistory[nameHistory.length - 1];
uuidCache.put(currentNameData.name.toLowerCase(), uuid);
nameCache.put(uuid, currentNameData.name);
return currentNameData.name;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Map<UUID, String> getNameList(ArrayList<UUID> uuids) {
Map<UUID, String> uuidStringMap = new HashMap<>();
for (UUID uuid : uuids) {
uuidStringMap.put(uuid, getName(uuid));
}
return uuidStringMap;
}
}

View File

@ -4,29 +4,29 @@
package com.wimbli.WorldBorder.UUID;
import java.io.IOException;
import java.util.UUID;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.UUID;
public class UUIDTypeAdapter extends TypeAdapter<UUID> {
public void write(JsonWriter out, UUID value) throws IOException {
out.value(fromUUID(value));
}
public static String fromUUID(UUID value) {
return value.toString().replace("-", "");
}
public UUID read(JsonReader in) throws IOException {
return fromString(in.nextString());
}
public static UUID fromString(String input) {
return UUID.fromString(input.replaceFirst(
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
}
public static String fromUUID(UUID value) {
return value.toString().replace("-", "");
}
public void write(JsonWriter out, UUID value) throws IOException {
out.value(fromUUID(value));
}
public static UUID fromString(String input) {
return UUID.fromString(input.replaceFirst(
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
}
public UUID read(JsonReader in) throws IOException {
return fromString(in.nextString());
}
}

View File

@ -1,213 +1,184 @@
package com.wimbli.WorldBorder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.bukkit.command.*;
import com.wimbli.WorldBorder.cmd.*;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.cmd.*;
import java.util.*;
public class WBCommand implements CommandExecutor
{
// map of all sub-commands with the command name (string) for quick reference
public Map<String, WBCmd> subCommands = new LinkedHashMap<String, WBCmd>();
// ref. list of the commands which can have a world name in front of the command itself (ex. /wb _world_ radius 100)
private Set<String> subCommandsWithWorldNames = new LinkedHashSet<String>();
// constructor
public WBCommand ()
{
addCmd(new CmdHelp()); // 1 example
addCmd(new CmdSet()); // 4 examples for player, 3 for console
addCmd(new CmdSetcorners()); // 1
addCmd(new CmdRadius()); // 1
addCmd(new CmdList()); // 1
//----- 8 per page of examples
addCmd(new CmdShape()); // 2
addCmd(new CmdClear()); // 2
addCmd(new CmdFill()); // 1
addCmd(new CmdTrim()); // 1
addCmd(new CmdBypass()); // 1
addCmd(new CmdBypasslist()); // 1
//-----
addCmd(new CmdKnockback()); // 1
addCmd(new CmdWrap()); // 1
addCmd(new CmdWhoosh()); // 1
addCmd(new CmdGetmsg()); // 1
addCmd(new CmdSetmsg()); // 1
addCmd(new CmdWshape()); // 3
//-----
addCmd(new CmdPreventPlace()); // 1
addCmd(new CmdPreventSpawn()); // 1
addCmd(new CmdDelay()); // 1
addCmd(new CmdDynmap()); // 1
addCmd(new CmdDynmapmsg()); // 1
addCmd(new CmdRemount()); // 1
addCmd(new CmdFillautosave()); // 1
addCmd(new CmdPortal()); // 1
//-----
addCmd(new CmdDenypearl()); // 1
addCmd(new CmdReload()); // 1
addCmd(new CmdDebug()); // 1
// this is the default command, which shows command example pages; should be last just in case
addCmd(new CmdCommands());
}
public class WBCommand implements CommandExecutor {
// map of all sub-commands with the command name (string) for quick reference
public Map<String, WBCmd> subCommands = new LinkedHashMap<String, WBCmd>();
// ref. list of the commands which can have a world name in front of the command itself (ex. /wb _world_ radius 100)
private Set<String> subCommandsWithWorldNames = new LinkedHashSet<String>();
private boolean wasWorldQuotation = false;
private void addCmd(WBCmd cmd)
{
subCommands.put(cmd.name, cmd);
if (cmd.hasWorldNameInput)
subCommandsWithWorldNames.add(cmd.name);
}
// constructor
public WBCommand() {
addCmd(new CmdHelp()); // 1 example
addCmd(new CmdSet()); // 4 examples for player, 3 for console
addCmd(new CmdSetcorners()); // 1
addCmd(new CmdRadius()); // 1
addCmd(new CmdList()); // 1
//----- 8 per page of examples
addCmd(new CmdShape()); // 2
addCmd(new CmdClear()); // 2
addCmd(new CmdFill()); // 1
addCmd(new CmdTrim()); // 1
addCmd(new CmdBypass()); // 1
addCmd(new CmdBypasslist()); // 1
//-----
addCmd(new CmdKnockback()); // 1
addCmd(new CmdWrap()); // 1
addCmd(new CmdWhoosh()); // 1
addCmd(new CmdGetmsg()); // 1
addCmd(new CmdSetmsg()); // 1
addCmd(new CmdWshape()); // 3
//-----
addCmd(new CmdPreventPlace()); // 1
addCmd(new CmdPreventSpawn()); // 1
addCmd(new CmdDelay()); // 1
addCmd(new CmdDynmap()); // 1
addCmd(new CmdDynmapmsg()); // 1
addCmd(new CmdRemount()); // 1
addCmd(new CmdFillautosave()); // 1
addCmd(new CmdPortal()); // 1
//-----
addCmd(new CmdDenypearl()); // 1
addCmd(new CmdReload()); // 1
addCmd(new CmdDebug()); // 1
// this is the default command, which shows command example pages; should be last just in case
addCmd(new CmdCommands());
}
private void addCmd(WBCmd cmd) {
subCommands.put(cmd.name, cmd);
if (cmd.hasWorldNameInput)
subCommandsWithWorldNames.add(cmd.name);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] split)
{
Player player = (sender instanceof Player) ? (Player)sender : null;
public boolean onCommand(CommandSender sender, Command command, String label, String[] split) {
Player player = (sender instanceof Player) ? (Player) sender : null;
// if world name is passed inside quotation marks, handle that, and get List<String> instead of String[]
List<String> params = concatenateQuotedWorldName(split);
// if world name is passed inside quotation marks, handle that, and get List<String> instead of String[]
List<String> params = concatenateQuotedWorldName(split);
String worldName = null;
// is second parameter the command and first parameter a world name? definitely world name if it was in quotation marks
if (wasWorldQuotation || (params.size() > 1 && !subCommands.containsKey(params.get(0)) && subCommandsWithWorldNames.contains(params.get(1))))
worldName = params.get(0);
String worldName = null;
// is second parameter the command and first parameter a world name? definitely world name if it was in quotation marks
if (wasWorldQuotation || (params.size() > 1 && !subCommands.containsKey(params.get(0)) && subCommandsWithWorldNames.contains(params.get(1))))
worldName = params.get(0);
// no command specified? show command examples / help
if (params.isEmpty())
params.add(0, "commands");
// no command specified? show command examples / help
if (params.isEmpty())
params.add(0, "commands");
// determined the command name
String cmdName = (worldName == null) ? params.get(0).toLowerCase() : params.get(1).toLowerCase();
// determined the command name
String cmdName = (worldName == null) ? params.get(0).toLowerCase() : params.get(1).toLowerCase();
// remove command name and (if there) world name from front of param array
params.remove(0);
if (worldName != null)
params.remove(0);
// remove command name and (if there) world name from front of param array
params.remove(0);
if (worldName != null)
params.remove(0);
// make sure command is recognized, default to showing command examples / help if not; also check for specified page number
if (!subCommands.containsKey(cmdName))
{
int page = (player == null) ? 0 : 1;
try
{
page = Integer.parseInt(cmdName);
}
catch(NumberFormatException ignored)
{
sender.sendMessage(WBCmd.C_ERR + "Command not recognized. Showing command list.");
}
cmdName = "commands";
params.add(0, Integer.toString(page));
}
// make sure command is recognized, default to showing command examples / help if not; also check for specified page number
if (!subCommands.containsKey(cmdName)) {
int page = (player == null) ? 0 : 1;
try {
page = Integer.parseInt(cmdName);
} catch (NumberFormatException ignored) {
sender.sendMessage(WBCmd.C_ERR + "Command not recognized. Showing command list.");
}
cmdName = "commands";
params.add(0, Integer.toString(page));
}
WBCmd subCommand = subCommands.get(cmdName);
WBCmd subCommand = subCommands.get(cmdName);
// check permission
if (!Config.HasPermission(player, subCommand.permission))
return true;
// check permission
if (!Config.HasPermission(player, subCommand.permission))
return true;
// if command requires world name when run by console, make sure that's in place
if (player == null && subCommand.hasWorldNameInput && subCommand.consoleRequiresWorldName && worldName == null)
{
sender.sendMessage(WBCmd.C_ERR + "This command requires a world to be specified if run by the console.");
subCommand.sendCmdHelp(sender);
return true;
}
// if command requires world name when run by console, make sure that's in place
if (player == null && subCommand.hasWorldNameInput && subCommand.consoleRequiresWorldName && worldName == null) {
sender.sendMessage(WBCmd.C_ERR + "This command requires a world to be specified if run by the console.");
subCommand.sendCmdHelp(sender);
return true;
}
// make sure valid number of parameters has been provided
if (params.size() < subCommand.minParams || params.size() > subCommand.maxParams)
{
if (subCommand.maxParams == 0)
sender.sendMessage(WBCmd.C_ERR + "This command does not accept any parameters.");
else
sender.sendMessage(WBCmd.C_ERR + "You have not provided a valid number of parameters.");
subCommand.sendCmdHelp(sender);
return true;
}
// make sure valid number of parameters has been provided
if (params.size() < subCommand.minParams || params.size() > subCommand.maxParams) {
if (subCommand.maxParams == 0)
sender.sendMessage(WBCmd.C_ERR + "This command does not accept any parameters.");
else
sender.sendMessage(WBCmd.C_ERR + "You have not provided a valid number of parameters.");
subCommand.sendCmdHelp(sender);
return true;
}
// execute command
subCommand.execute(sender, player, params, worldName);
// execute command
subCommand.execute(sender, player, params, worldName);
return true;
}
return true;
}
// if world name is surrounded by quotation marks, combine it down and flag wasWorldQuotation if it's first param.
// also return List<String> instead of input primitive String[]
private List<String> concatenateQuotedWorldName(String[] split) {
wasWorldQuotation = false;
List<String> args = new ArrayList<String>(Arrays.asList(split));
private boolean wasWorldQuotation = false;
int startIndex = -1;
for (int i = 0; i < args.size(); i++) {
if (args.get(i).startsWith("\"")) {
startIndex = i;
break;
}
}
if (startIndex == -1)
return args;
// if world name is surrounded by quotation marks, combine it down and flag wasWorldQuotation if it's first param.
// also return List<String> instead of input primitive String[]
private List<String> concatenateQuotedWorldName(String[] split)
{
wasWorldQuotation = false;
List<String> args = new ArrayList<String>(Arrays.asList(split));
if (args.get(startIndex).endsWith("\"")) {
args.set(startIndex, args.get(startIndex).substring(1, args.get(startIndex).length() - 1));
if (startIndex == 0)
wasWorldQuotation = true;
} else {
List<String> concat = new ArrayList<String>(args);
Iterator<String> concatI = concat.iterator();
int startIndex = -1;
for (int i = 0; i < args.size(); i++)
{
if (args.get(i).startsWith("\""))
{
startIndex = i;
break;
}
}
if (startIndex == -1)
return args;
// skip past any parameters in front of the one we're starting on
for (int i = 1; i < startIndex + 1; i++) {
concatI.next();
}
if (args.get(startIndex).endsWith("\""))
{
args.set(startIndex, args.get(startIndex).substring(1, args.get(startIndex).length() - 1));
if (startIndex == 0)
wasWorldQuotation = true;
}
else
{
List<String> concat = new ArrayList<String>(args);
Iterator<String> concatI = concat.iterator();
StringBuilder quote = new StringBuilder(concatI.next());
while (concatI.hasNext()) {
String next = concatI.next();
concatI.remove();
quote.append(" ");
quote.append(next);
if (next.endsWith("\"")) {
concat.set(startIndex, quote.substring(1, quote.length() - 1));
args = concat;
if (startIndex == 0)
wasWorldQuotation = true;
break;
}
}
}
return args;
}
// skip past any parameters in front of the one we're starting on
for (int i = 1; i < startIndex + 1; i++)
{
concatI.next();
}
StringBuilder quote = new StringBuilder(concatI.next());
while (concatI.hasNext())
{
String next = concatI.next();
concatI.remove();
quote.append(" ");
quote.append(next);
if (next.endsWith("\""))
{
concat.set(startIndex, quote.substring(1, quote.length() - 1));
args = concat;
if (startIndex == 0)
wasWorldQuotation = true;
break;
}
}
}
return args;
}
public Set<String> getCommandNames()
{
// using TreeSet to sort alphabetically
Set<String> commands = new TreeSet<>(subCommands.keySet());
// removing default "commands" command as it's not normally shown or run like other commands
commands.remove("commands");
return commands;
}
public Set<String> getCommandNames() {
// using TreeSet to sort alphabetically
Set<String> commands = new TreeSet<>(subCommands.keySet());
// removing default "commands" command as it's not normally shown or run like other commands
commands.remove("commands");
return commands;
}
}

View File

@ -1,56 +1,50 @@
package com.wimbli.WorldBorder;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.Location;
public class WBListener implements Listener
{
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event)
{
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
public class WBListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event) {
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
if (Config.Debug())
Config.log("Teleport cause: " + event.getCause().toString());
if (Config.Debug())
Config.log("Teleport cause: " + event.getCause().toString());
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, true);
if (newLoc != null)
{
if(event.getCause() == PlayerTeleportEvent.TeleportCause.ENDER_PEARL && Config.getDenyEnderpearl())
{
event.setCancelled(true);
return;
}
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, true);
if (newLoc != null) {
if (event.getCause() == PlayerTeleportEvent.TeleportCause.ENDER_PEARL && Config.getDenyEnderpearl()) {
event.setCancelled(true);
return;
}
event.setTo(newLoc);
}
}
event.setTo(newLoc);
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerPortal(PlayerPortalEvent event)
{
// if knockback is set to 0, or portal redirection is disabled, simply return
if (Config.KnockBack() == 0.0 || !Config.portalRedirection())
return;
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerPortal(PlayerPortalEvent event) {
// if knockback is set to 0, or portal redirection is disabled, simply return
if (Config.KnockBack() == 0.0 || !Config.portalRedirection())
return;
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, false);
if (newLoc != null)
event.setTo(newLoc);
}
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, false);
if (newLoc != null)
event.setTo(newLoc);
}
@EventHandler(priority = EventPriority.MONITOR)
public void onChunkLoad(ChunkLoadEvent event)
{
@EventHandler(priority = EventPriority.MONITOR)
public void onChunkLoad(ChunkLoadEvent event) {
/* // tested, found to spam pretty rapidly as client repeatedly requests the same chunks since they're not being sent
// definitely too spammy at only 16 blocks outside border
// potentially useful at standard 208 block padding as it was triggering only occasionally while trying to get out all along edge of round border, though sometimes up to 3 triggers within a second corresponding to 3 adjacent chunks
@ -64,30 +58,29 @@ public class WBListener implements Listener
Config.logWarn("New chunk generation has been prevented at X " + chunk.getX() + ", Z " + chunk.getZ());
}
*/
// make sure our border monitoring task is still running like it should
if (Config.isBorderTimerRunning()) return;
// make sure our border monitoring task is still running like it should
if (Config.isBorderTimerRunning()) return;
Config.logWarn("Border-checking task was not running! Something on your server apparently killed it. It will now be restarted.");
Config.StartBorderTimer();
}
Config.logWarn("Border-checking task was not running! Something on your server apparently killed it. It will now be restarted.");
Config.StartBorderTimer();
}
/*
* Check if there is a fill task running, and if yes, if it's for the
* world that the unload event refers to, set "force loaded" flag off
* and track if chunk was somehow on unload prevention list
*/
@EventHandler
public void onChunkUnload(ChunkUnloadEvent e)
{
if (Config.fillTask == null)
return;
/*
* Check if there is a fill task running, and if yes, if it's for the
* world that the unload event refers to, set "force loaded" flag off
* and track if chunk was somehow on unload prevention list
*/
@EventHandler
public void onChunkUnload(ChunkUnloadEvent e) {
if (Config.fillTask == null)
return;
Chunk chunk = e.getChunk();
if (e.getWorld() != Config.fillTask.getWorld())
return;
Chunk chunk = e.getChunk();
if (e.getWorld() != Config.fillTask.getWorld())
return;
// just to be on the safe side, in case it's still set at this point somehow
chunk.setForceLoaded(false);
}
// just to be on the safe side, in case it's still set at this point somehow
chunk.setForceLoaded(false);
}
}

View File

@ -4,81 +4,74 @@ import org.bukkit.Location;
import org.bukkit.plugin.java.JavaPlugin;
public class WorldBorder extends JavaPlugin
{
public static volatile WorldBorder plugin = null;
public static volatile WBCommand wbCommand = null;
private BlockPlaceListener blockPlaceListener = null;
private MobSpawnListener mobSpawnListener = null;
public class WorldBorder extends JavaPlugin {
public static volatile WorldBorder plugin = null;
public static volatile WBCommand wbCommand = null;
private BlockPlaceListener blockPlaceListener = null;
private MobSpawnListener mobSpawnListener = null;
@Override
public void onEnable()
{
if (plugin == null)
plugin = this;
if (wbCommand == null)
wbCommand = new WBCommand();
@Override
public void onEnable() {
if (plugin == null)
plugin = this;
if (wbCommand == null)
wbCommand = new WBCommand();
// Load (or create new) config file
Config.load(this, false);
// Load (or create new) config file
Config.load(this, false);
// our one real command, though it does also have aliases "wb" and "worldborder"
getCommand("wborder").setExecutor(wbCommand);
// our one real command, though it does also have aliases "wb" and "worldborder"
getCommand("wborder").setExecutor(wbCommand);
// keep an eye on teleports, to redirect them to a spot inside the border if necessary
getServer().getPluginManager().registerEvents(new WBListener(), this);
if (Config.preventBlockPlace())
enableBlockPlaceListener(true);
// keep an eye on teleports, to redirect them to a spot inside the border if necessary
getServer().getPluginManager().registerEvents(new WBListener(), this);
if (Config.preventMobSpawn())
enableMobSpawnListener(true);
if (Config.preventBlockPlace())
enableBlockPlaceListener(true);
// integrate with DynMap if it's available
DynMapFeatures.setup();
if (Config.preventMobSpawn())
enableMobSpawnListener(true);
// Well I for one find this info useful, so...
Location spawn = getServer().getWorlds().get(0).getSpawnLocation();
Config.log("For reference, the main world's spawn location is at X: " + Config.coord.format(spawn.getX()) + " Y: " + Config.coord.format(spawn.getY()) + " Z: " + Config.coord.format(spawn.getZ()));
}
// integrate with DynMap if it's available
DynMapFeatures.setup();
@Override
public void onDisable()
{
DynMapFeatures.removeAllBorders();
Config.StopBorderTimer();
Config.StoreFillTask();
Config.StopFillTask();
}
// Well I for one find this info useful, so...
Location spawn = getServer().getWorlds().get(0).getSpawnLocation();
Config.log("For reference, the main world's spawn location is at X: " + Config.coord.format(spawn.getX()) + " Y: " + Config.coord.format(spawn.getY()) + " Z: " + Config.coord.format(spawn.getZ()));
}
// for other plugins to hook into
public BorderData getWorldBorder(String worldName)
{
return Config.Border(worldName);
}
@Override
public void onDisable() {
DynMapFeatures.removeAllBorders();
Config.StopBorderTimer();
Config.StoreFillTask();
Config.StopFillTask();
}
/**
* @deprecated Replaced by {@link #getWorldBorder(String worldName)};
* this method name starts with an uppercase letter, which it shouldn't
*/
public BorderData GetWorldBorder(String worldName)
{
return getWorldBorder(worldName);
}
// for other plugins to hook into
public BorderData getWorldBorder(String worldName) {
return Config.Border(worldName);
}
public void enableBlockPlaceListener(boolean enable)
{
if (enable)
getServer().getPluginManager().registerEvents(this.blockPlaceListener = new BlockPlaceListener(), this);
else if (blockPlaceListener != null)
blockPlaceListener.unregister();
}
/**
* @deprecated Replaced by {@link #getWorldBorder(String worldName)};
* this method name starts with an uppercase letter, which it shouldn't
*/
public BorderData GetWorldBorder(String worldName) {
return getWorldBorder(worldName);
}
public void enableMobSpawnListener(boolean enable)
{
if (enable)
getServer().getPluginManager().registerEvents(this.mobSpawnListener = new MobSpawnListener(), this);
else if (mobSpawnListener != null)
mobSpawnListener.unregister();
}
public void enableBlockPlaceListener(boolean enable) {
if (enable)
getServer().getPluginManager().registerEvents(this.blockPlaceListener = new BlockPlaceListener(), this);
else if (blockPlaceListener != null)
blockPlaceListener.unregister();
}
public void enableMobSpawnListener(boolean enable) {
if (enable)
getServer().getPluginManager().registerEvents(this.mobSpawnListener = new MobSpawnListener(), this);
else if (mobSpawnListener != null)
mobSpawnListener.unregister();
}
}

View File

@ -1,311 +1,264 @@
package com.wimbli.WorldBorder;
import org.bukkit.World;
import org.bukkit.entity.Player;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.nio.IntBuffer;
import java.util.List;
import java.util.Map;
import org.bukkit.entity.Player;
import org.bukkit.World;
import java.util.*;
// image output stuff, for debugging method at bottom of this file
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
// by the way, this region file handler was created based on the divulged region file format: http://mojang.com/2011/02/16/minecraft-save-file-format-in-beta-1-3/
public class WorldFileData
{
private transient World world;
private transient File regionFolder = null;
private transient File[] regionFiles = null;
private transient Player notifyPlayer = null;
private transient Map<CoordXZ, List<Boolean>> regionChunkExistence = Collections.synchronizedMap(new HashMap<CoordXZ, List<Boolean>>());
public class WorldFileData {
private transient World world;
private transient File regionFolder = null;
private transient File[] regionFiles = null;
private transient Player notifyPlayer = null;
private transient Map<CoordXZ, List<Boolean>> regionChunkExistence = Collections.synchronizedMap(new HashMap<CoordXZ, List<Boolean>>());
// Use this static method to create a new instance of this class. If null is returned, there was a problem so any process relying on this should be cancelled.
public static WorldFileData create(World world, Player notifyPlayer)
{
WorldFileData newData = new WorldFileData(world, notifyPlayer);
// the constructor is private; use create() method above to create an instance of this class.
private WorldFileData(World world, Player notifyPlayer) {
this.world = world;
this.notifyPlayer = notifyPlayer;
}
newData.regionFolder = new File(newData.world.getWorldFolder(), "region");
if (!newData.regionFolder.exists() || !newData.regionFolder.isDirectory())
{
// check for region folder inside a DIM* folder (DIM-1 for nether, DIM1 for end, DIMwhatever for custom world types)
File[] possibleDimFolders = newData.world.getWorldFolder().listFiles(new DimFolderFileFilter());
for (File possibleDimFolder : possibleDimFolders)
{
File possible = new File(newData.world.getWorldFolder(), possibleDimFolder.getName() + File.separator + "region");
if (possible.exists() && possible.isDirectory())
{
newData.regionFolder = possible;
break;
}
}
if (!newData.regionFolder.exists() || !newData.regionFolder.isDirectory())
{
newData.sendMessage("Could not validate folder for world's region files. Looked in "+newData.world.getWorldFolder().getPath()+" for valid DIM* folder with a region folder in it.");
return null;
}
}
// Use this static method to create a new instance of this class. If null is returned, there was a problem so any process relying on this should be cancelled.
public static WorldFileData create(World world, Player notifyPlayer) {
WorldFileData newData = new WorldFileData(world, notifyPlayer);
// Accepted region file formats: MCR is from late beta versions through 1.1, MCA is from 1.2+
newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCA"));
if (newData.regionFiles == null || newData.regionFiles.length == 0)
{
newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCR"));
if (newData.regionFiles == null || newData.regionFiles.length == 0)
{
newData.sendMessage("Could not find any region files. Looked in: "+newData.regionFolder.getPath());
return null;
}
}
newData.regionFolder = new File(newData.world.getWorldFolder(), "region");
if (!newData.regionFolder.exists() || !newData.regionFolder.isDirectory()) {
// check for region folder inside a DIM* folder (DIM-1 for nether, DIM1 for end, DIMwhatever for custom world types)
File[] possibleDimFolders = newData.world.getWorldFolder().listFiles(new DimFolderFileFilter());
for (File possibleDimFolder : possibleDimFolders) {
File possible = new File(newData.world.getWorldFolder(), possibleDimFolder.getName() + File.separator + "region");
if (possible.exists() && possible.isDirectory()) {
newData.regionFolder = possible;
break;
}
}
if (!newData.regionFolder.exists() || !newData.regionFolder.isDirectory()) {
newData.sendMessage("Could not validate folder for world's region files. Looked in " + newData.world.getWorldFolder().getPath() + " for valid DIM* folder with a region folder in it.");
return null;
}
}
return newData;
}
// Accepted region file formats: MCR is from late beta versions through 1.1, MCA is from 1.2+
newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCA"));
if (newData.regionFiles == null || newData.regionFiles.length == 0) {
newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCR"));
if (newData.regionFiles == null || newData.regionFiles.length == 0) {
newData.sendMessage("Could not find any region files. Looked in: " + newData.regionFolder.getPath());
return null;
}
}
// the constructor is private; use create() method above to create an instance of this class.
private WorldFileData(World world, Player notifyPlayer)
{
this.world = world;
this.notifyPlayer = notifyPlayer;
}
return newData;
}
// number of region files this world has
public int regionFileCount() {
return regionFiles.length;
}
// folder where world's region files are located
public File regionFolder() {
return regionFolder;
}
// return entire list of region files
public File[] regionFiles() {
return regionFiles.clone();
}
// return a region file by index
public File regionFile(int index) {
if (regionFiles.length < index)
return null;
return regionFiles[index];
}
// get the X and Z world coordinates of the region from the filename
public CoordXZ regionFileCoordinates(int index) {
File regionFile = this.regionFile(index);
String[] coords = regionFile.getName().split("\\.");
int x, z;
try {
x = Integer.parseInt(coords[1]);
z = Integer.parseInt(coords[2]);
return new CoordXZ(x, z);
} catch (Exception ex) {
sendMessage("Error! Region file found with abnormal name: " + regionFile.getName());
return null;
}
}
// number of region files this world has
public int regionFileCount()
{
return regionFiles.length;
}
// folder where world's region files are located
public File regionFolder()
{
return regionFolder;
}
// return entire list of region files
public File[] regionFiles()
{
return regionFiles.clone();
}
// return a region file by index
public File regionFile(int index)
{
if (regionFiles.length < index)
return null;
return regionFiles[index];
}
// get the X and Z world coordinates of the region from the filename
public CoordXZ regionFileCoordinates(int index)
{
File regionFile = this.regionFile(index);
String[] coords = regionFile.getName().split("\\.");
int x, z;
try
{
x = Integer.parseInt(coords[1]);
z = Integer.parseInt(coords[2]);
return new CoordXZ (x, z);
}
catch(Exception ex)
{
sendMessage("Error! Region file found with abnormal name: "+regionFile.getName());
return null;
}
}
// Find out if the chunk at the given coordinates exists.
public boolean doesChunkExist(int x, int z)
{
CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z));
List<Boolean> regionChunks = this.getRegionData(region);
// Find out if the chunk at the given coordinates exists.
public boolean doesChunkExist(int x, int z) {
CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z));
List<Boolean> regionChunks = this.getRegionData(region);
// Bukkit.getLogger().info("x: "+x+" z: "+z+" offset: "+coordToRegionOffset(x, z));
return regionChunks.get(coordToRegionOffset(x, z));
}
return regionChunks.get(coordToRegionOffset(x, z));
}
// Find out if the chunk at the given coordinates has been fully generated.
// Minecraft only fully generates a chunk when adjacent chunks are also loaded.
public boolean isChunkFullyGenerated(int x, int z)
{ // if all adjacent chunks exist, it should be a safe enough bet that this one is fully generated
// For 1.13+, due to world gen changes, this is now effectively a 3 chunk radius requirement vs a 1 chunk radius
for (int xx = x-3; xx <= x+3; xx++)
{
for (int zz = z-3; zz <= z+3; zz++)
{
if (!doesChunkExist(xx, zz))
return false;
}
}
return true;
}
// Find out if the chunk at the given coordinates has been fully generated.
// Minecraft only fully generates a chunk when adjacent chunks are also loaded.
public boolean isChunkFullyGenerated(int x, int z) { // if all adjacent chunks exist, it should be a safe enough bet that this one is fully generated
// For 1.13+, due to world gen changes, this is now effectively a 3 chunk radius requirement vs a 1 chunk radius
for (int xx = x - 3; xx <= x + 3; xx++) {
for (int zz = z - 3; zz <= z + 3; zz++) {
if (!doesChunkExist(xx, zz))
return false;
}
}
return true;
}
// Method to let us know a chunk has been generated, to update our region map.
public void chunkExistsNow(int x, int z)
{
CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z));
List<Boolean> regionChunks = this.getRegionData(region);
regionChunks.set(coordToRegionOffset(x, z), true);
}
// Method to let us know a chunk has been generated, to update our region map.
public void chunkExistsNow(int x, int z) {
CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z));
List<Boolean> regionChunks = this.getRegionData(region);
regionChunks.set(coordToRegionOffset(x, z), true);
}
// region is 32 * 32 chunks; chunk pointers are stored in region file at position: x + z*32 (32 * 32 chunks = 1024)
// input x and z values can be world-based chunk coordinates or local-to-region chunk coordinates either one
private int coordToRegionOffset(int x, int z) {
// "%" modulus is used to convert potential world coordinates to definitely be local region coordinates
x = x % 32;
z = z % 32;
// similarly, for local coordinates, we need to wrap negative values around
if (x < 0) x += 32;
if (z < 0) z += 32;
// return offset position for the now definitely local x and z values
return (x + (z * 32));
}
// region is 32 * 32 chunks; chunk pointers are stored in region file at position: x + z*32 (32 * 32 chunks = 1024)
// input x and z values can be world-based chunk coordinates or local-to-region chunk coordinates either one
private int coordToRegionOffset(int x, int z)
{
// "%" modulus is used to convert potential world coordinates to definitely be local region coordinates
x = x % 32;
z = z % 32;
// similarly, for local coordinates, we need to wrap negative values around
if (x < 0) x += 32;
if (z < 0) z += 32;
// return offset position for the now definitely local x and z values
return (x + (z * 32));
}
private List<Boolean> getRegionData(CoordXZ region) {
List<Boolean> data = regionChunkExistence.get(region);
if (data != null)
return data;
private List<Boolean> getRegionData(CoordXZ region)
{
List<Boolean> data = regionChunkExistence.get(region);
if (data != null)
return data;
// data for the specified region isn't loaded yet, so init it as empty and try to find the file and load the data
data = new ArrayList<Boolean>(1024);
for (int i = 0; i < 1024; i++) {
data.add(Boolean.FALSE);
}
// data for the specified region isn't loaded yet, so init it as empty and try to find the file and load the data
data = new ArrayList<Boolean>(1024);
for (int i = 0; i < 1024; i++)
{
data.add(Boolean.FALSE);
}
for (int i = 0; i < regionFiles.length; i++) {
CoordXZ coord = regionFileCoordinates(i);
// is this region file the one we're looking for?
if (!coord.equals(region))
continue;
for (int i = 0; i < regionFiles.length; i++)
{
CoordXZ coord = regionFileCoordinates(i);
// is this region file the one we're looking for?
if ( ! coord.equals(region))
continue;
try {
RandomAccessFile regionData = new RandomAccessFile(this.regionFile(i), "r");
try
{
RandomAccessFile regionData = new RandomAccessFile(this.regionFile(i), "r");
// Use of ByteBuffer+IntBuffer for reading file headers to improve performance, as suggested by aikar, reference:
// https://github.com/PaperMC/Paper/blob/b62dfa0bf95ac27ba0fbb3fae18c064e4bb61d50/Spigot-Server-Patches/0086-Reduce-IO-ops-opening-a-new-region-file.patch
ByteBuffer header = ByteBuffer.allocate(8192);
while (header.hasRemaining()) {
if (regionData.getChannel().read(header) == -1)
throw new EOFException();
}
header.clear();
IntBuffer headerAsInts = header.asIntBuffer();
// Use of ByteBuffer+IntBuffer for reading file headers to improve performance, as suggested by aikar, reference:
// https://github.com/PaperMC/Paper/blob/b62dfa0bf95ac27ba0fbb3fae18c064e4bb61d50/Spigot-Server-Patches/0086-Reduce-IO-ops-opening-a-new-region-file.patch
ByteBuffer header = ByteBuffer.allocate(8192);
while (header.hasRemaining())
{
if (regionData.getChannel().read(header) == -1)
throw new EOFException();
}
header.clear();
IntBuffer headerAsInts = header.asIntBuffer();
// first 4096 bytes of region file consists of 4-byte int pointers to chunk data in the file (32*32 chunks = 1024; 1024 chunks * 4 bytes each = 4096)
for (int j = 0; j < 1024; j++)
{
// if chunk pointer data is 0, chunk doesn't exist yet; otherwise, it does
if (headerAsInts.get() != 0)
data.set(j, true);
}
// Read timestamps
for (int j = 0; j < 1024; j++)
{
// if timestamp is zero, it is protochunk (ignore it)
if ((headerAsInts.get() == 0) && data.get(j))
data.set(j, false);
}
regionData.close();
}
catch (FileNotFoundException ex)
{
sendMessage("Error! Could not open region file to find generated chunks: "+this.regionFile(i).getName());
}
catch (IOException ex)
{
sendMessage("Error! Could not read region file to find generated chunks: "+this.regionFile(i).getName());
}
}
regionChunkExistence.put(region, data);
// first 4096 bytes of region file consists of 4-byte int pointers to chunk data in the file (32*32 chunks = 1024; 1024 chunks * 4 bytes each = 4096)
for (int j = 0; j < 1024; j++) {
// if chunk pointer data is 0, chunk doesn't exist yet; otherwise, it does
if (headerAsInts.get() != 0)
data.set(j, true);
}
// Read timestamps
for (int j = 0; j < 1024; j++) {
// if timestamp is zero, it is protochunk (ignore it)
if ((headerAsInts.get() == 0) && data.get(j))
data.set(j, false);
}
regionData.close();
} catch (FileNotFoundException ex) {
sendMessage("Error! Could not open region file to find generated chunks: " + this.regionFile(i).getName());
} catch (IOException ex) {
sendMessage("Error! Could not read region file to find generated chunks: " + this.regionFile(i).getName());
}
}
regionChunkExistence.put(region, data);
// testImage(region, data);
return data;
}
return data;
}
// send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text)
{
Config.log("[WorldData] " + text);
if (notifyPlayer != null && notifyPlayer.isOnline())
notifyPlayer.sendMessage("[WorldData] " + text);
}
// send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text) {
Config.log("[WorldData] " + text);
if (notifyPlayer != null && notifyPlayer.isOnline())
notifyPlayer.sendMessage("[WorldData] " + text);
}
// file filter used for region files
private static class ExtFileFilter implements FileFilter
{
String ext;
public ExtFileFilter(String extension)
{
this.ext = extension.toLowerCase();
}
// crude chunk map PNG image output, for debugging
private void testImage(CoordXZ region, List<Boolean> data) {
int width = 32;
int height = 32;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
int current = 0;
g2.setColor(Color.BLACK);
@Override
public boolean accept(File file)
{
return (
file.exists()
&& file.isFile()
&& file.getName().toLowerCase().endsWith(ext)
);
}
}
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
if (data.get(current).booleanValue())
g2.fillRect(x, z, x + 1, z + 1);
current++;
}
}
// file filter used for DIM* folders (for nether, End, and custom world types)
private static class DimFolderFileFilter implements FileFilter
{
@Override
public boolean accept(File file)
{
return (
file.exists()
&& file.isDirectory()
&& file.getName().toLowerCase().startsWith("dim")
);
}
}
File f = new File("region_" + region.x + "_" + region.z + "_.png");
Config.log(f.getAbsolutePath());
try {
// png is an image format (like gif or jpg)
ImageIO.write(bi, "png", f);
} catch (IOException ex) {
Config.log("[SEVERE]" + ex.getLocalizedMessage());
}
}
// file filter used for region files
private static class ExtFileFilter implements FileFilter {
String ext;
// crude chunk map PNG image output, for debugging
private void testImage(CoordXZ region, List<Boolean> data) {
int width = 32;
int height = 32;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bi.createGraphics();
int current = 0;
g2.setColor(Color.BLACK);
public ExtFileFilter(String extension) {
this.ext = extension.toLowerCase();
}
for (int x = 0; x < 32; x++)
{
for (int z = 0; z < 32; z++)
{
if (data.get(current).booleanValue())
g2.fillRect(x,z, x+1, z+1);
current++;
}
}
@Override
public boolean accept(File file) {
return (
file.exists()
&& file.isFile()
&& file.getName().toLowerCase().endsWith(ext)
);
}
}
File f = new File("region_"+region.x+"_"+region.z+"_.png");
Config.log(f.getAbsolutePath());
try {
// png is an image format (like gif or jpg)
ImageIO.write(bi, "png", f);
} catch (IOException ex) {
Config.log("[SEVERE]" + ex.getLocalizedMessage());
}
}
// file filter used for DIM* folders (for nether, End, and custom world types)
private static class DimFolderFileFilter implements FileFilter {
@Override
public boolean accept(File file) {
return (
file.exists()
&& file.isDirectory()
&& file.getName().toLowerCase().startsWith("dim")
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,12 @@
package com.wimbli.WorldBorder;
import com.wimbli.WorldBorder.Events.WorldBorderTrimFinishedEvent;
import com.wimbli.WorldBorder.Events.WorldBorderTrimStartEvent;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -7,430 +14,374 @@ import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.Server;
import org.bukkit.World;
import com.wimbli.WorldBorder.Events.WorldBorderTrimFinishedEvent;
import com.wimbli.WorldBorder.Events.WorldBorderTrimStartEvent;
public class WorldTrimTask implements Runnable {
// general task-related reference data
private transient Server server = null;
private transient World world = null;
private transient WorldFileData worldData = null;
private transient BorderData border = null;
private transient boolean readyToGo = false;
private transient boolean paused = false;
private transient int taskID = -1;
private transient Player notifyPlayer = null;
private transient int chunksPerRun = 1;
// values for what chunk in the current region we're at
private transient int currentRegion = -1; // region(file) we're at in regionFiles
private transient int regionX = 0; // X location value of the current region
private transient int regionZ = 0; // X location value of the current region
private transient int currentChunk = 0; // chunk we've reached in the current region (regionChunks)
private transient List<CoordXZ> regionChunks = new ArrayList<CoordXZ>(1024);
private transient List<CoordXZ> trimChunks = new ArrayList<CoordXZ>(1024);
private transient int counter = 0;
// for reporting progress back to user occasionally
private transient long lastReport = Config.Now();
private transient int reportTarget = 0;
private transient int reportTotal = 0;
private transient int reportTrimmedRegions = 0;
private transient int reportTrimmedChunks = 0;
public class WorldTrimTask implements Runnable
{
// general task-related reference data
private transient Server server = null;
private transient World world = null;
private transient WorldFileData worldData = null;
private transient BorderData border = null;
private transient boolean readyToGo = false;
private transient boolean paused = false;
private transient int taskID = -1;
private transient Player notifyPlayer = null;
private transient int chunksPerRun = 1;
// values for what chunk in the current region we're at
private transient int currentRegion = -1; // region(file) we're at in regionFiles
private transient int regionX = 0; // X location value of the current region
private transient int regionZ = 0; // X location value of the current region
private transient int currentChunk = 0; // chunk we've reached in the current region (regionChunks)
private transient List<CoordXZ> regionChunks = new ArrayList<CoordXZ>(1024);
private transient List<CoordXZ> trimChunks = new ArrayList<CoordXZ>(1024);
private transient int counter = 0;
public WorldTrimTask(Server theServer, Player player, String worldName, int trimDistance, int chunksPerRun) {
this.server = theServer;
this.notifyPlayer = player;
this.chunksPerRun = chunksPerRun;
// for reporting progress back to user occasionally
private transient long lastReport = Config.Now();
private transient int reportTarget = 0;
private transient int reportTotal = 0;
private transient int reportTrimmedRegions = 0;
private transient int reportTrimmedChunks = 0;
this.world = server.getWorld(worldName);
if (this.world == null) {
if (worldName.isEmpty())
sendMessage("You must specify a world!");
else
sendMessage("World \"" + worldName + "\" not found!");
this.stop();
return;
}
this.border = (Config.Border(worldName) == null) ? null : Config.Border(worldName).copy();
if (this.border == null) {
sendMessage("No border found for world \"" + worldName + "\"!");
this.stop();
return;
}
this.border.setRadiusX(border.getRadiusX() + trimDistance);
this.border.setRadiusZ(border.getRadiusZ() + trimDistance);
worldData = WorldFileData.create(world, notifyPlayer);
if (worldData == null) {
this.stop();
return;
}
// each region file covers up to 1024 chunks; with all operations we might need to do, let's figure 3X that
this.reportTarget = worldData.regionFileCount() * 3072;
// queue up the first file
if (!nextFile())
return;
this.readyToGo = true;
Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimStartEvent(this));
}
public void setTaskID(int ID) {
this.taskID = ID;
}
public WorldTrimTask(Server theServer, Player player, String worldName, int trimDistance, int chunksPerRun)
{
this.server = theServer;
this.notifyPlayer = player;
this.chunksPerRun = chunksPerRun;
public void run() {
if (server == null || !readyToGo || paused)
return;
this.world = server.getWorld(worldName);
if (this.world == null)
{
if (worldName.isEmpty())
sendMessage("You must specify a world!");
else
sendMessage("World \"" + worldName + "\" not found!");
this.stop();
return;
}
// this is set so it only does one iteration at a time, no matter how frequently the timer fires
readyToGo = false;
// and this is tracked to keep one iteration from dragging on too long and possibly choking the system if the user specified a really high frequency
long loopStartTime = Config.Now();
this.border = (Config.Border(worldName) == null) ? null : Config.Border(worldName).copy();
if (this.border == null)
{
sendMessage("No border found for world \"" + worldName + "\"!");
this.stop();
return;
}
counter = 0;
while (counter <= chunksPerRun) {
// in case the task has been paused while we're repeating...
if (paused)
return;
this.border.setRadiusX(border.getRadiusX() + trimDistance);
this.border.setRadiusZ(border.getRadiusZ() + trimDistance);
long now = Config.Now();
worldData = WorldFileData.create(world, notifyPlayer);
if (worldData == null)
{
this.stop();
return;
}
// every 5 seconds or so, give basic progress report to let user know how it's going
if (now > lastReport + 5000)
reportProgress();
// each region file covers up to 1024 chunks; with all operations we might need to do, let's figure 3X that
this.reportTarget = worldData.regionFileCount() * 3072;
// if this iteration has been running for 45ms (almost 1 tick) or more, stop to take a breather; shouldn't normally be possible with Trim, but just in case
if (now > loopStartTime + 45) {
readyToGo = true;
return;
}
// queue up the first file
if (!nextFile())
return;
this.readyToGo = true;
Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimStartEvent(this));
}
public void setTaskID(int ID)
{
this.taskID = ID;
}
public void run()
{
if (server == null || !readyToGo || paused)
return;
// this is set so it only does one iteration at a time, no matter how frequently the timer fires
readyToGo = false;
// and this is tracked to keep one iteration from dragging on too long and possibly choking the system if the user specified a really high frequency
long loopStartTime = Config.Now();
counter = 0;
while (counter <= chunksPerRun)
{
// in case the task has been paused while we're repeating...
if (paused)
return;
long now = Config.Now();
// every 5 seconds or so, give basic progress report to let user know how it's going
if (now > lastReport + 5000)
reportProgress();
// if this iteration has been running for 45ms (almost 1 tick) or more, stop to take a breather; shouldn't normally be possible with Trim, but just in case
if (now > loopStartTime + 45)
{
readyToGo = true;
return;
}
if (regionChunks.isEmpty())
addCornerChunks();
else if (currentChunk == 4)
{ // determine if region is completely _inside_ border based on corner chunks
if (trimChunks.isEmpty())
{ // it is, so skip it and move on to next file
counter += 4;
nextFile();
continue;
}
addEdgeChunks();
addInnerChunks();
}
else if (currentChunk == 124 && trimChunks.size() == 124)
{ // region is completely _outside_ border based on edge chunks, so delete file and move on to next
counter += 16;
trimChunks = regionChunks;
unloadChunks();
reportTrimmedRegions++;
File regionFile = worldData.regionFile(currentRegion);
if (!regionFile.delete())
{
sendMessage("Error! Region file which is outside the border could not be deleted: "+regionFile.getName());
wipeChunks();
}
else
{
// if DynMap is installed, re-render the trimmed region ... disabled since it's not currently working, oh well
if (regionChunks.isEmpty())
addCornerChunks();
else if (currentChunk == 4) { // determine if region is completely _inside_ border based on corner chunks
if (trimChunks.isEmpty()) { // it is, so skip it and move on to next file
counter += 4;
nextFile();
continue;
}
addEdgeChunks();
addInnerChunks();
} else if (currentChunk == 124 && trimChunks.size() == 124) { // region is completely _outside_ border based on edge chunks, so delete file and move on to next
counter += 16;
trimChunks = regionChunks;
unloadChunks();
reportTrimmedRegions++;
File regionFile = worldData.regionFile(currentRegion);
if (!regionFile.delete()) {
sendMessage("Error! Region file which is outside the border could not be deleted: " + regionFile.getName());
wipeChunks();
} else {
// if DynMap is installed, re-render the trimmed region ... disabled since it's not currently working, oh well
// DynMapFeatures.renderRegion(world.getName(), new CoordXZ(regionX, regionZ));
}
}
nextFile();
continue;
}
else if (currentChunk == 1024)
{ // last chunk of the region has been checked, time to wipe out whichever chunks are outside the border
counter += 32;
unloadChunks();
wipeChunks();
nextFile();
continue;
}
nextFile();
continue;
} else if (currentChunk == 1024) { // last chunk of the region has been checked, time to wipe out whichever chunks are outside the border
counter += 32;
unloadChunks();
wipeChunks();
nextFile();
continue;
}
// check whether chunk is inside the border or not, add it to the "trim" list if not
CoordXZ chunk = regionChunks.get(currentChunk);
if (!isChunkInsideBorder(chunk))
trimChunks.add(chunk);
// check whether chunk is inside the border or not, add it to the "trim" list if not
CoordXZ chunk = regionChunks.get(currentChunk);
if (!isChunkInsideBorder(chunk))
trimChunks.add(chunk);
currentChunk++;
counter++;
}
currentChunk++;
counter++;
}
reportTotal += counter;
reportTotal += counter;
// ready for the next iteration to run
readyToGo = true;
}
// ready for the next iteration to run
readyToGo = true;
}
// Advance to the next region file. Returns true if successful, false if the next file isn't accessible for any reason
private boolean nextFile()
{
reportTotal = currentRegion * 3072;
currentRegion++;
regionX = regionZ = currentChunk = 0;
regionChunks = new ArrayList<CoordXZ>(1024);
trimChunks = new ArrayList<CoordXZ>(1024);
// Advance to the next region file. Returns true if successful, false if the next file isn't accessible for any reason
private boolean nextFile() {
reportTotal = currentRegion * 3072;
currentRegion++;
regionX = regionZ = currentChunk = 0;
regionChunks = new ArrayList<CoordXZ>(1024);
trimChunks = new ArrayList<CoordXZ>(1024);
// have we already handled all region files?
if (currentRegion >= worldData.regionFileCount())
{ // hey, we're done
paused = true;
readyToGo = false;
finish();
return false;
}
// have we already handled all region files?
if (currentRegion >= worldData.regionFileCount()) { // hey, we're done
paused = true;
readyToGo = false;
finish();
return false;
}
counter += 16;
counter += 16;
// get the X and Z coordinates of the current region
CoordXZ coord = worldData.regionFileCoordinates(currentRegion);
if (coord == null)
return false;
// get the X and Z coordinates of the current region
CoordXZ coord = worldData.regionFileCoordinates(currentRegion);
if (coord == null)
return false;
regionX = coord.x;
regionZ = coord.z;
return true;
}
regionX = coord.x;
regionZ = coord.z;
return true;
}
// add just the 4 corner chunks of the region; can determine if entire region is _inside_ the border
private void addCornerChunks()
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ)));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + 31, CoordXZ.regionToChunk(regionZ)));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ) + 31));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + 31, CoordXZ.regionToChunk(regionZ) + 31));
}
// add just the 4 corner chunks of the region; can determine if entire region is _inside_ the border
private void addCornerChunks() {
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ)));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + 31, CoordXZ.regionToChunk(regionZ)));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ) + 31));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + 31, CoordXZ.regionToChunk(regionZ) + 31));
}
// add all chunks along the 4 edges of the region (minus the corners); can determine if entire region is _outside_ the border
private void addEdgeChunks()
{
int chunkX = 0, chunkZ;
// add all chunks along the 4 edges of the region (minus the corners); can determine if entire region is _outside_ the border
private void addEdgeChunks() {
int chunkX = 0, chunkZ;
for (chunkZ = 1; chunkZ < 31; chunkZ++)
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX)+chunkX, CoordXZ.regionToChunk(regionZ)+chunkZ));
}
chunkX = 31;
for (chunkZ = 1; chunkZ < 31; chunkZ++)
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX)+chunkX, CoordXZ.regionToChunk(regionZ)+chunkZ));
}
chunkZ = 0;
for (chunkX = 1; chunkX < 31; chunkX++)
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX)+chunkX, CoordXZ.regionToChunk(regionZ)+chunkZ));
}
chunkZ = 31;
for (chunkX = 1; chunkX < 31; chunkX++)
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX)+chunkX, CoordXZ.regionToChunk(regionZ)+chunkZ));
}
counter += 4;
}
for (chunkZ = 1; chunkZ < 31; chunkZ++) {
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
}
chunkX = 31;
for (chunkZ = 1; chunkZ < 31; chunkZ++) {
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
}
chunkZ = 0;
for (chunkX = 1; chunkX < 31; chunkX++) {
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
}
chunkZ = 31;
for (chunkX = 1; chunkX < 31; chunkX++) {
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
}
counter += 4;
}
// add the remaining interior chunks (after corners and edges)
private void addInnerChunks()
{
for (int chunkX = 1; chunkX < 31; chunkX++)
{
for (int chunkZ = 1; chunkZ < 31; chunkZ++)
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX)+chunkX, CoordXZ.regionToChunk(regionZ)+chunkZ));
}
}
counter += 32;
}
// add the remaining interior chunks (after corners and edges)
private void addInnerChunks() {
for (int chunkX = 1; chunkX < 31; chunkX++) {
for (int chunkZ = 1; chunkZ < 31; chunkZ++) {
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
}
}
counter += 32;
}
// make sure chunks set to be trimmed are not currently loaded by the server
private void unloadChunks()
{
for (CoordXZ unload : trimChunks)
{
if (world.isChunkLoaded(unload.x, unload.z))
world.unloadChunk(unload.x, unload.z, false);
}
counter += trimChunks.size();
}
// make sure chunks set to be trimmed are not currently loaded by the server
private void unloadChunks() {
for (CoordXZ unload : trimChunks) {
if (world.isChunkLoaded(unload.x, unload.z))
world.unloadChunk(unload.x, unload.z, false);
}
counter += trimChunks.size();
}
// edit region file to wipe all chunk pointers for chunks outside the border
private void wipeChunks()
{
File regionFile = worldData.regionFile(currentRegion);
if (!regionFile.canWrite())
{
if (!regionFile.setWritable(true))
throw new RuntimeException();
// edit region file to wipe all chunk pointers for chunks outside the border
private void wipeChunks() {
File regionFile = worldData.regionFile(currentRegion);
if (!regionFile.canWrite()) {
if (!regionFile.setWritable(true))
throw new RuntimeException();
if (!regionFile.canWrite())
{
sendMessage("Error! region file is locked and can't be trimmed: "+regionFile.getName());
return;
}
}
if (!regionFile.canWrite()) {
sendMessage("Error! region file is locked and can't be trimmed: " + regionFile.getName());
return;
}
}
// since our stored chunk positions are based on world, we need to offset those to positions in the region file
int offsetX = CoordXZ.regionToChunk(regionX);
int offsetZ = CoordXZ.regionToChunk(regionZ);
long wipePos = 0;
int chunkCount = 0;
// since our stored chunk positions are based on world, we need to offset those to positions in the region file
int offsetX = CoordXZ.regionToChunk(regionX);
int offsetZ = CoordXZ.regionToChunk(regionZ);
long wipePos = 0;
int chunkCount = 0;
try
{
RandomAccessFile unChunk = new RandomAccessFile(regionFile, "rwd");
for (CoordXZ wipe : trimChunks)
{
// if the chunk pointer is empty (chunk doesn't technically exist), no need to wipe the already empty pointer
if (!worldData.doesChunkExist(wipe.x, wipe.z))
continue;
try {
RandomAccessFile unChunk = new RandomAccessFile(regionFile, "rwd");
for (CoordXZ wipe : trimChunks) {
// if the chunk pointer is empty (chunk doesn't technically exist), no need to wipe the already empty pointer
if (!worldData.doesChunkExist(wipe.x, wipe.z))
continue;
// wipe this extraneous chunk's pointer... note that this method isn't perfect since the actual chunk data is left orphaned,
// but Minecraft will overwrite the orphaned data sector if/when another chunk is created in the region, so it's not so bad
wipePos = 4 * ((wipe.x - offsetX) + ((wipe.z - offsetZ) * 32));
unChunk.seek(wipePos);
unChunk.writeInt(0);
chunkCount++;
}
unChunk.close();
// wipe this extraneous chunk's pointer... note that this method isn't perfect since the actual chunk data is left orphaned,
// but Minecraft will overwrite the orphaned data sector if/when another chunk is created in the region, so it's not so bad
wipePos = 4 * ((wipe.x - offsetX) + ((wipe.z - offsetZ) * 32));
unChunk.seek(wipePos);
unChunk.writeInt(0);
chunkCount++;
}
unChunk.close();
// if DynMap is installed, re-render the trimmed chunks ... disabled since it's not currently working, oh well
// if DynMap is installed, re-render the trimmed chunks ... disabled since it's not currently working, oh well
// DynMapFeatures.renderChunks(world.getName(), trimChunks);
reportTrimmedChunks += chunkCount;
}
catch (FileNotFoundException ex)
{
sendMessage("Error! Could not open region file to wipe individual chunks: "+regionFile.getName());
}
catch (IOException ex)
{
sendMessage("Error! Could not modify region file to wipe individual chunks: "+regionFile.getName());
}
counter += trimChunks.size();
}
reportTrimmedChunks += chunkCount;
} catch (FileNotFoundException ex) {
sendMessage("Error! Could not open region file to wipe individual chunks: " + regionFile.getName());
} catch (IOException ex) {
sendMessage("Error! Could not modify region file to wipe individual chunks: " + regionFile.getName());
}
counter += trimChunks.size();
}
private boolean isChunkInsideBorder(CoordXZ chunk)
{
return border.insideBorder(CoordXZ.chunkToBlock(chunk.x) + 8, CoordXZ.chunkToBlock(chunk.z) + 8);
}
private boolean isChunkInsideBorder(CoordXZ chunk) {
return border.insideBorder(CoordXZ.chunkToBlock(chunk.x) + 8, CoordXZ.chunkToBlock(chunk.z) + 8);
}
// for successful completion
public void finish()
{
reportTotal = reportTarget;
reportProgress();
Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimFinishedEvent(world, reportTotal));
sendMessage("task successfully completed!");
this.stop();
}
// for successful completion
public void finish() {
reportTotal = reportTarget;
reportProgress();
Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimFinishedEvent(world, reportTotal));
sendMessage("task successfully completed!");
this.stop();
}
// for cancelling prematurely
public void cancel()
{
this.stop();
}
// for cancelling prematurely
public void cancel() {
this.stop();
}
// we're done, whether finished or cancelled
private void stop()
{
if (server == null)
return;
// we're done, whether finished or cancelled
private void stop() {
if (server == null)
return;
readyToGo = false;
if (taskID != -1)
server.getScheduler().cancelTask(taskID);
server = null;
readyToGo = false;
if (taskID != -1)
server.getScheduler().cancelTask(taskID);
server = null;
sendMessage("NOTICE: it is recommended that you restart your server after a Trim, to be on the safe side.");
if (DynMapFeatures.renderEnabled())
sendMessage("This especially true with DynMap. You should also run a fullrender in DynMap for the trimmed world after restarting, so trimmed chunks are updated on the map.");
}
sendMessage("NOTICE: it is recommended that you restart your server after a Trim, to be on the safe side.");
if (DynMapFeatures.renderEnabled())
sendMessage("This especially true with DynMap. You should also run a fullrender in DynMap for the trimmed world after restarting, so trimmed chunks are updated on the map.");
}
// is this task still valid/workable?
public boolean valid()
{
return this.server != null;
}
// is this task still valid/workable?
public boolean valid() {
return this.server != null;
}
// handle pausing/unpausing the task
public void pause()
{
pause(!this.paused);
}
public void pause(boolean pause)
{
this.paused = pause;
if (pause)
reportProgress();
}
public boolean isPaused()
{
return this.paused;
}
// handle pausing/unpausing the task
public void pause() {
pause(!this.paused);
}
// let the user know how things are coming along
private void reportProgress()
{
lastReport = Config.Now();
double perc = getPercentageCompleted();
sendMessage(reportTrimmedRegions + " entire region(s) and " + reportTrimmedChunks + " individual chunk(s) trimmed so far (" + Config.coord.format(perc) + "% done" + ")");
}
public void pause(boolean pause) {
this.paused = pause;
if (pause)
reportProgress();
}
// send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text)
{
Config.log("[Trim] " + text);
if (notifyPlayer != null)
notifyPlayer.sendMessage("[Trim] " + text);
}
/**
* Get the percentage completed for the trim task.
*
* @return Percentage
*/
public double getPercentageCompleted() {
return ((double) (reportTotal) / (double) reportTarget) * 100;
}
public boolean isPaused() {
return this.paused;
}
/**
* Amount of chunks completed for the trim task.
*
* @return Number of chunks processed.
*/
public int getChunksCompleted() {
return reportTotal;
}
// let the user know how things are coming along
private void reportProgress() {
lastReport = Config.Now();
double perc = getPercentageCompleted();
sendMessage(reportTrimmedRegions + " entire region(s) and " + reportTrimmedChunks + " individual chunk(s) trimmed so far (" + Config.coord.format(perc) + "% done" + ")");
}
/**
* Total amount of chunks that need to be trimmed for the trim task.
*
* @return Number of chunks that need to be processed.
*/
public int getChunksTotal() {
return reportTarget;
}
// send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text) {
Config.log("[Trim] " + text);
if (notifyPlayer != null)
notifyPlayer.sendMessage("[Trim] " + text);
}
/**
* Get the percentage completed for the trim task.
*
* @return Percentage
*/
public double getPercentageCompleted() {
return ((double) (reportTotal) / (double) reportTarget) * 100;
}
/**
* Amount of chunks completed for the trim task.
*
* @return Number of chunks processed.
*/
public int getChunksCompleted() {
return reportTotal;
}
/**
* Total amount of chunks that need to be trimmed for the trim task.
*
* @return Number of chunks that need to be processed.
*/
public int getChunksTotal() {
return reportTarget;
}
}

View File

@ -1,100 +1,85 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import com.wimbli.WorldBorder.UUID.UUIDFetcher;
import com.wimbli.WorldBorder.WorldBorder;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
import com.wimbli.WorldBorder.UUID.UUIDFetcher;
public class CmdBypass extends WBCmd {
public CmdBypass() {
name = permission = "bypass";
minParams = 0;
maxParams = 2;
addCmdExample(nameEmphasized() + "{player} [on|off] - let player go beyond border.");
helpText = "If [player] isn't specified, command sender is used. If [on|off] isn't specified, the value will " +
"be toggled. Once bypass is enabled, the player will not be stopped by any borders until bypass is " +
"disabled for them again. Use the " + commandEmphasized("bypasslist") + C_DESC + "command to list all " +
"players with bypass enabled.";
}
public class CmdBypass extends WBCmd
{
public CmdBypass()
{
name = permission = "bypass";
minParams = 0;
maxParams = 2;
@Override
public void cmdStatus(CommandSender sender) {
if (!(sender instanceof Player))
return;
addCmdExample(nameEmphasized() + "{player} [on|off] - let player go beyond border.");
helpText = "If [player] isn't specified, command sender is used. If [on|off] isn't specified, the value will " +
"be toggled. Once bypass is enabled, the player will not be stopped by any borders until bypass is " +
"disabled for them again. Use the " + commandEmphasized("bypasslist") + C_DESC + "command to list all " +
"players with bypass enabled.";
}
boolean bypass = Config.isPlayerBypassing(((Player) sender).getUniqueId());
sender.sendMessage(C_HEAD + "Border bypass is currently " + enabledColored(bypass) + C_HEAD + " for you.");
}
@Override
public void cmdStatus(CommandSender sender)
{
if (!(sender instanceof Player))
return;
@Override
public void execute(final CommandSender sender, final Player player, final List<String> params, String worldName) {
if (player == null && params.isEmpty()) {
sendErrorAndHelp(sender, "When running this command from console, you must specify a player.");
return;
}
boolean bypass = Config.isPlayerBypassing(((Player)sender).getUniqueId());
sender.sendMessage(C_HEAD + "Border bypass is currently " + enabledColored(bypass) + C_HEAD + " for you.");
}
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() {
@Override
public void run() {
final String sPlayer = (params.isEmpty()) ? player.getName() : params.get(0);
UUID uPlayer = (params.isEmpty()) ? player.getUniqueId() : null;
@Override
public void execute(final CommandSender sender, final Player player, final List<String> params, String worldName)
{
if (player == null && params.isEmpty())
{
sendErrorAndHelp(sender, "When running this command from console, you must specify a player.");
return;
}
if (uPlayer == null) {
Player p = Bukkit.getPlayer(sPlayer);
if (p != null) {
uPlayer = p.getUniqueId();
} else {
// only do UUID lookup using Mojang server if specified player isn't online
try {
uPlayer = UUIDFetcher.getUUID(sPlayer);
} catch (Exception ex) {
sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified. " + ex.getLocalizedMessage());
return;
}
}
}
if (uPlayer == null) {
sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified; null value returned.");
return;
}
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable()
{
@Override
public void run()
{
final String sPlayer = (params.isEmpty()) ? player.getName() : params.get(0);
UUID uPlayer = (params.isEmpty()) ? player.getUniqueId() : null;
boolean bypassing = !Config.isPlayerBypassing(uPlayer);
if (params.size() > 1)
bypassing = strAsBool(params.get(1));
if (uPlayer == null)
{
Player p = Bukkit.getPlayer(sPlayer);
if (p != null)
{
uPlayer = p.getUniqueId();
}
else
{
// only do UUID lookup using Mojang server if specified player isn't online
try
{
uPlayer = UUIDFetcher.getUUID(sPlayer);
}
catch(Exception ex)
{
sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified. " + ex.getLocalizedMessage());
return;
}
}
}
if (uPlayer == null)
{
sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified; null value returned.");
return;
}
Config.setPlayerBypass(uPlayer, bypassing);
boolean bypassing = !Config.isPlayerBypassing(uPlayer);
if (params.size() > 1)
bypassing = strAsBool(params.get(1));
Player target = Bukkit.getPlayer(sPlayer);
if (target != null && target.isOnline())
target.sendMessage("Border bypass is now " + enabledColored(bypassing) + ".");
Config.setPlayerBypass(uPlayer, bypassing);
Player target = Bukkit.getPlayer(sPlayer);
if (target != null && target.isOnline())
target.sendMessage("Border bypass is now " + enabledColored(bypassing) + ".");
Config.log("Border bypass for player \"" + sPlayer + "\" is " + (bypassing ? "enabled" : "disabled") +
(player != null ? " at the command of player \"" + player.getName() + "\"" : "") + ".");
if (player != null && player != target)
sender.sendMessage("Border bypass for player \"" + sPlayer + "\" is " + enabledColored(bypassing) + ".");
}
});
}
Config.log("Border bypass for player \"" + sPlayer + "\" is " + (bypassing ? "enabled" : "disabled") +
(player != null ? " at the command of player \"" + player.getName() + "\"" : "") + ".");
if (player != null && player != target)
sender.sendMessage("Border bypass for player \"" + sPlayer + "\" is " + enabledColored(bypassing) + ".");
}
});
}
}

View File

@ -1,58 +1,49 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import com.wimbli.WorldBorder.UUID.UUIDFetcher;
import com.wimbli.WorldBorder.WorldBorder;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
import com.wimbli.WorldBorder.UUID.UUIDFetcher;
public class CmdBypasslist extends WBCmd {
public CmdBypasslist() {
name = permission = "bypasslist";
minParams = maxParams = 0;
addCmdExample(nameEmphasized() + "- list players with border bypass enabled.");
helpText = "The bypass list will persist between server restarts, and applies to all worlds. Use the " +
commandEmphasized("bypass") + C_DESC + "command to add or remove players.";
}
public class CmdBypasslist extends WBCmd
{
public CmdBypasslist()
{
name = permission = "bypasslist";
minParams = maxParams = 0;
@Override
public void execute(final CommandSender sender, Player player, List<String> params, String worldName) {
final ArrayList<UUID> uuids = Config.getPlayerBypassList();
if (uuids == null || uuids.isEmpty()) {
sender.sendMessage("Players with border bypass enabled: <none>");
return;
}
addCmdExample(nameEmphasized() + "- list players with border bypass enabled.");
helpText = "The bypass list will persist between server restarts, and applies to all worlds. Use the " +
commandEmphasized("bypass") + C_DESC + "command to add or remove players.";
}
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() {
@Override
public void run() {
try {
Map<UUID, String> names = UUIDFetcher.getNameList(uuids);
String nameString = names.values().toString();
@Override
public void execute(final CommandSender sender, Player player, List<String> params, String worldName)
{
final ArrayList<UUID> uuids = Config.getPlayerBypassList();
if (uuids == null || uuids.isEmpty())
{
sender.sendMessage("Players with border bypass enabled: <none>");
return;
}
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable()
{
@Override
public void run()
{
try
{
Map<UUID, String> names = UUIDFetcher.getNameList(uuids);
String nameString = names.values().toString();
sender.sendMessage("Players with border bypass enabled: " + nameString.substring(1, nameString.length() - 1));
}
catch(Exception ex)
{
sendErrorAndHelp(sender, "Failed to look up names for the UUIDs in the border bypass list. " + ex.getLocalizedMessage());
return;
}
}
});
}
sender.sendMessage("Players with border bypass enabled: " + nameString.substring(1, nameString.length() - 1));
} catch (Exception ex) {
sendErrorAndHelp(sender, "Failed to look up names for the UUIDs in the border bypass list. " + ex.getLocalizedMessage());
return;
}
}
});
}
}

View File

@ -1,68 +1,60 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.BorderData;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdClear extends WBCmd {
public CmdClear() {
name = permission = "clear";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 0;
maxParams = 1;
addCmdExample(nameEmphasizedW() + "- remove border for this world.");
addCmdExample(nameEmphasized() + "^all - remove border for all worlds.");
helpText = "If run by an in-game player and [world] or \"all\" isn't specified, the world you are currently " +
"in is used.";
}
public class CmdClear extends WBCmd
{
public CmdClear()
{
name = permission = "clear";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 0;
maxParams = 1;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
// handle "clear all" command separately
if (params.size() == 1 && params.get(0).equalsIgnoreCase("all")) {
if (worldName != null) {
sendErrorAndHelp(sender, "You should not specify a world with \"clear all\".");
return;
}
addCmdExample(nameEmphasizedW() + "- remove border for this world.");
addCmdExample(nameEmphasized() + "^all - remove border for all worlds.");
helpText = "If run by an in-game player and [world] or \"all\" isn't specified, the world you are currently " +
"in is used.";
}
Config.removeAllBorders();
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
// handle "clear all" command separately
if (params.size() == 1 && params.get(0).equalsIgnoreCase("all"))
{
if (worldName != null)
{
sendErrorAndHelp(sender, "You should not specify a world with \"clear all\".");
return;
}
if (player != null)
sender.sendMessage("All borders have been cleared for all worlds.");
return;
}
Config.removeAllBorders();
if (worldName == null) {
if (player == null) {
sendErrorAndHelp(sender, "You must specify a world name from console if not using \"clear all\".");
return;
}
worldName = player.getWorld().getName();
}
if (player != null)
sender.sendMessage("All borders have been cleared for all worlds.");
return;
}
BorderData border = Config.Border(worldName);
if (border == null) {
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return;
}
if (worldName == null)
{
if (player == null)
{
sendErrorAndHelp(sender, "You must specify a world name from console if not using \"clear all\".");
return;
}
worldName = player.getWorld().getName();
}
Config.removeBorder(worldName);
BorderData border = Config.Border(worldName);
if (border == null)
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return;
}
Config.removeBorder(worldName);
if (player != null)
sender.sendMessage("Border cleared for world \"" + worldName + "\".");
}
if (player != null)
sender.sendMessage("Border cleared for world \"" + worldName + "\".");
}
}

View File

@ -1,74 +1,63 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.WorldBorder;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdCommands extends WBCmd {
private static int pageSize = 8; // examples to list per page; 10 lines available, 1 for header, 1 for footer
public CmdCommands() {
name = "commands";
permission = "help";
hasWorldNameInput = false;
}
public class CmdCommands extends WBCmd
{
private static int pageSize = 8; // examples to list per page; 10 lines available, 1 for header, 1 for footer
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
// determine which page we're viewing
int page = (player == null) ? 0 : 1;
if (!params.isEmpty()) {
try {
page = Integer.parseInt(params.get(0));
} catch (NumberFormatException ignored) {
}
}
public CmdCommands()
{
name = "commands";
permission = "help";
hasWorldNameInput = false;
}
// see whether we're showing examples to player or to console, and determine number of pages available
List<String> examples = (player == null) ? cmdExamplesConsole : cmdExamplesPlayer;
int pageCount = (int) Math.ceil(examples.size() / (double) pageSize);
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
// determine which page we're viewing
int page = (player == null) ? 0 : 1;
if (!params.isEmpty())
{
try
{
page = Integer.parseInt(params.get(0));
}
catch(NumberFormatException ignored) {}
}
// if specified page number is negative or higher than we have available, default back to first page
if (page < 0 || page > pageCount)
page = (player == null) ? 0 : 1;
// see whether we're showing examples to player or to console, and determine number of pages available
List<String> examples = (player == null) ? cmdExamplesConsole : cmdExamplesPlayer;
int pageCount = (int) Math.ceil(examples.size() / (double) pageSize);
// send command example header
sender.sendMessage(C_HEAD + WorldBorder.plugin.getDescription().getFullName() + " - key: " +
commandEmphasized("command") + C_REQ + "<required> " + C_OPT + "[optional]");
// if specified page number is negative or higher than we have available, default back to first page
if (page < 0 || page > pageCount)
page = (player == null) ? 0 : 1;
if (page > 0) {
// send examples for this page
int first = ((page - 1) * pageSize);
int count = Math.min(pageSize, examples.size() - first);
for (int i = first; i < first + count; i++) {
sender.sendMessage(examples.get(i));
}
// send command example header
sender.sendMessage( C_HEAD + WorldBorder.plugin.getDescription().getFullName() + " - key: " +
commandEmphasized("command") + C_REQ + "<required> " + C_OPT + "[optional]" );
if (page > 0)
{
// send examples for this page
int first = ((page - 1) * pageSize);
int count = Math.min(pageSize, examples.size() - first);
for(int i = first; i < first + count; i++)
{
sender.sendMessage(examples.get(i));
}
// send page footer, if relevant; manual spacing to get right side lined up near edge is crude, but sufficient
String footer = C_HEAD + " (Page " + page + "/" + pageCount + ") " + cmd(sender);
if (page < pageCount)
sender.sendMessage(footer + Integer.toString(page + 1) + C_DESC + " - view next page of commands.");
else if (page > 1)
sender.sendMessage(footer + C_DESC + "- view first page of commands.");
}
else
{
// if page "0" is specified, send all examples; done by default for console but can be specified by player
for (String example : examples)
{
sender.sendMessage(example);
}
}
}
// send page footer, if relevant; manual spacing to get right side lined up near edge is crude, but sufficient
String footer = C_HEAD + " (Page " + page + "/" + pageCount + ") " + cmd(sender);
if (page < pageCount)
sender.sendMessage(footer + Integer.toString(page + 1) + C_DESC + " - view next page of commands.");
else if (page > 1)
sender.sendMessage(footer + C_DESC + "- view first page of commands.");
} else {
// if page "0" is specified, send all examples; done by default for console but can be specified by player
for (String example : examples) {
sender.sendMessage(example);
}
}
}
}

View File

@ -1,40 +1,34 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdDebug extends WBCmd {
public CmdDebug() {
name = permission = "debug";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - turn console debug output on or off.");
helpText = "Default value: off. Debug mode will show some extra debugging data in the server console/log when " +
"players are knocked back from the border or are teleported.";
}
public class CmdDebug extends WBCmd
{
public CmdDebug()
{
name = permission = "debug";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "Debug mode is " + enabledColored(Config.Debug()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - turn console debug output on or off.");
helpText = "Default value: off. Debug mode will show some extra debugging data in the server console/log when " +
"players are knocked back from the border or are teleported.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setDebug(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "Debug mode is " + enabledColored(Config.Debug()) + C_HEAD + ".");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setDebug(strAsBool(params.get(0)));
if (player != null)
{
Config.log((Config.Debug() ? "Enabled" : "Disabled") + " debug output at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
if (player != null) {
Config.log((Config.Debug() ? "Enabled" : "Disabled") + " debug output at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
}

View File

@ -1,51 +1,43 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdDelay extends WBCmd {
public CmdDelay() {
name = permission = "delay";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<amount> - time between border checks.");
helpText = "Default value: 5. The <amount> is in server ticks, of which there are roughly 20 every second, each " +
"tick taking ~50ms. The default value therefore has border checks run about 4 times per second.";
}
public class CmdDelay extends WBCmd
{
public CmdDelay()
{
name = permission = "delay";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
int delay = Config.TimerTicks();
sender.sendMessage(C_HEAD + "Timer delay is set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms.");
}
addCmdExample(nameEmphasized() + "<amount> - time between border checks.");
helpText = "Default value: 5. The <amount> is in server ticks, of which there are roughly 20 every second, each " +
"tick taking ~50ms. The default value therefore has border checks run about 4 times per second.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
int delay = 0;
try {
delay = Integer.parseInt(params.get(0));
if (delay < 1)
throw new NumberFormatException();
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The timer delay must be an integer of 1 or higher.");
return;
}
@Override
public void cmdStatus(CommandSender sender)
{
int delay = Config.TimerTicks();
sender.sendMessage(C_HEAD + "Timer delay is set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms.");
}
Config.setTimerTicks(delay);
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
int delay = 0;
try
{
delay = Integer.parseInt(params.get(0));
if (delay < 1)
throw new NumberFormatException();
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The timer delay must be an integer of 1 or higher.");
return;
}
Config.setTimerTicks(delay);
if (player != null)
cmdStatus(sender);
}
if (player != null)
cmdStatus(sender);
}
}

View File

@ -1,42 +1,36 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdDenypearl extends WBCmd {
public CmdDenypearl() {
name = permission = "denypearl";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - stop ender pearls past the border.");
helpText = "Default value: on. When enabled, this setting will directly cancel attempts to use an ender pearl to " +
"get past the border rather than just knocking the player back. This should prevent usage of ender " +
"pearls to glitch into areas otherwise inaccessible at the border edge.";
}
public class CmdDenypearl extends WBCmd
{
public CmdDenypearl()
{
name = permission = "denypearl";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "Direct cancellation of ender pearls thrown past the border is " +
enabledColored(Config.getDenyEnderpearl()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - stop ender pearls past the border.");
helpText = "Default value: on. When enabled, this setting will directly cancel attempts to use an ender pearl to " +
"get past the border rather than just knocking the player back. This should prevent usage of ender " +
"pearls to glitch into areas otherwise inaccessible at the border edge.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setDenyEnderpearl(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "Direct cancellation of ender pearls thrown past the border is " +
enabledColored(Config.getDenyEnderpearl()) + C_HEAD + ".");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setDenyEnderpearl(strAsBool(params.get(0)));
if (player != null)
{
Config.log((Config.getDenyEnderpearl() ? "Enabled" : "Disabled") + " direct cancellation of ender pearls thrown past the border at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
if (player != null) {
Config.log((Config.getDenyEnderpearl() ? "Enabled" : "Disabled") + " direct cancellation of ender pearls thrown past the border at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
}

View File

@ -1,40 +1,34 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdDynmap extends WBCmd {
public CmdDynmap() {
name = permission = "dynmap";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - turn DynMap border display on or off.");
helpText = "Default value: on. If you are running the DynMap plugin and this setting is enabled, all borders will " +
"be visually shown in DynMap.";
}
public class CmdDynmap extends WBCmd
{
public CmdDynmap()
{
name = permission = "dynmap";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "DynMap border display is " + enabledColored(Config.DynmapBorderEnabled()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - turn DynMap border display on or off.");
helpText = "Default value: on. If you are running the DynMap plugin and this setting is enabled, all borders will " +
"be visually shown in DynMap.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setDynmapBorderEnabled(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "DynMap border display is " + enabledColored(Config.DynmapBorderEnabled()) + C_HEAD + ".");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setDynmapBorderEnabled(strAsBool(params.get(0)));
if (player != null)
{
cmdStatus(sender);
Config.log((Config.DynmapBorderEnabled() ? "Enabled" : "Disabled") + " DynMap border display at the command of player \"" + player.getName() + "\".");
}
}
if (player != null) {
cmdStatus(sender);
Config.log((Config.DynmapBorderEnabled() ? "Enabled" : "Disabled") + " DynMap border display at the command of player \"" + player.getName() + "\".");
}
}
}

View File

@ -1,48 +1,42 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdDynmapmsg extends WBCmd {
public CmdDynmapmsg() {
name = permission = "dynmapmsg";
minParams = 1;
addCmdExample(nameEmphasized() + "<text> - DynMap border labels will show this.");
helpText = "Default value: \"The border of the world.\". If you are running the DynMap plugin and the " +
commandEmphasized("dynmap") + C_DESC + "command setting is enabled, the borders shown in DynMap will " +
"be labelled with this text.";
}
public class CmdDynmapmsg extends WBCmd
{
public CmdDynmapmsg()
{
name = permission = "dynmapmsg";
minParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "DynMap border label is set to: " + C_ERR + Config.DynmapMessage());
}
addCmdExample(nameEmphasized() + "<text> - DynMap border labels will show this.");
helpText = "Default value: \"The border of the world.\". If you are running the DynMap plugin and the " +
commandEmphasized("dynmap") + C_DESC + "command setting is enabled, the borders shown in DynMap will " +
"be labelled with this text.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
StringBuilder message = new StringBuilder();
boolean first = true;
for (String param : params) {
if (!first)
message.append(" ");
message.append(param);
first = false;
}
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "DynMap border label is set to: " + C_ERR + Config.DynmapMessage());
}
Config.setDynmapMessage(message.toString());
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
StringBuilder message = new StringBuilder();
boolean first = true;
for (String param : params)
{
if (!first)
message.append(" ");
message.append(param);
first = false;
}
Config.setDynmapMessage(message.toString());
if (player != null)
cmdStatus(sender);
}
if (player != null)
cmdStatus(sender);
}
}

View File

@ -1,183 +1,161 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import com.wimbli.WorldBorder.CoordXZ;
import com.wimbli.WorldBorder.WorldBorder;
import com.wimbli.WorldBorder.WorldFillTask;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdFill extends WBCmd {
/* with "view-distance=10" in server.properties on a fast VM test server and "Render Distance: Far" in client,
* hitting border during testing was loading 11+ chunks beyond the border in a couple of directions (10 chunks in
* the other two directions). This could be worse on a more loaded or worse server, so:
*/
private final int defaultPadding = CoordXZ.chunkToBlock(13);
private String fillWorld = "";
private int fillFrequency = 20;
private int fillPadding = defaultPadding;
private boolean fillForceLoad = false;
public CmdFill() {
name = permission = "fill";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 0;
maxParams = 3;
public class CmdFill extends WBCmd
{
public CmdFill()
{
name = permission = "fill";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 0;
maxParams = 3;
addCmdExample(nameEmphasizedW() + "[freq] [pad] [force] - fill world to border.");
helpText = "This command will generate missing world chunks inside your border. [freq] is the frequency " +
"of chunks per second that will be checked (default 20). [pad] is the number of blocks padding added " +
"beyond the border itself (default 208, to cover player visual range). [force] can be specified as true " +
"to force all chunks to be loaded even if they seem to be fully generated (default false).";
}
addCmdExample(nameEmphasizedW() + "[freq] [pad] [force] - fill world to border.");
helpText = "This command will generate missing world chunks inside your border. [freq] is the frequency " +
"of chunks per second that will be checked (default 20). [pad] is the number of blocks padding added " +
"beyond the border itself (default 208, to cover player visual range). [force] can be specified as true " +
"to force all chunks to be loaded even if they seem to be fully generated (default false).";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
boolean confirm = false;
// check for "cancel", "pause", or "confirm"
if (params.size() >= 1) {
String check = params.get(0).toLowerCase();
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
boolean confirm = false;
// check for "cancel", "pause", or "confirm"
if (params.size() >= 1)
{
String check = params.get(0).toLowerCase();
if (check.equals("cancel") || check.equals("stop")) {
if (!makeSureFillIsRunning(sender))
return;
sender.sendMessage(C_HEAD + "Cancelling the world map generation task.");
fillDefaults();
Config.StopFillTask();
return;
} else if (check.equals("pause")) {
if (!makeSureFillIsRunning(sender))
return;
Config.fillTask.pause();
sender.sendMessage(C_HEAD + "The world map generation task is now " + (Config.fillTask.isPaused() ? "" : "un") + "paused.");
return;
}
if (check.equals("cancel") || check.equals("stop"))
{
if (!makeSureFillIsRunning(sender))
return;
sender.sendMessage(C_HEAD + "Cancelling the world map generation task.");
fillDefaults();
Config.StopFillTask();
return;
}
else if (check.equals("pause"))
{
if (!makeSureFillIsRunning(sender))
return;
Config.fillTask.pause();
sender.sendMessage(C_HEAD + "The world map generation task is now " + (Config.fillTask.isPaused() ? "" : "un") + "paused.");
return;
}
confirm = check.equals("confirm");
}
confirm = check.equals("confirm");
}
// if not just confirming, make sure a world name is available
if (worldName == null && !confirm) {
if (player != null)
worldName = player.getWorld().getName();
else {
sendErrorAndHelp(sender, "You must specify a world!");
return;
}
}
// if not just confirming, make sure a world name is available
if (worldName == null && !confirm)
{
if (player != null)
worldName = player.getWorld().getName();
else
{
sendErrorAndHelp(sender, "You must specify a world!");
return;
}
}
// colorized "/wb fill "
String cmd = cmd(sender) + nameEmphasized() + C_CMD;
// colorized "/wb fill "
String cmd = cmd(sender) + nameEmphasized() + C_CMD;
// make sure Fill isn't already running
if (Config.fillTask != null && Config.fillTask.valid()) {
sender.sendMessage(C_ERR + "The world map generation task is already running.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
return;
}
// make sure Fill isn't already running
if (Config.fillTask != null && Config.fillTask.valid())
{
sender.sendMessage(C_ERR + "The world map generation task is already running.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
return;
}
// set frequency and/or padding if those were specified
try {
if (params.size() >= 1 && !confirm)
fillFrequency = Math.abs(Integer.parseInt(params.get(0)));
if (params.size() >= 2 && !confirm)
fillPadding = Math.abs(Integer.parseInt(params.get(1)));
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The frequency and padding values must be integers.");
fillDefaults();
return;
}
if (fillFrequency <= 0) {
sendErrorAndHelp(sender, "The frequency value must be greater than zero.");
fillDefaults();
return;
}
// set frequency and/or padding if those were specified
try
{
if (params.size() >= 1 && !confirm)
fillFrequency = Math.abs(Integer.parseInt(params.get(0)));
if (params.size() >= 2 && !confirm)
fillPadding = Math.abs(Integer.parseInt(params.get(1)));
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The frequency and padding values must be integers.");
fillDefaults();
return;
}
if (fillFrequency <= 0)
{
sendErrorAndHelp(sender, "The frequency value must be greater than zero.");
fillDefaults();
return;
}
// see if the command specifies to load even chunks which should already be fully generated
if (params.size() == 3)
fillForceLoad = strAsBool(params.get(2));
// see if the command specifies to load even chunks which should already be fully generated
if (params.size() == 3)
fillForceLoad = strAsBool(params.get(2));
// set world if it was specified
if (worldName != null)
fillWorld = worldName;
// set world if it was specified
if (worldName != null)
fillWorld = worldName;
if (confirm) { // command confirmed, go ahead with it
if (fillWorld.isEmpty()) {
sendErrorAndHelp(sender, "You must first use this command successfully without confirming.");
return;
}
if (confirm)
{ // command confirmed, go ahead with it
if (fillWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first use this command successfully without confirming.");
return;
}
if (player != null)
Config.log("Filling out world to border at the command of player \"" + player.getName() + "\".");
if (player != null)
Config.log("Filling out world to border at the command of player \"" + player.getName() + "\".");
int ticks = 1, repeats = 1;
if (fillFrequency > 20)
repeats = fillFrequency / 20;
else
ticks = 20 / fillFrequency;
int ticks = 1, repeats = 1;
if (fillFrequency > 20)
repeats = fillFrequency / 20;
else
ticks = 20 / fillFrequency;
/* */
Config.log("world: " + fillWorld + " padding: " + fillPadding + " repeats: " + repeats + " ticks: " + ticks);
Config.fillTask = new WorldFillTask(Bukkit.getServer(), player, fillWorld, fillPadding, repeats, ticks, fillForceLoad);
if (Config.fillTask.valid()) {
int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.fillTask, ticks, ticks);
Config.fillTask.setTaskID(task);
sender.sendMessage("WorldBorder map generation task for world \"" + fillWorld + "\" started.");
} else
sender.sendMessage(C_ERR + "The world map generation task failed to start.");
/* */ Config.log("world: " + fillWorld + " padding: " + fillPadding + " repeats: " + repeats + " ticks: " + ticks);
Config.fillTask = new WorldFillTask(Bukkit.getServer(), player, fillWorld, fillPadding, repeats, ticks, fillForceLoad);
if (Config.fillTask.valid())
{
int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.fillTask, ticks, ticks);
Config.fillTask.setTaskID(task);
sender.sendMessage("WorldBorder map generation task for world \"" + fillWorld + "\" started.");
}
else
sender.sendMessage(C_ERR + "The world map generation task failed to start.");
fillDefaults();
} else {
if (fillWorld.isEmpty()) {
sendErrorAndHelp(sender, "You must first specify a valid world.");
return;
}
fillDefaults();
}
else
{
if (fillWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first specify a valid world.");
return;
}
sender.sendMessage(C_HEAD + "World generation task is ready for world \"" + fillWorld + "\", attempting to process up to " + fillFrequency + " chunks per second (default 20). The map will be padded out " + fillPadding + " blocks beyond the border (default " + defaultPadding + "). Parts of the world which are already fully generated will be " + (fillForceLoad ? "loaded anyway." : "skipped."));
sender.sendMessage(C_HEAD + "This process can take a very long time depending on the world's border size. Also, depending on the chunk processing rate, players will likely experience severe lag for the duration.");
sender.sendMessage(C_DESC + "You should now use " + cmd + "confirm" + C_DESC + " to start the process.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
}
}
sender.sendMessage(C_HEAD + "World generation task is ready for world \"" + fillWorld + "\", attempting to process up to " + fillFrequency + " chunks per second (default 20). The map will be padded out " + fillPadding + " blocks beyond the border (default " + defaultPadding + "). Parts of the world which are already fully generated will be " + (fillForceLoad ? "loaded anyway." : "skipped."));
sender.sendMessage(C_HEAD + "This process can take a very long time depending on the world's border size. Also, depending on the chunk processing rate, players will likely experience severe lag for the duration.");
sender.sendMessage(C_DESC + "You should now use " + cmd + "confirm" + C_DESC + " to start the process.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
}
}
private void fillDefaults() {
fillWorld = "";
fillFrequency = 20;
fillPadding = defaultPadding;
fillForceLoad = false;
}
/* with "view-distance=10" in server.properties on a fast VM test server and "Render Distance: Far" in client,
* hitting border during testing was loading 11+ chunks beyond the border in a couple of directions (10 chunks in
* the other two directions). This could be worse on a more loaded or worse server, so:
*/
private final int defaultPadding = CoordXZ.chunkToBlock(13);
private String fillWorld = "";
private int fillFrequency = 20;
private int fillPadding = defaultPadding;
private boolean fillForceLoad = false;
private void fillDefaults()
{
fillWorld = "";
fillFrequency = 20;
fillPadding = defaultPadding;
fillForceLoad = false;
}
private boolean makeSureFillIsRunning(CommandSender sender)
{
if (Config.fillTask != null && Config.fillTask.valid())
return true;
sendErrorAndHelp(sender, "The world map generation task is not currently running.");
return false;
}
private boolean makeSureFillIsRunning(CommandSender sender) {
if (Config.fillTask != null && Config.fillTask.valid())
return true;
sendErrorAndHelp(sender, "The world map generation task is not currently running.");
return false;
}
}

View File

@ -1,61 +1,50 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdFillautosave extends WBCmd {
public CmdFillautosave() {
name = permission = "fillautosave";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<seconds> - world save interval for Fill.");
helpText = "Default value: 30 seconds.";
}
public class CmdFillautosave extends WBCmd
{
public CmdFillautosave()
{
name = permission = "fillautosave";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
int seconds = Config.FillAutosaveFrequency();
if (seconds == 0) {
sender.sendMessage(C_HEAD + "World autosave frequency during Fill process is set to 0, disabling it.");
sender.sendMessage(C_HEAD + "Note that much progress can be lost this way if there is a bug or crash in " +
"the world generation process from Bukkit or any world generation plugin you use.");
} else {
sender.sendMessage(C_HEAD + "World autosave frequency during Fill process is set to " + seconds + " seconds (rounded to a multiple of 5).");
sender.sendMessage(C_HEAD + "New chunks generated by the Fill process will be forcibly saved to disk " +
"this often to prevent loss of progress due to bugs or crashes in the world generation process.");
}
}
addCmdExample(nameEmphasized() + "<seconds> - world save interval for Fill.");
helpText = "Default value: 30 seconds.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
int seconds = 0;
try {
seconds = Integer.parseInt(params.get(0));
if (seconds < 0)
throw new NumberFormatException();
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The world autosave frequency must be an integer of 0 or higher. Setting to 0 will disable autosaving of the world during the Fill process.");
return;
}
@Override
public void cmdStatus(CommandSender sender)
{
int seconds = Config.FillAutosaveFrequency();
if (seconds == 0)
{
sender.sendMessage(C_HEAD + "World autosave frequency during Fill process is set to 0, disabling it.");
sender.sendMessage(C_HEAD + "Note that much progress can be lost this way if there is a bug or crash in " +
"the world generation process from Bukkit or any world generation plugin you use.");
}
else
{
sender.sendMessage(C_HEAD + "World autosave frequency during Fill process is set to " + seconds + " seconds (rounded to a multiple of 5).");
sender.sendMessage(C_HEAD + "New chunks generated by the Fill process will be forcibly saved to disk " +
"this often to prevent loss of progress due to bugs or crashes in the world generation process.");
}
}
Config.setFillAutosaveFrequency(seconds);
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
int seconds = 0;
try
{
seconds = Integer.parseInt(params.get(0));
if (seconds < 0)
throw new NumberFormatException();
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The world autosave frequency must be an integer of 0 or higher. Setting to 0 will disable autosaving of the world during the Fill process.");
return;
}
Config.setFillAutosaveFrequency(seconds);
if (player != null)
cmdStatus(sender);
}
if (player != null)
cmdStatus(sender);
}
}

View File

@ -1,30 +1,26 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdGetmsg extends WBCmd {
public CmdGetmsg() {
name = permission = "getmsg";
minParams = maxParams = 0;
addCmdExample(nameEmphasized() + "- display border message.");
helpText = "This command simply displays the message shown to players knocked back from the border.";
}
public class CmdGetmsg extends WBCmd
{
public CmdGetmsg()
{
name = permission = "getmsg";
minParams = maxParams = 0;
addCmdExample(nameEmphasized() + "- display border message.");
helpText = "This command simply displays the message shown to players knocked back from the border.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
sender.sendMessage("Border message is currently set to:");
sender.sendMessage(Config.MessageRaw());
sender.sendMessage("Formatted border message:");
sender.sendMessage(Config.Message());
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
sender.sendMessage("Border message is currently set to:");
sender.sendMessage(Config.MessageRaw());
sender.sendMessage("Formatted border message:");
sender.sendMessage(Config.Message());
}
}

View File

@ -1,53 +1,45 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.WorldBorder;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Set;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdHelp extends WBCmd {
public CmdHelp() {
name = permission = "help";
minParams = 0;
maxParams = 10;
public class CmdHelp extends WBCmd
{
public CmdHelp()
{
name = permission = "help";
minParams = 0;
maxParams = 10;
addCmdExample(nameEmphasized() + "[command] - get help on command usage.");
addCmdExample(nameEmphasized() + "[command] - get help on command usage.");
// helpText = "If [command] is specified, info for that particular command will be provided.";
}
}
@Override
public void cmdStatus(CommandSender sender)
{
String commands = WorldBorder.wbCommand.getCommandNames().toString().replace(", ", C_DESC + ", " + C_CMD);
sender.sendMessage(C_HEAD + "Commands: " + C_CMD + commands.substring(1, commands.length() - 1));
sender.sendMessage("Example, for info on \"set\" command: " + cmd(sender) + nameEmphasized() + C_CMD + "set");
sender.sendMessage(C_HEAD + "For a full command example list, simply run the root " + cmd(sender) + C_HEAD + "command by itself with nothing specified.");
}
@Override
public void cmdStatus(CommandSender sender) {
String commands = WorldBorder.wbCommand.getCommandNames().toString().replace(", ", C_DESC + ", " + C_CMD);
sender.sendMessage(C_HEAD + "Commands: " + C_CMD + commands.substring(1, commands.length() - 1));
sender.sendMessage("Example, for info on \"set\" command: " + cmd(sender) + nameEmphasized() + C_CMD + "set");
sender.sendMessage(C_HEAD + "For a full command example list, simply run the root " + cmd(sender) + C_HEAD + "command by itself with nothing specified.");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
if (params.isEmpty())
{
sendCmdHelp(sender);
return;
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
if (params.isEmpty()) {
sendCmdHelp(sender);
return;
}
Set<String> commands = WorldBorder.wbCommand.getCommandNames();
for (String param : params)
{
if (commands.contains(param.toLowerCase()))
{
WorldBorder.wbCommand.subCommands.get(param.toLowerCase()).sendCmdHelp(sender);
return;
}
}
sendErrorAndHelp(sender, "No command recognized.");
}
Set<String> commands = WorldBorder.wbCommand.getCommandNames();
for (String param : params) {
if (commands.contains(param.toLowerCase())) {
WorldBorder.wbCommand.subCommands.get(param.toLowerCase()).sendCmdHelp(sender);
return;
}
}
sendErrorAndHelp(sender, "No command recognized.");
}
}

View File

@ -1,53 +1,45 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdKnockback extends WBCmd {
public CmdKnockback() {
name = permission = "knockback";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<distance> - how far to move the player back.");
helpText = "Default value: 3.0 (blocks). Players who cross the border will be knocked back to this distance inside.";
}
public class CmdKnockback extends WBCmd
{
public CmdKnockback()
{
name = permission = "knockback";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
double kb = Config.KnockBack();
if (kb < 1)
sender.sendMessage(C_HEAD + "Knockback is set to 0, disabling border enforcement.");
else
sender.sendMessage(C_HEAD + "Knockback is set to " + kb + " blocks inside the border.");
}
addCmdExample(nameEmphasized() + "<distance> - how far to move the player back.");
helpText = "Default value: 3.0 (blocks). Players who cross the border will be knocked back to this distance inside.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
double numBlocks = 0.0;
try {
numBlocks = Double.parseDouble(params.get(0));
if (numBlocks < 0.0 || (numBlocks > 0.0 && numBlocks < 1.0))
throw new NumberFormatException();
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The knockback must be a decimal value of at least 1.0, or it can be 0.");
return;
}
@Override
public void cmdStatus(CommandSender sender)
{
double kb = Config.KnockBack();
if (kb < 1)
sender.sendMessage(C_HEAD + "Knockback is set to 0, disabling border enforcement.");
else
sender.sendMessage(C_HEAD + "Knockback is set to " + kb + " blocks inside the border.");
}
Config.setKnockBack(numBlocks);
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
double numBlocks = 0.0;
try
{
numBlocks = Double.parseDouble(params.get(0));
if (numBlocks < 0.0 || (numBlocks > 0.0 && numBlocks < 1.0))
throw new NumberFormatException();
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The knockback must be a decimal value of at least 1.0, or it can be 0.");
return;
}
Config.setKnockBack(numBlocks);
if (player != null)
cmdStatus(sender);
}
if (player != null)
cmdStatus(sender);
}
}

View File

@ -1,42 +1,36 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Set;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdList extends WBCmd {
public CmdList() {
name = permission = "list";
minParams = maxParams = 0;
addCmdExample(nameEmphasized() + "- show border information for all worlds.");
helpText = "This command will list full information for every border you have set including position, " +
"radius, and shape. The default border shape will also be indicated.";
}
public class CmdList extends WBCmd
{
public CmdList()
{
name = permission = "list";
minParams = maxParams = 0;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
sender.sendMessage("Default border shape for all worlds is \"" + Config.ShapeName() + "\".");
addCmdExample(nameEmphasized() + "- show border information for all worlds.");
helpText = "This command will list full information for every border you have set including position, " +
"radius, and shape. The default border shape will also be indicated.";
}
Set<String> list = Config.BorderDescriptions();
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
sender.sendMessage("Default border shape for all worlds is \"" + Config.ShapeName() + "\".");
if (list.isEmpty()) {
sender.sendMessage("There are no borders currently set.");
return;
}
Set<String> list = Config.BorderDescriptions();
if (list.isEmpty())
{
sender.sendMessage("There are no borders currently set.");
return;
}
for(String borderDesc : list)
{
sender.sendMessage(borderDesc);
}
}
for (String borderDesc : list) {
sender.sendMessage(borderDesc);
}
}
}

View File

@ -1,41 +1,35 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdPortal extends WBCmd {
public CmdPortal() {
name = permission = "portal";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - turn portal redirection on or off.");
helpText = "Default value: on. This feature monitors new portal creation and changes the target new portal " +
"location if it is outside of the border. Try disabling this if you have problems with other plugins " +
"related to portals.";
}
public class CmdPortal extends WBCmd
{
public CmdPortal()
{
name = permission = "portal";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "Portal redirection is " + enabledColored(Config.portalRedirection()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - turn portal redirection on or off.");
helpText = "Default value: on. This feature monitors new portal creation and changes the target new portal " +
"location if it is outside of the border. Try disabling this if you have problems with other plugins " +
"related to portals.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setPortalRedirection(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "Portal redirection is " + enabledColored(Config.portalRedirection()) + C_HEAD + ".");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setPortalRedirection(strAsBool(params.get(0)));
if (player != null)
{
Config.log((Config.portalRedirection() ? "Enabled" : "Disabled") + " portal redirection at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
if (player != null) {
Config.log((Config.portalRedirection() ? "Enabled" : "Disabled") + " portal redirection at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
}

View File

@ -1,37 +1,33 @@
package com.wimbli.WorldBorder.cmd;
import java.util.List;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.Config;
import java.util.List;
public class CmdPreventPlace extends WBCmd {
public CmdPreventPlace() {
name = permission = "preventblockplace";
minParams = maxParams = 1;
public CmdPreventPlace() {
name = permission = "preventblockplace";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - stop block placement past border.");
helpText = "Default value: off. When enabled, this setting will prevent players from placing blocks outside the world's border.";
}
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "Prevention of block placement outside the border is " + enabledColored(Config.preventBlockPlace()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - stop block placement past border.");
helpText = "Default value: off. When enabled, this setting will prevent players from placing blocks outside the world's border.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setPreventBlockPlace(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "Prevention of block placement outside the border is " + enabledColored(Config.preventBlockPlace()) + C_HEAD + ".");
}
if (player != null)
{
Config.log((Config.preventBlockPlace() ? "Enabled" : "Disabled") + " preventblockplace at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setPreventBlockPlace(strAsBool(params.get(0)));
if (player != null) {
Config.log((Config.preventBlockPlace() ? "Enabled" : "Disabled") + " preventblockplace at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
}

View File

@ -8,29 +8,26 @@ import java.util.List;
public class CmdPreventSpawn extends WBCmd {
public CmdPreventSpawn() {
name = permission = "preventmobspawn";
minParams = maxParams = 1;
public CmdPreventSpawn() {
name = permission = "preventmobspawn";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - stop mob spawning past border.");
helpText = "Default value: off. When enabled, this setting will prevent mobs from naturally spawning outside the world's border.";
}
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "Prevention of mob spawning outside the border is " + enabledColored(Config.preventMobSpawn()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - stop mob spawning past border.");
helpText = "Default value: off. When enabled, this setting will prevent mobs from naturally spawning outside the world's border.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setPreventMobSpawn(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "Prevention of mob spawning outside the border is " + enabledColored(Config.preventMobSpawn()) + C_HEAD + ".");
}
if (player != null)
{
Config.log((Config.preventMobSpawn() ? "Enabled" : "Disabled") + " preventmobspawn at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setPreventMobSpawn(strAsBool(params.get(0)));
if (player != null) {
Config.log((Config.preventMobSpawn() ? "Enabled" : "Disabled") + " preventmobspawn at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
}

View File

@ -1,91 +1,74 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.BorderData;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdRadius extends WBCmd {
public CmdRadius() {
name = permission = "radius";
hasWorldNameInput = true;
minParams = 1;
maxParams = 2;
addCmdExample(nameEmphasizedW() + "<radiusX> [radiusZ] - change radius.");
helpText = "Using this command you can adjust the radius of an existing border. If [radiusZ] is not " +
"specified, the radiusX value will be used for both. You can also optionally specify + or - at the start " +
"of <radiusX> and [radiusZ] to increase or decrease the existing radius rather than setting a new value.";
}
public class CmdRadius extends WBCmd
{
public CmdRadius()
{
name = permission = "radius";
hasWorldNameInput = true;
minParams = 1;
maxParams = 2;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
if (worldName == null)
worldName = player.getWorld().getName();
addCmdExample(nameEmphasizedW() + "<radiusX> [radiusZ] - change radius.");
helpText = "Using this command you can adjust the radius of an existing border. If [radiusZ] is not " +
"specified, the radiusX value will be used for both. You can also optionally specify + or - at the start " +
"of <radiusX> and [radiusZ] to increase or decrease the existing radius rather than setting a new value.";
}
BorderData border = Config.Border(worldName);
if (border == null) {
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") must first have a border set normally.");
return;
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
if (worldName == null)
worldName = player.getWorld().getName();
double x = border.getX();
double z = border.getZ();
int radiusX;
int radiusZ;
try {
if (params.get(0).startsWith("+")) {
// Add to the current radius
radiusX = border.getRadiusX();
radiusX += Integer.parseInt(params.get(0).substring(1));
} else if (params.get(0).startsWith("-")) {
// Subtract from the current radius
radiusX = border.getRadiusX();
radiusX -= Integer.parseInt(params.get(0).substring(1));
} else
radiusX = Integer.parseInt(params.get(0));
BorderData border = Config.Border(worldName);
if (border == null)
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") must first have a border set normally.");
return;
}
if (params.size() == 2) {
if (params.get(1).startsWith("+")) {
// Add to the current radius
radiusZ = border.getRadiusZ();
radiusZ += Integer.parseInt(params.get(1).substring(1));
} else if (params.get(1).startsWith("-")) {
// Subtract from the current radius
radiusZ = border.getRadiusZ();
radiusZ -= Integer.parseInt(params.get(1).substring(1));
} else
radiusZ = Integer.parseInt(params.get(1));
} else
radiusZ = radiusX;
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The radius value(s) must be integers.");
return;
}
double x = border.getX();
double z = border.getZ();
int radiusX;
int radiusZ;
try
{
if (params.get(0).startsWith("+"))
{
// Add to the current radius
radiusX = border.getRadiusX();
radiusX += Integer.parseInt(params.get(0).substring(1));
}
else if(params.get(0).startsWith("-"))
{
// Subtract from the current radius
radiusX = border.getRadiusX();
radiusX -= Integer.parseInt(params.get(0).substring(1));
}
else
radiusX = Integer.parseInt(params.get(0));
Config.setBorder(worldName, radiusX, radiusZ, x, z);
if (params.size() == 2)
{
if (params.get(1).startsWith("+"))
{
// Add to the current radius
radiusZ = border.getRadiusZ();
radiusZ += Integer.parseInt(params.get(1).substring(1));
}
else if(params.get(1).startsWith("-"))
{
// Subtract from the current radius
radiusZ = border.getRadiusZ();
radiusZ -= Integer.parseInt(params.get(1).substring(1));
}
else
radiusZ = Integer.parseInt(params.get(1));
}
else
radiusZ = radiusX;
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The radius value(s) must be integers.");
return;
}
Config.setBorder(worldName, radiusX, radiusZ, x, z);
if (player != null)
sender.sendMessage("Radius has been set. " + Config.BorderDescription(worldName));
}
if (player != null)
sender.sendMessage("Radius has been set. " + Config.BorderDescription(worldName));
}
}

View File

@ -1,34 +1,31 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import com.wimbli.WorldBorder.WorldBorder;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdReload extends WBCmd {
public CmdReload() {
name = permission = "reload";
minParams = maxParams = 0;
addCmdExample(nameEmphasized() + "- re-load data from config.yml.");
helpText = "If you make manual changes to config.yml while the server is running, you can use this command " +
"to make WorldBorder load the changes without needing to restart the server.";
}
public class CmdReload extends WBCmd
{
public CmdReload()
{
name = permission = "reload";
minParams = maxParams = 0;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
if (player != null)
Config.log("Reloading config file at the command of player \"" + player.getName() + "\".");
addCmdExample(nameEmphasized() + "- re-load data from config.yml.");
helpText = "If you make manual changes to config.yml while the server is running, you can use this command " +
"to make WorldBorder load the changes without needing to restart the server.";
}
Config.load(WorldBorder.plugin, true);
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
if (player != null)
Config.log("Reloading config file at the command of player \"" + player.getName() + "\".");
Config.load(WorldBorder.plugin, true);
if (player != null)
sender.sendMessage("WorldBorder configuration reloaded.");
}
if (player != null)
sender.sendMessage("WorldBorder configuration reloaded.");
}
}

View File

@ -1,59 +1,50 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdRemount extends WBCmd {
public CmdRemount() {
name = permission = "remount";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<amount> - player remount delay after knockback.");
helpText = "Default value: 0 (disabled). If set higher than 0, WorldBorder will attempt to re-mount players who " +
"are knocked back from the border while riding something after this many server ticks. This setting can " +
"cause really nasty glitches if enabled and set too low due to CraftBukkit teleportation problems.";
}
public class CmdRemount extends WBCmd
{
public CmdRemount()
{
name = permission = "remount";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
int delay = Config.RemountTicks();
if (delay == 0)
sender.sendMessage(C_HEAD + "Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle.");
else {
sender.sendMessage(C_HEAD + "Remount delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms / " + (((double) delay * 50.0) / 1000.0) + " seconds. Setting to 0 would disable remounting.");
if (delay < 10)
sender.sendMessage(C_ERR + "WARNING:" + C_DESC + " setting this to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches.");
}
}
addCmdExample(nameEmphasized() + "<amount> - player remount delay after knockback.");
helpText = "Default value: 0 (disabled). If set higher than 0, WorldBorder will attempt to re-mount players who " +
"are knocked back from the border while riding something after this many server ticks. This setting can " +
"cause really nasty glitches if enabled and set too low due to CraftBukkit teleportation problems.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
int delay = 0;
try {
delay = Integer.parseInt(params.get(0));
if (delay < 0)
throw new NumberFormatException();
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The remount delay must be an integer of 0 or higher. Setting to 0 will disable remounting.");
return;
}
@Override
public void cmdStatus(CommandSender sender)
{
int delay = Config.RemountTicks();
if (delay == 0)
sender.sendMessage(C_HEAD + "Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle.");
else
{
sender.sendMessage(C_HEAD + "Remount delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms / " + (((double)delay * 50.0) / 1000.0) + " seconds. Setting to 0 would disable remounting.");
if (delay < 10)
sender.sendMessage(C_ERR + "WARNING:" + C_DESC + " setting this to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches.");
}
}
Config.setRemountTicks(delay);
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
int delay = 0;
try
{
delay = Integer.parseInt(params.get(0));
if (delay < 0)
throw new NumberFormatException();
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The remount delay must be an integer of 0 or higher. Setting to 0 will disable remounting.");
return;
}
Config.setRemountTicks(delay);
if (player != null)
cmdStatus(sender);
}
if (player != null)
cmdStatus(sender);
}
}

View File

@ -1,145 +1,119 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.Location;
import org.bukkit.World;
import com.wimbli.WorldBorder.*;
public class CmdSet extends WBCmd {
public CmdSet() {
name = permission = "set";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 1;
maxParams = 4;
addCmdExample(nameEmphasizedW() + "<radiusX> [radiusZ] <x> <z> - use x/z coords.");
addCmdExample(nameEmphasizedW() + "<radiusX> [radiusZ] ^spawn - use spawn point.");
addCmdExample(nameEmphasized() + "<radiusX> [radiusZ] - set border, centered on you.", true, false, true);
addCmdExample(nameEmphasized() + "<radiusX> [radiusZ] ^player <name> - center on player.");
helpText = "Set a border for a world, with several options for defining the center location. [world] is " +
"optional for players and defaults to the world the player is in. If [radiusZ] is not specified, the " +
"radiusX value will be used for both. The <x> and <z> coordinates can be decimal values (ex. 1.234).";
}
public class CmdSet extends WBCmd
{
public CmdSet()
{
name = permission = "set";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 1;
maxParams = 4;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
// passsing a single parameter (radiusX) is only acceptable from player
if ((params.size() == 1) && player == null) {
sendErrorAndHelp(sender, "You have not provided a sufficient number of parameters.");
return;
}
addCmdExample(nameEmphasizedW() + "<radiusX> [radiusZ] <x> <z> - use x/z coords.");
addCmdExample(nameEmphasizedW() + "<radiusX> [radiusZ] ^spawn - use spawn point.");
addCmdExample(nameEmphasized() + "<radiusX> [radiusZ] - set border, centered on you.", true, false, true);
addCmdExample(nameEmphasized() + "<radiusX> [radiusZ] ^player <name> - center on player.");
helpText = "Set a border for a world, with several options for defining the center location. [world] is " +
"optional for players and defaults to the world the player is in. If [radiusZ] is not specified, the " +
"radiusX value will be used for both. The <x> and <z> coordinates can be decimal values (ex. 1.234).";
}
// "set" command from player or console, world specified
if (worldName != null) {
if (params.size() == 2 && !params.get(params.size() - 1).equalsIgnoreCase("spawn")) { // command can only be this short if "spawn" is specified rather than x + z or player name
sendErrorAndHelp(sender, "You have not provided a sufficient number of arguments.");
return;
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
// passsing a single parameter (radiusX) is only acceptable from player
if ((params.size() == 1) && player == null)
{
sendErrorAndHelp(sender, "You have not provided a sufficient number of parameters.");
return;
}
World world = sender.getServer().getWorld(worldName);
if (world == null) {
if (params.get(params.size() - 1).equalsIgnoreCase("spawn")) {
sendErrorAndHelp(sender, "The world you specified (\"" + worldName + "\") could not be found on the server, so the spawn point cannot be determined.");
return;
}
sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway.");
}
}
// "set" command from player using current world since it isn't specified, or allowed from console only if player name is specified
else {
if (player == null) {
if (!params.get(params.size() - 2).equalsIgnoreCase("player")) { // command can only be called by console without world specified if player is specified instead
sendErrorAndHelp(sender, "You must specify a world name from console if not specifying a player name.");
return;
}
player = Bukkit.getPlayer(params.get(params.size() - 1));
if (player == null || !player.isOnline()) {
sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online.");
return;
}
}
worldName = player.getWorld().getName();
}
// "set" command from player or console, world specified
if (worldName != null)
{
if (params.size() == 2 && ! params.get(params.size() - 1).equalsIgnoreCase("spawn"))
{ // command can only be this short if "spawn" is specified rather than x + z or player name
sendErrorAndHelp(sender, "You have not provided a sufficient number of arguments.");
return;
}
int radiusX, radiusZ;
double x, z;
int radiusCount = params.size();
World world = sender.getServer().getWorld(worldName);
if (world == null)
{
if (params.get(params.size() - 1).equalsIgnoreCase("spawn"))
{
sendErrorAndHelp(sender, "The world you specified (\"" + worldName + "\") could not be found on the server, so the spawn point cannot be determined.");
return;
}
sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway.");
}
}
// "set" command from player using current world since it isn't specified, or allowed from console only if player name is specified
else
{
if (player == null)
{
if (! params.get(params.size() - 2).equalsIgnoreCase("player"))
{ // command can only be called by console without world specified if player is specified instead
sendErrorAndHelp(sender, "You must specify a world name from console if not specifying a player name.");
return;
}
player = Bukkit.getPlayer(params.get(params.size() - 1));
if (player == null || ! player.isOnline())
{
sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online.");
return;
}
}
worldName = player.getWorld().getName();
}
try {
if (params.get(params.size() - 1).equalsIgnoreCase("spawn")) { // "spawn" specified for x/z coordinates
Location loc = sender.getServer().getWorld(worldName).getSpawnLocation();
x = loc.getX();
z = loc.getZ();
radiusCount -= 1;
} else if (params.size() > 2 && params.get(params.size() - 2).equalsIgnoreCase("player")) { // player name specified for x/z coordinates
Player playerT = Bukkit.getPlayer(params.get(params.size() - 1));
if (playerT == null || !playerT.isOnline()) {
sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online.");
return;
}
worldName = playerT.getWorld().getName();
x = playerT.getLocation().getX();
z = playerT.getLocation().getZ();
radiusCount -= 2;
} else {
if (player == null || radiusCount > 2) { // x and z specified
x = Double.parseDouble(params.get(params.size() - 2));
z = Double.parseDouble(params.get(params.size() - 1));
radiusCount -= 2;
} else { // using coordinates of command sender (player)
x = player.getLocation().getX();
z = player.getLocation().getZ();
}
}
int radiusX, radiusZ;
double x, z;
int radiusCount = params.size();
radiusX = Integer.parseInt(params.get(0));
if (radiusCount < 2)
radiusZ = radiusX;
else
radiusZ = Integer.parseInt(params.get(1));
try
{
if (params.get(params.size() - 1).equalsIgnoreCase("spawn"))
{ // "spawn" specified for x/z coordinates
Location loc = sender.getServer().getWorld(worldName).getSpawnLocation();
x = loc.getX();
z = loc.getZ();
radiusCount -= 1;
}
else if (params.size() > 2 && params.get(params.size() - 2).equalsIgnoreCase("player"))
{ // player name specified for x/z coordinates
Player playerT = Bukkit.getPlayer(params.get(params.size() - 1));
if (playerT == null || ! playerT.isOnline())
{
sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online.");
return;
}
worldName = playerT.getWorld().getName();
x = playerT.getLocation().getX();
z = playerT.getLocation().getZ();
radiusCount -= 2;
}
else
{
if (player == null || radiusCount > 2)
{ // x and z specified
x = Double.parseDouble(params.get(params.size() - 2));
z = Double.parseDouble(params.get(params.size() - 1));
radiusCount -= 2;
}
else
{ // using coordinates of command sender (player)
x = player.getLocation().getX();
z = player.getLocation().getZ();
}
}
if (radiusX < Config.KnockBack() || radiusZ < Config.KnockBack()) {
sendErrorAndHelp(sender, "Radius value(s) must be more than the knockback distance.");
return;
}
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "Radius value(s) must be integers and x and z values must be numerical.");
return;
}
radiusX = Integer.parseInt(params.get(0));
if (radiusCount < 2)
radiusZ = radiusX;
else
radiusZ = Integer.parseInt(params.get(1));
if (radiusX < Config.KnockBack() || radiusZ < Config.KnockBack())
{
sendErrorAndHelp(sender, "Radius value(s) must be more than the knockback distance.");
return;
}
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "Radius value(s) must be integers and x and z values must be numerical.");
return;
}
Config.setBorder(worldName, radiusX, radiusZ, x, z);
sender.sendMessage("Border has been set. " + Config.BorderDescription(worldName));
}
Config.setBorder(worldName, radiusX, radiusZ, x, z);
sender.sendMessage("Border has been set. " + Config.BorderDescription(worldName));
}
}

View File

@ -1,58 +1,48 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.World;
import com.wimbli.WorldBorder.*;
public class CmdSetcorners extends WBCmd {
public CmdSetcorners() {
name = "setcorners";
permission = "set";
hasWorldNameInput = true;
minParams = maxParams = 4;
addCmdExample(nameEmphasizedW() + "<x1> <z1> <x2> <z2> - corner coords.");
helpText = "This is an alternate way to set a border, by specifying the X and Z coordinates of two opposite " +
"corners of the border area ((x1, z1) to (x2, z2)). [world] is optional for players and defaults to the " +
"world the player is in.";
}
public class CmdSetcorners extends WBCmd
{
public CmdSetcorners()
{
name = "setcorners";
permission = "set";
hasWorldNameInput = true;
minParams = maxParams = 4;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
if (worldName == null) {
worldName = player.getWorld().getName();
} else {
World worldTest = sender.getServer().getWorld(worldName);
if (worldTest == null)
sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway.");
}
addCmdExample(nameEmphasizedW() + "<x1> <z1> <x2> <z2> - corner coords.");
helpText = "This is an alternate way to set a border, by specifying the X and Z coordinates of two opposite " +
"corners of the border area ((x1, z1) to (x2, z2)). [world] is optional for players and defaults to the " +
"world the player is in.";
}
try {
double x1 = Double.parseDouble(params.get(0));
double z1 = Double.parseDouble(params.get(1));
double x2 = Double.parseDouble(params.get(2));
double z2 = Double.parseDouble(params.get(3));
Config.setBorderCorners(worldName, x1, z1, x2, z2);
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The x1, z1, x2, and z2 coordinate values must be numerical.");
return;
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
if (worldName == null)
{
worldName = player.getWorld().getName();
}
else
{
World worldTest = sender.getServer().getWorld(worldName);
if (worldTest == null)
sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway.");
}
try
{
double x1 = Double.parseDouble(params.get(0));
double z1 = Double.parseDouble(params.get(1));
double x2 = Double.parseDouble(params.get(2));
double z2 = Double.parseDouble(params.get(3));
Config.setBorderCorners(worldName, x1, z1, x2, z2);
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The x1, z1, x2, and z2 coordinate values must be numerical.");
return;
}
if(player != null)
sender.sendMessage("Border has been set. " + Config.BorderDescription(worldName));
}
if (player != null)
sender.sendMessage("Border has been set. " + Config.BorderDescription(worldName));
}
}

View File

@ -1,48 +1,42 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdSetmsg extends WBCmd {
public CmdSetmsg() {
name = permission = "setmsg";
minParams = 1;
addCmdExample(nameEmphasized() + "<text> - set border message.");
helpText = "Default value: \"&cYou have reached the edge of this world.\". This command lets you set the message shown to players who are knocked back from the border.";
}
public class CmdSetmsg extends WBCmd
{
public CmdSetmsg()
{
name = permission = "setmsg";
minParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "Border message is set to:");
sender.sendMessage(Config.MessageRaw());
sender.sendMessage(C_HEAD + "Formatted border message:");
sender.sendMessage(Config.Message());
}
addCmdExample(nameEmphasized() + "<text> - set border message.");
helpText = "Default value: \"&cYou have reached the edge of this world.\". This command lets you set the message shown to players who are knocked back from the border.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
StringBuilder message = new StringBuilder();
boolean first = true;
for (String param : params) {
if (!first)
message.append(" ");
message.append(param);
first = false;
}
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "Border message is set to:");
sender.sendMessage(Config.MessageRaw());
sender.sendMessage(C_HEAD + "Formatted border message:");
sender.sendMessage(Config.Message());
}
Config.setMessage(message.toString());
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
StringBuilder message = new StringBuilder();
boolean first = true;
for (String param : params)
{
if (!first)
message.append(" ");
message.append(param);
first = false;
}
Config.setMessage(message.toString());
cmdStatus(sender);
}
cmdStatus(sender);
}
}

View File

@ -1,49 +1,43 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdShape extends WBCmd {
public CmdShape() {
name = permission = "shape";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<round|square> - set the default border shape.");
addCmdExample(nameEmphasized() + "<elliptic|rectangular> - same as above.");
helpText = "Default value: round/elliptic. The default border shape will be used on all worlds which don't " +
"have an individual shape set using the " + commandEmphasized("wshape") + C_DESC + "command. Elliptic " +
"and round work the same, as rectangular and square do. The difference is down to whether the X and Z " +
"radius are the same.";
}
public class CmdShape extends WBCmd
{
public CmdShape()
{
name = permission = "shape";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "The default border shape for all worlds is currently set to \"" + Config.ShapeName() + "\".");
}
addCmdExample(nameEmphasized() + "<round|square> - set the default border shape.");
addCmdExample(nameEmphasized() + "<elliptic|rectangular> - same as above.");
helpText = "Default value: round/elliptic. The default border shape will be used on all worlds which don't " +
"have an individual shape set using the " + commandEmphasized("wshape") + C_DESC + "command. Elliptic " +
"and round work the same, as rectangular and square do. The difference is down to whether the X and Z " +
"radius are the same.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
String shape = params.get(0).toLowerCase();
if (shape.equals("rectangular") || shape.equals("square"))
Config.setShape(false);
else if (shape.equals("elliptic") || shape.equals("round"))
Config.setShape(true);
else {
sendErrorAndHelp(sender, "You must specify one of the 4 valid shape names below.");
return;
}
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "The default border shape for all worlds is currently set to \"" + Config.ShapeName() + "\".");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
String shape = params.get(0).toLowerCase();
if (shape.equals("rectangular") || shape.equals("square"))
Config.setShape(false);
else if (shape.equals("elliptic") || shape.equals("round"))
Config.setShape(true);
else
{
sendErrorAndHelp(sender, "You must specify one of the 4 valid shape names below.");
return;
}
if (player != null)
cmdStatus(sender);
}
if (player != null)
cmdStatus(sender);
}
}

View File

@ -1,175 +1,152 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import com.wimbli.WorldBorder.CoordXZ;
import com.wimbli.WorldBorder.WorldBorder;
import com.wimbli.WorldBorder.WorldTrimTask;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdTrim extends WBCmd {
/* with "view-distance=10" in server.properties on a fast VM test server and "Render Distance: Far" in client,
* hitting border during testing was loading 11+ chunks beyond the border in a couple of directions (10 chunks in
* the other two directions). This could be worse on a more loaded or worse server, so:
*/
private final int defaultPadding = CoordXZ.chunkToBlock(13);
private String trimWorld = "";
private int trimFrequency = 5000;
private int trimPadding = defaultPadding;
public CmdTrim() {
name = permission = "trim";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 0;
maxParams = 2;
public class CmdTrim extends WBCmd
{
public CmdTrim()
{
name = permission = "trim";
hasWorldNameInput = true;
consoleRequiresWorldName = false;
minParams = 0;
maxParams = 2;
addCmdExample(nameEmphasizedW() + "[freq] [pad] - trim world outside of border.");
helpText = "This command will remove chunks which are outside the world's border. [freq] is the frequency " +
"of chunks per second that will be checked (default 5000). [pad] is the number of blocks padding kept " +
"beyond the border itself (default 208, to cover player visual range).";
}
addCmdExample(nameEmphasizedW() + "[freq] [pad] - trim world outside of border.");
helpText = "This command will remove chunks which are outside the world's border. [freq] is the frequency " +
"of chunks per second that will be checked (default 5000). [pad] is the number of blocks padding kept " +
"beyond the border itself (default 208, to cover player visual range).";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
boolean confirm = false;
// check for "cancel", "pause", or "confirm"
if (params.size() >= 1) {
String check = params.get(0).toLowerCase();
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
boolean confirm = false;
// check for "cancel", "pause", or "confirm"
if (params.size() >= 1)
{
String check = params.get(0).toLowerCase();
if (check.equals("cancel") || check.equals("stop")) {
if (!makeSureTrimIsRunning(sender))
return;
sender.sendMessage(C_HEAD + "Cancelling the world map trimming task.");
trimDefaults();
Config.StopTrimTask();
return;
} else if (check.equals("pause")) {
if (!makeSureTrimIsRunning(sender))
return;
Config.trimTask.pause();
sender.sendMessage(C_HEAD + "The world map trimming task is now " + (Config.trimTask.isPaused() ? "" : "un") + "paused.");
return;
}
if (check.equals("cancel") || check.equals("stop"))
{
if (!makeSureTrimIsRunning(sender))
return;
sender.sendMessage(C_HEAD + "Cancelling the world map trimming task.");
trimDefaults();
Config.StopTrimTask();
return;
}
else if (check.equals("pause"))
{
if (!makeSureTrimIsRunning(sender))
return;
Config.trimTask.pause();
sender.sendMessage(C_HEAD + "The world map trimming task is now " + (Config.trimTask.isPaused() ? "" : "un") + "paused.");
return;
}
confirm = check.equals("confirm");
}
confirm = check.equals("confirm");
}
// if not just confirming, make sure a world name is available
if (worldName == null && !confirm) {
if (player != null)
worldName = player.getWorld().getName();
else {
sendErrorAndHelp(sender, "You must specify a world!");
return;
}
}
// if not just confirming, make sure a world name is available
if (worldName == null && !confirm)
{
if (player != null)
worldName = player.getWorld().getName();
else
{
sendErrorAndHelp(sender, "You must specify a world!");
return;
}
}
// colorized "/wb trim "
String cmd = cmd(sender) + nameEmphasized() + C_CMD;
// colorized "/wb trim "
String cmd = cmd(sender) + nameEmphasized() + C_CMD;
// make sure Trim isn't already running
if (Config.trimTask != null && Config.trimTask.valid()) {
sender.sendMessage(C_ERR + "The world map trimming task is already running.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
return;
}
// make sure Trim isn't already running
if (Config.trimTask != null && Config.trimTask.valid())
{
sender.sendMessage(C_ERR + "The world map trimming task is already running.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
return;
}
// set frequency and/or padding if those were specified
try {
if (params.size() >= 1 && !confirm)
trimFrequency = Math.abs(Integer.parseInt(params.get(0)));
if (params.size() >= 2 && !confirm)
trimPadding = Math.abs(Integer.parseInt(params.get(1)));
} catch (NumberFormatException ex) {
sendErrorAndHelp(sender, "The frequency and padding values must be integers.");
trimDefaults();
return;
}
if (trimFrequency <= 0) {
sendErrorAndHelp(sender, "The frequency value must be greater than zero.");
trimDefaults();
return;
}
// set frequency and/or padding if those were specified
try
{
if (params.size() >= 1 && !confirm)
trimFrequency = Math.abs(Integer.parseInt(params.get(0)));
if (params.size() >= 2 && !confirm)
trimPadding = Math.abs(Integer.parseInt(params.get(1)));
}
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The frequency and padding values must be integers.");
trimDefaults();
return;
}
if (trimFrequency <= 0)
{
sendErrorAndHelp(sender, "The frequency value must be greater than zero.");
trimDefaults();
return;
}
// set world if it was specified
if (worldName != null)
trimWorld = worldName;
// set world if it was specified
if (worldName != null)
trimWorld = worldName;
if (confirm) { // command confirmed, go ahead with it
if (trimWorld.isEmpty()) {
sendErrorAndHelp(sender, "You must first use this command successfully without confirming.");
return;
}
if (confirm)
{ // command confirmed, go ahead with it
if (trimWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first use this command successfully without confirming.");
return;
}
if (player != null)
Config.log("Trimming world beyond border at the command of player \"" + player.getName() + "\".");
if (player != null)
Config.log("Trimming world beyond border at the command of player \"" + player.getName() + "\".");
int ticks = 1, repeats = 1;
if (trimFrequency > 20)
repeats = trimFrequency / 20;
else
ticks = 20 / trimFrequency;
int ticks = 1, repeats = 1;
if (trimFrequency > 20)
repeats = trimFrequency / 20;
else
ticks = 20 / trimFrequency;
Config.trimTask = new WorldTrimTask(Bukkit.getServer(), player, trimWorld, trimPadding, repeats);
if (Config.trimTask.valid()) {
int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.trimTask, ticks, ticks);
Config.trimTask.setTaskID(task);
sender.sendMessage("WorldBorder map trimming task for world \"" + trimWorld + "\" started.");
} else
sender.sendMessage(C_ERR + "The world map trimming task failed to start.");
Config.trimTask = new WorldTrimTask(Bukkit.getServer(), player, trimWorld, trimPadding, repeats);
if (Config.trimTask.valid())
{
int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.trimTask, ticks, ticks);
Config.trimTask.setTaskID(task);
sender.sendMessage("WorldBorder map trimming task for world \"" + trimWorld + "\" started.");
}
else
sender.sendMessage(C_ERR + "The world map trimming task failed to start.");
trimDefaults();
} else {
if (trimWorld.isEmpty()) {
sendErrorAndHelp(sender, "You must first specify a valid world.");
return;
}
trimDefaults();
}
else
{
if (trimWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first specify a valid world.");
return;
}
sender.sendMessage(C_HEAD + "World trimming task is ready for world \"" + trimWorld + "\", attempting to process up to " + trimFrequency + " chunks per second (default 20). The map will be trimmed past " + trimPadding + " blocks beyond the border (default " + defaultPadding + ").");
sender.sendMessage(C_HEAD + "This process can take a very long time depending on the world's overall size. Also, depending on the chunk processing rate, players may experience lag for the duration.");
sender.sendMessage(C_DESC + "You should now use " + cmd + "confirm" + C_DESC + " to start the process.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
}
}
sender.sendMessage(C_HEAD + "World trimming task is ready for world \"" + trimWorld + "\", attempting to process up to " + trimFrequency + " chunks per second (default 20). The map will be trimmed past " + trimPadding + " blocks beyond the border (default " + defaultPadding + ").");
sender.sendMessage(C_HEAD + "This process can take a very long time depending on the world's overall size. Also, depending on the chunk processing rate, players may experience lag for the duration.");
sender.sendMessage(C_DESC + "You should now use " + cmd + "confirm" + C_DESC + " to start the process.");
sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
}
}
private void trimDefaults() {
trimWorld = "";
trimFrequency = 5000;
trimPadding = defaultPadding;
}
/* with "view-distance=10" in server.properties on a fast VM test server and "Render Distance: Far" in client,
* hitting border during testing was loading 11+ chunks beyond the border in a couple of directions (10 chunks in
* the other two directions). This could be worse on a more loaded or worse server, so:
*/
private final int defaultPadding = CoordXZ.chunkToBlock(13);
private String trimWorld = "";
private int trimFrequency = 5000;
private int trimPadding = defaultPadding;
private void trimDefaults()
{
trimWorld = "";
trimFrequency = 5000;
trimPadding = defaultPadding;
}
private boolean makeSureTrimIsRunning(CommandSender sender)
{
if (Config.trimTask != null && Config.trimTask.valid())
return true;
sendErrorAndHelp(sender, "The world map trimming task is not currently running.");
return false;
}
private boolean makeSureTrimIsRunning(CommandSender sender) {
if (Config.trimTask != null && Config.trimTask.valid())
return true;
sendErrorAndHelp(sender, "The world map trimming task is not currently running.");
return false;
}
}

View File

@ -1,40 +1,34 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdWhoosh extends WBCmd {
public CmdWhoosh() {
name = permission = "whoosh";
minParams = maxParams = 1;
addCmdExample(nameEmphasized() + "<on|off> - turn knockback effect on or off.");
helpText = "Default value: on. This will show a particle effect and play a sound where a player is knocked " +
"back from the border.";
}
public class CmdWhoosh extends WBCmd
{
public CmdWhoosh()
{
name = permission = "whoosh";
minParams = maxParams = 1;
@Override
public void cmdStatus(CommandSender sender) {
sender.sendMessage(C_HEAD + "\"Whoosh\" knockback effect is " + enabledColored(Config.whooshEffect()) + C_HEAD + ".");
}
addCmdExample(nameEmphasized() + "<on|off> - turn knockback effect on or off.");
helpText = "Default value: on. This will show a particle effect and play a sound where a player is knocked " +
"back from the border.";
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
Config.setWhooshEffect(strAsBool(params.get(0)));
@Override
public void cmdStatus(CommandSender sender)
{
sender.sendMessage(C_HEAD + "\"Whoosh\" knockback effect is " + enabledColored(Config.whooshEffect()) + C_HEAD + ".");
}
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
Config.setWhooshEffect(strAsBool(params.get(0)));
if (player != null)
{
Config.log((Config.whooshEffect() ? "Enabled" : "Disabled") + " \"whoosh\" knockback effect at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
if (player != null) {
Config.log((Config.whooshEffect() ? "Enabled" : "Disabled") + " \"whoosh\" knockback effect at the command of player \"" + player.getName() + "\".");
cmdStatus(sender);
}
}
}

View File

@ -1,61 +1,54 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.BorderData;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdWrap extends WBCmd {
public CmdWrap() {
name = permission = "wrap";
minParams = 1;
maxParams = 2;
addCmdExample(nameEmphasized() + "{world} <on|off> - can make border crossings wrap.");
helpText = "When border wrapping is enabled for a world, players will be sent around to the opposite edge " +
"of the border when they cross it instead of being knocked back. [world] is optional for players and " +
"defaults to the world the player is in.";
}
public class CmdWrap extends WBCmd
{
public CmdWrap()
{
name = permission = "wrap";
minParams = 1;
maxParams = 2;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
if (player == null && params.size() == 1) {
sendErrorAndHelp(sender, "When running this command from console, you must specify a world.");
return;
}
addCmdExample(nameEmphasized() + "{world} <on|off> - can make border crossings wrap.");
helpText = "When border wrapping is enabled for a world, players will be sent around to the opposite edge " +
"of the border when they cross it instead of being knocked back. [world] is optional for players and " +
"defaults to the world the player is in.";
}
boolean wrap = false;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
if (player == null && params.size() == 1)
{
sendErrorAndHelp(sender, "When running this command from console, you must specify a world.");
return;
}
// world and wrap on/off specified
if (params.size() == 2) {
worldName = params.get(0);
wrap = strAsBool(params.get(1));
}
// no world specified, just wrap on/off
else {
worldName = player.getWorld().getName();
wrap = strAsBool(params.get(0));
}
boolean wrap = false;
BorderData border = Config.Border(worldName);
if (border == null) {
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return;
}
// world and wrap on/off specified
if (params.size() == 2)
{
worldName = params.get(0);
wrap = strAsBool(params.get(1));
}
// no world specified, just wrap on/off
else
{
worldName = player.getWorld().getName();
wrap = strAsBool(params.get(0));
}
border.setWrapping(wrap);
Config.setBorder(worldName, border, false);
BorderData border = Config.Border(worldName);
if (border == null)
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return;
}
border.setWrapping(wrap);
Config.setBorder(worldName, border, false);
sender.sendMessage("Border for world \"" + worldName + "\" is now set to " + (wrap ? "" : "not ") + "wrap around.");
}
sender.sendMessage("Border for world \"" + worldName + "\" is now set to " + (wrap ? "" : "not ") + "wrap around.");
}
}

View File

@ -1,69 +1,62 @@
package com.wimbli.WorldBorder.cmd;
import com.wimbli.WorldBorder.BorderData;
import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*;
public class CmdWshape extends WBCmd {
public CmdWshape() {
name = permission = "wshape";
minParams = 1;
maxParams = 2;
addCmdExample(nameEmphasized() + "{world} <elliptic|rectangular|default> - shape");
addCmdExample(C_DESC + " override for a single world.", true, true, false);
addCmdExample(nameEmphasized() + "{world} <round|square|default> - same as above.");
helpText = "This will override the default border shape for a single world. The value \"default\" implies " +
"a world is just using the default border shape. See the " + commandEmphasized("shape") + C_DESC +
"command for more info and to set the default border shape.";
}
public class CmdWshape extends WBCmd
{
public CmdWshape()
{
name = permission = "wshape";
minParams = 1;
maxParams = 2;
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
if (player == null && params.size() == 1) {
sendErrorAndHelp(sender, "When running this command from console, you must specify a world.");
return;
}
addCmdExample(nameEmphasized() + "{world} <elliptic|rectangular|default> - shape");
addCmdExample(C_DESC + " override for a single world.", true, true, false);
addCmdExample(nameEmphasized() + "{world} <round|square|default> - same as above.");
helpText = "This will override the default border shape for a single world. The value \"default\" implies " +
"a world is just using the default border shape. See the " + commandEmphasized("shape") + C_DESC +
"command for more info and to set the default border shape.";
}
String shapeName = "";
@Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName)
{
if (player == null && params.size() == 1)
{
sendErrorAndHelp(sender, "When running this command from console, you must specify a world.");
return;
}
// world and shape specified
if (params.size() == 2) {
worldName = params.get(0);
shapeName = params.get(1).toLowerCase();
}
// no world specified, just shape
else {
worldName = player.getWorld().getName();
shapeName = params.get(0).toLowerCase();
}
String shapeName = "";
BorderData border = Config.Border(worldName);
if (border == null) {
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return;
}
// world and shape specified
if (params.size() == 2)
{
worldName = params.get(0);
shapeName = params.get(1).toLowerCase();
}
// no world specified, just shape
else
{
worldName = player.getWorld().getName();
shapeName = params.get(0).toLowerCase();
}
Boolean shape = null;
if (shapeName.equals("rectangular") || shapeName.equals("square"))
shape = false;
else if (shapeName.equals("elliptic") || shapeName.equals("round"))
shape = true;
BorderData border = Config.Border(worldName);
if (border == null)
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return;
}
border.setShape(shape);
Config.setBorder(worldName, border, false);
Boolean shape = null;
if (shapeName.equals("rectangular") || shapeName.equals("square"))
shape = false;
else if (shapeName.equals("elliptic") || shapeName.equals("round"))
shape = true;
border.setShape(shape);
Config.setBorder(worldName, border, false);
sender.sendMessage("Border shape for world \"" + worldName + "\" is now set to \"" + Config.ShapeName(shape) + "\".");
}
sender.sendMessage("Border shape for world \"" + worldName + "\" is now set to \"" + Config.ShapeName(shape) + "\".");
}
}

View File

@ -1,147 +1,128 @@
package com.wimbli.WorldBorder.cmd;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
public abstract class WBCmd {
/*
* Primary variables, should be set as needed in constructors for the subclassed commands
*/
// color values for strings
public final static String C_CMD = ChatColor.AQUA.toString(); // main commands
public final static String C_DESC = ChatColor.WHITE.toString(); // command descriptions
public final static String C_ERR = ChatColor.RED.toString(); // errors / notices
public final static String C_HEAD = ChatColor.YELLOW.toString(); // command listing header
public final static String C_OPT = ChatColor.DARK_GREEN.toString(); // optional values
public final static String C_REQ = ChatColor.GREEN.toString(); // required values
// colorized root command, for console and for player
public final static String CMD_C = C_CMD + "wb ";
public final static String CMD_P = C_CMD + "/wb ";
// much like the above, but used for displaying command list from root /wb command, listing all commands
public final static List<String> cmdExamplesConsole = new ArrayList<String>(48); // 48 command capacity, 6 full pages
public abstract class WBCmd
{
/*
* Primary variables, should be set as needed in constructors for the subclassed commands
*/
/*
* Helper variables and methods
*/
public final static List<String> cmdExamplesPlayer = new ArrayList<String>(48); // still, could need to increase later
// command name, command permission; normally the same thing
public String name = "";
public String permission = null;
// whether command can accept a world name before itself
public boolean hasWorldNameInput = false;
public boolean consoleRequiresWorldName = true;
// minimum and maximum number of accepted parameters
public int minParams = 0;
public int maxParams = 9999;
// help/explanation text to be shown after command example(s) for this command
public String helpText = null;
// list of command examples for this command to be displayed as usage reference, separate between players and console
// ... these generally should be set indirectly using addCmdExample() within the constructor for each command class
public List<String> cmdExamplePlayer = new ArrayList<String>();
public List<String> cmdExampleConsole = new ArrayList<String>();
// command name, command permission; normally the same thing
public String name = "";
public String permission = null;
/*
* The guts of the command run in here; needs to be overriden in the subclassed commands
*/
public abstract void execute(CommandSender sender, Player player, List<String> params, String worldName);
// whether command can accept a world name before itself
public boolean hasWorldNameInput = false;
public boolean consoleRequiresWorldName = true;
/*
* This is an optional override, used to provide some extra command status info, like the currently set value
*/
public void cmdStatus(CommandSender sender) {
}
// minimum and maximum number of accepted parameters
public int minParams = 0;
public int maxParams = 9999;
// add command examples for use the default "/wb" command list and for internal usage reference, formatted and colorized
public void addCmdExample(String example) {
addCmdExample(example, true, true, true);
}
// help/explanation text to be shown after command example(s) for this command
public String helpText = null;
public void addCmdExample(String example, boolean forPlayer, boolean forConsole, boolean prefix) {
// go ahead and colorize required "<>" and optional "[]" parameters, extra command words, and description
example = example.replace("<", C_REQ + "<").replace("[", C_OPT + "[").replace("^", C_CMD).replace("- ", C_DESC + "- ");
/*
* The guts of the command run in here; needs to be overriden in the subclassed commands
*/
public abstract void execute(CommandSender sender, Player player, List<String> params, String worldName);
// all "{}" are replaced by "[]" (optional) for player, "<>" (required) for console
if (forPlayer) {
String exampleP = (prefix ? CMD_P : "") + example.replace("{", C_OPT + "[").replace("}", "]");
cmdExamplePlayer.add(exampleP);
cmdExamplesPlayer.add(exampleP);
}
if (forConsole) {
String exampleC = (prefix ? CMD_C : "") + example.replace("{", C_REQ + "<").replace("}", ">");
cmdExampleConsole.add(exampleC);
cmdExamplesConsole.add(exampleC);
}
}
/*
* This is an optional override, used to provide some extra command status info, like the currently set value
*/
public void cmdStatus(CommandSender sender) {}
// return root command formatted for player or console, based on sender
public String cmd(CommandSender sender) {
return (sender instanceof Player) ? CMD_P : CMD_C;
}
// formatted and colorized text, intended for marking command name
public String commandEmphasized(String text) {
return C_CMD + ChatColor.UNDERLINE + text + ChatColor.RESET + " ";
}
/*
* Helper variables and methods
*/
// returns green "enabled" or red "disabled" text
public String enabledColored(boolean enabled) {
return enabled ? C_REQ + "enabled" : C_ERR + "disabled";
}
// color values for strings
public final static String C_CMD = ChatColor.AQUA.toString(); // main commands
public final static String C_DESC = ChatColor.WHITE.toString(); // command descriptions
public final static String C_ERR = ChatColor.RED.toString(); // errors / notices
public final static String C_HEAD = ChatColor.YELLOW.toString(); // command listing header
public final static String C_OPT = ChatColor.DARK_GREEN.toString(); // optional values
public final static String C_REQ = ChatColor.GREEN.toString(); // required values
// formatted and colorized command name, optionally prefixed with "[world]" (for player) / "<world>" (for console)
public String nameEmphasized() {
return commandEmphasized(name);
}
// colorized root command, for console and for player
public final static String CMD_C = C_CMD + "wb ";
public final static String CMD_P = C_CMD + "/wb ";
public String nameEmphasizedW() {
return "{world} " + nameEmphasized();
}
// list of command examples for this command to be displayed as usage reference, separate between players and console
// ... these generally should be set indirectly using addCmdExample() within the constructor for each command class
public List<String> cmdExamplePlayer = new ArrayList<String>();
public List<String> cmdExampleConsole = new ArrayList<String>();
// send command example message(s) and other helpful info
public void sendCmdHelp(CommandSender sender) {
for (String example : ((sender instanceof Player) ? cmdExamplePlayer : cmdExampleConsole)) {
sender.sendMessage(example);
}
cmdStatus(sender);
if (helpText != null && !helpText.isEmpty())
sender.sendMessage(C_DESC + helpText);
}
// much like the above, but used for displaying command list from root /wb command, listing all commands
public final static List<String> cmdExamplesConsole = new ArrayList<String>(48); // 48 command capacity, 6 full pages
public final static List<String> cmdExamplesPlayer = new ArrayList<String>(48); // still, could need to increase later
// send error message followed by command example message(s)
public void sendErrorAndHelp(CommandSender sender, String error) {
sender.sendMessage(C_ERR + error);
sendCmdHelp(sender);
}
// add command examples for use the default "/wb" command list and for internal usage reference, formatted and colorized
public void addCmdExample(String example)
{
addCmdExample(example, true, true, true);
}
public void addCmdExample(String example, boolean forPlayer, boolean forConsole, boolean prefix)
{
// go ahead and colorize required "<>" and optional "[]" parameters, extra command words, and description
example = example.replace("<", C_REQ+"<").replace("[", C_OPT+"[").replace("^", C_CMD).replace("- ", C_DESC+"- ");
// all "{}" are replaced by "[]" (optional) for player, "<>" (required) for console
if (forPlayer)
{
String exampleP = (prefix ? CMD_P : "") + example.replace("{", C_OPT + "[").replace("}", "]");
cmdExamplePlayer.add(exampleP);
cmdExamplesPlayer.add(exampleP);
}
if (forConsole)
{
String exampleC = (prefix ? CMD_C : "") + example.replace("{", C_REQ + "<").replace("}", ">");
cmdExampleConsole.add(exampleC);
cmdExamplesConsole.add(exampleC);
}
}
// return root command formatted for player or console, based on sender
public String cmd(CommandSender sender)
{
return (sender instanceof Player) ? CMD_P : CMD_C;
}
// formatted and colorized text, intended for marking command name
public String commandEmphasized(String text)
{
return C_CMD + ChatColor.UNDERLINE + text + ChatColor.RESET + " ";
}
// returns green "enabled" or red "disabled" text
public String enabledColored(boolean enabled)
{
return enabled ? C_REQ+"enabled" : C_ERR+"disabled";
}
// formatted and colorized command name, optionally prefixed with "[world]" (for player) / "<world>" (for console)
public String nameEmphasized()
{
return commandEmphasized(name);
}
public String nameEmphasizedW()
{
return "{world} " + nameEmphasized();
}
// send command example message(s) and other helpful info
public void sendCmdHelp(CommandSender sender)
{
for (String example : ((sender instanceof Player) ? cmdExamplePlayer : cmdExampleConsole))
{
sender.sendMessage(example);
}
cmdStatus(sender);
if (helpText != null && !helpText.isEmpty())
sender.sendMessage(C_DESC + helpText);
}
// send error message followed by command example message(s)
public void sendErrorAndHelp(CommandSender sender, String error)
{
sender.sendMessage(C_ERR + error);
sendCmdHelp(sender);
}
// interpret string as boolean value (yes/no, true/false, on/off, +/-, 1/0)
public boolean strAsBool(String str)
{
str = str.toLowerCase();
return str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1");
}
// interpret string as boolean value (yes/no, true/false, on/off, +/-, 1/0)
public boolean strAsBool(String str) {
str = str.toLowerCase();
return str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1");
}
}

View File

@ -1,7 +1,7 @@
name: WorldBorder
author: Brettflan
authors: [Brettflan, PryPurity]
description: Efficient, feature-rich plugin for limiting the size of your worlds.
version: 1.9.10 (beta)
version: 2.0.0 (beta)
api-version: 1.13
main: com.wimbli.WorldBorder.WorldBorder
softdepend:
@ -11,41 +11,41 @@ commands:
description: Primary command for WorldBorder.
aliases: [wb]
usage: |
/<command> - list available commands (show help).
/<command> help [command] - get help on command usage.
/<command> [world] set <radiusX> [radiusZ] <x> <z> - set world border.
/<command> [world] set <radiusX> [radiusZ] spawn - use spawn point.
/<command> set <radiusX> [radiusZ] - set world border, centered on you.
/<command> set <radiusX> [radiusZ] player <name> - center on player.
/<command> [world] setcorners <x1> <z1> <x2> <z2> - set border from corners.
/<command> [world] radius <radiusX> [radiusZ] - change border's radius.
/<command> list - show border information for all worlds.
/<command> shape <elliptic|rectangular> - set the default border shape.
/<command> shape <round|square> - same as above, backwards compatible.
/<command> [world] clear - remove border for this world.
/<command> clear all - remove border for all worlds.
/<command> [world] fill [freq] [pad] [force] - generate world to border.
/<command> [world] trim [freq] [pad] - trim world outside of border.
/<command> bypass [player] [on/off] - let player go beyond border.
/<command> bypasslist - list players with border bypass enabled.
/<command> knockback <distance> - how far to move the player back.
/<command> wrap [world] <on/off> - can make border crossings wrap around.
/<command> whoosh <on/off> - turn knockback effect on or off.
/<command> getmsg - display border message.
/<command> setmsg <text> - set border message.
/<command> wshape [world] <elliptic|rectangular|default> - override shape.
/<command> wshape [world] <round|square|default> - same as above values.
/<command> delay <amount> - time between border checks.
/<command> dynmap <on/off> - turn DynMap border display on or off.
/<command> dynmapmsg <text> - DynMap border labels will show this.
/<command> remount <amount> - delay before remounting after knockback.
/<command> fillautosave <seconds> - world save interval for Fill process.
/<command> portal <on/off> - turn portal redirection on or off.
/<command> denypearl <on/off> - stop ender pearls thrown past the border.
/<command> preventblockplace <on|off> - stop block placement past border.
/<command> preventmobspawn <on|off> - stop mob spawning past border.
/<command> reload - re-load data from config.yml.
/<command> debug <on/off> - turn debug mode on or off.
/<command> - list available commands (show help).
/<command> help [command] - get help on command usage.
/<command> [world] set <radiusX> [radiusZ] <x> <z> - set world border.
/<command> [world] set <radiusX> [radiusZ] spawn - use spawn point.
/<command> set <radiusX> [radiusZ] - set world border, centered on you.
/<command> set <radiusX> [radiusZ] player <name> - center on player.
/<command> [world] setcorners <x1> <z1> <x2> <z2> - set border from corners.
/<command> [world] radius <radiusX> [radiusZ] - change border's radius.
/<command> list - show border information for all worlds.
/<command> shape <elliptic|rectangular> - set the default border shape.
/<command> shape <round|square> - same as above, backwards compatible.
/<command> [world] clear - remove border for this world.
/<command> clear all - remove border for all worlds.
/<command> [world] fill [freq] [pad] [force] - generate world to border.
/<command> [world] trim [freq] [pad] - trim world outside of border.
/<command> bypass [player] [on/off] - let player go beyond border.
/<command> bypasslist - list players with border bypass enabled.
/<command> knockback <distance> - how far to move the player back.
/<command> wrap [world] <on/off> - can make border crossings wrap around.
/<command> whoosh <on/off> - turn knockback effect on or off.
/<command> getmsg - display border message.
/<command> setmsg <text> - set border message.
/<command> wshape [world] <elliptic|rectangular|default> - override shape.
/<command> wshape [world] <round|square|default> - same as above values.
/<command> delay <amount> - time between border checks.
/<command> dynmap <on/off> - turn DynMap border display on or off.
/<command> dynmapmsg <text> - DynMap border labels will show this.
/<command> remount <amount> - delay before remounting after knockback.
/<command> fillautosave <seconds> - world save interval for Fill process.
/<command> portal <on/off> - turn portal redirection on or off.
/<command> denypearl <on/off> - stop ender pearls thrown past the border.
/<command> preventblockplace <on|off> - stop block placement past border.
/<command> preventmobspawn <on|off> - stop mob spawning past border.
/<command> reload - re-load data from config.yml.
/<command> debug <on/off> - turn debug mode on or off.
permissions:
worldborder.*:
description: Grants all WorldBorder permissions