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

11
pom.xml
View File

@ -1,5 +1,5 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
<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"> 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> <modelVersion>4.0.0</modelVersion>
<groupId>com.wimbli.WorldBorder</groupId> <groupId>com.wimbli.WorldBorder</groupId>
<artifactId>WorldBorder</artifactId> <artifactId>WorldBorder</artifactId>
@ -35,14 +35,14 @@
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.14-R0.1-SNAPSHOT</version> <version>1.15.2-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!--Bukkit API--> <!--Bukkit API-->
<dependency> <dependency>
<groupId>org.bukkit</groupId> <groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId> <artifactId>bukkit</artifactId>
<version>1.14-R0.1-SNAPSHOT</version> <version>1.15.2-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!--Dynmap API--> <!--Dynmap API-->
@ -78,7 +78,8 @@
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version> <version>3.1.1</version>
<configuration> <configuration>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml
</dependencyReducedPomLocation>
<relocations> <relocations>
<relocation> <relocation>
<pattern>io.papermc.lib</pattern> <pattern>io.papermc.lib</pattern>

View File

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

View File

@ -1,46 +1,24 @@
package com.wimbli.WorldBorder; 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 com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.Location;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.bukkit.World;
import java.util.*;
public class BorderCheckTask implements Runnable public class BorderCheckTask implements Runnable {
{
@Override
public void run()
{
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
Collection<Player> players = ImmutableList.copyOf(Bukkit.getServer().getOnlinePlayers());
for (Player player : players)
{
checkPlayer(player, null, false, true);
}
}
// 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. // 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>()); private static Set<String> handlingPlayers = Collections.synchronizedSet(new LinkedHashSet<String>());
// 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 // 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) public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly, boolean notify) {
{
if (player == null || !player.isOnline()) return null; if (player == null || !player.isOnline()) return null;
Location loc = (targetLoc == null) ? player.getLocation().clone() : targetLoc; Location loc = (targetLoc == null) ? player.getLocation().clone() : targetLoc;
@ -71,12 +49,10 @@ public class BorderCheckTask implements Runnable
* after players are ejected we can wait a few ticks (long enough for their client to receive new entity location) * 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 * and then set them as passenger of the vehicle again
*/ */
if (player.isInsideVehicle()) if (player.isInsideVehicle()) {
{
Entity ride = player.getVehicle(); Entity ride = player.getVehicle();
player.leaveVehicle(); player.leaveVehicle();
if (ride != null) if (ride != null) { // vehicles need to be offset vertically and have velocity stopped
{ // vehicles need to be offset vertically and have velocity stopped
double vertOffset = (ride instanceof LivingEntity) ? 0 : ride.getLocation().getY() - loc.getY(); double vertOffset = (ride instanceof LivingEntity) ? 0 : ride.getLocation().getY() - loc.getY();
Location rideLoc = newLoc.clone(); Location rideLoc = newLoc.clone();
rideLoc.setY(newLoc.getY() + vertOffset); rideLoc.setY(newLoc.getY() + vertOffset);
@ -86,8 +62,7 @@ public class BorderCheckTask implements Runnable
ride.setVelocity(new Vector(0, 0, 0)); ride.setVelocity(new Vector(0, 0, 0));
ride.teleport(rideLoc, TeleportCause.PLUGIN); ride.teleport(rideLoc, TeleportCause.PLUGIN);
if (Config.RemountTicks() > 0) if (Config.RemountTicks() > 0) {
{
setPassengerDelayed(ride, player, player.getName(), Config.RemountTicks()); setPassengerDelayed(ride, player, player.getName(), Config.RemountTicks());
handlingVehicle = true; handlingVehicle = true;
} }
@ -97,11 +72,9 @@ public class BorderCheckTask implements Runnable
// check if player has something (a pet, maybe?) riding them; only possible through odd plugins. // 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 // it can prevent all teleportation of the player completely, so it's very much not good and needs handling
List<Entity> passengers = player.getPassengers(); List<Entity> passengers = player.getPassengers();
if (!passengers.isEmpty()) if (!passengers.isEmpty()) {
{
player.eject(); player.eject();
for (Entity rider : passengers) for (Entity rider : passengers) {
{
rider.teleport(newLoc, TeleportCause.PLUGIN); rider.teleport(newLoc, TeleportCause.PLUGIN);
if (Config.Debug()) if (Config.Debug())
Config.logWarn("Player had a passenger riding on them: " + rider.getType()); Config.logWarn("Player had a passenger riding on them: " + rider.getType());
@ -123,15 +96,13 @@ public class BorderCheckTask implements Runnable
return null; return null;
} }
public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly)
{ public static Location checkPlayer(Player player, Location targetLoc, boolean returnLocationOnly) {
return checkPlayer(player, targetLoc, returnLocationOnly, true); return checkPlayer(player, targetLoc, returnLocationOnly, true);
} }
private static Location newLocation(Player player, Location loc, BorderData border, boolean notify) private static Location newLocation(Player player, Location loc, BorderData border, boolean notify) {
{ if (Config.Debug()) {
if (Config.Debug())
{
Config.logWarn((notify ? "Border crossing" : "Check was run") + " in \"" + loc.getWorld().getName() + "\". Border " + border.toString()); 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())); Config.logWarn("Player position X: " + Config.coord.format(loc.getX()) + " Y: " + Config.coord.format(loc.getY()) + " Z: " + Config.coord.format(loc.getZ()));
} }
@ -139,12 +110,10 @@ public class BorderCheckTask implements Runnable
Location newLoc = border.correctedPosition(loc, Config.ShapeRound(), player.isFlying()); Location newLoc = border.correctedPosition(loc, Config.ShapeRound(), player.isFlying());
// it's remotely possible (such as in the Nether) a suitable location isn't available, in which case... // it's remotely possible (such as in the Nether) a suitable location isn't available, in which case...
if (newLoc == null) if (newLoc == null) {
{
if (Config.Debug()) if (Config.Debug())
Config.logWarn("Target new location unviable, using spawn or killing player."); Config.logWarn("Target new location unviable, using spawn or killing player.");
if (Config.getIfPlayerKill()) if (Config.getIfPlayerKill()) {
{
player.setHealth(0.0D); player.setHealth(0.0D);
return null; return null;
} }
@ -160,13 +129,10 @@ public class BorderCheckTask implements Runnable
return newLoc; return newLoc;
} }
private static void setPassengerDelayed(final Entity vehicle, final Player player, final String playerName, long delay) private static void setPassengerDelayed(final Entity vehicle, final Player player, final String playerName, long delay) {
{ Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() {
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable()
{
@Override @Override
public void run() public void run() {
{
handlingPlayers.remove(playerName.toLowerCase()); handlingPlayers.remove(playerName.toLowerCase());
if (vehicle == null || player == null) if (vehicle == null || player == null)
return; return;
@ -175,4 +141,17 @@ public class BorderCheckTask implements Runnable
} }
}, delay); }, delay);
} }
@Override
public void run() {
// if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0)
return;
Collection<Player> players = ImmutableList.copyOf(Bukkit.getServer().getOnlinePlayers());
for (Player player : players) {
checkPlayer(player, null, false, true);
}
}
} }

View File

@ -1,299 +1,21 @@
package com.wimbli.WorldBorder; package com.wimbli.WorldBorder;
import java.util.EnumSet;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import java.util.EnumSet;
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);
}
// backwards-compatible methods from before elliptical/rectangular shapes were supported public class BorderData {
/**
* @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);
}
//these material IDs are acceptable for places to teleport player; breathable blocks and water //these material IDs are acceptable for places to teleport player; breathable blocks and water
public static final EnumSet<Material> safeOpenBlocks = EnumSet.noneOf(Material.class); public static final EnumSet<Material> safeOpenBlocks = EnumSet.noneOf(Material.class);
static //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.AIR);
safeOpenBlocks.add(Material.CAVE_AIR); safeOpenBlocks.add(Material.CAVE_AIR);
safeOpenBlocks.add(Material.OAK_SAPLING); safeOpenBlocks.add(Material.OAK_SAPLING);
@ -374,8 +96,7 @@ public class BorderData
safeOpenBlocks.add(Material.TALL_GRASS); safeOpenBlocks.add(Material.TALL_GRASS);
safeOpenBlocks.add(Material.LARGE_FERN); safeOpenBlocks.add(Material.LARGE_FERN);
safeOpenBlocks.add(Material.BEETROOTS); safeOpenBlocks.add(Material.BEETROOTS);
try try { // signs in 1.14 can be different wood types
{ // signs in 1.14 can be different wood types
safeOpenBlocks.add(Material.ACACIA_SIGN); safeOpenBlocks.add(Material.ACACIA_SIGN);
safeOpenBlocks.add(Material.ACACIA_WALL_SIGN); safeOpenBlocks.add(Material.ACACIA_WALL_SIGN);
safeOpenBlocks.add(Material.BIRCH_SIGN); safeOpenBlocks.add(Material.BIRCH_SIGN);
@ -388,14 +109,11 @@ public class BorderData
safeOpenBlocks.add(Material.OAK_WALL_SIGN); safeOpenBlocks.add(Material.OAK_WALL_SIGN);
safeOpenBlocks.add(Material.SPRUCE_SIGN); safeOpenBlocks.add(Material.SPRUCE_SIGN);
safeOpenBlocks.add(Material.SPRUCE_WALL_SIGN); safeOpenBlocks.add(Material.SPRUCE_WALL_SIGN);
} catch (NoSuchFieldError ex) {
} }
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 static {
public static final EnumSet<Material> painfulBlocks = EnumSet.noneOf(Material.class);
static
{
painfulBlocks.add(Material.LAVA); painfulBlocks.add(Material.LAVA);
painfulBlocks.add(Material.FIRE); painfulBlocks.add(Material.FIRE);
painfulBlocks.add(Material.CACTUS); painfulBlocks.add(Material.CACTUS);
@ -403,9 +121,268 @@ public class BorderData
painfulBlocks.add(Material.MAGMA_BLOCK); 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;
}
// 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 // 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) 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 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 && safeOpenBlocks.contains(world.getBlockAt(X, Y + 1, Z).getType()); // above target block open and safe
if (!safe || flying) if (!safe || flying)
@ -418,11 +395,8 @@ public class BorderData
); );
} }
private static final int limBot = 0;
// find closest safe Y position from the starting position // find closest safe Y position from the starting position
private double getSafeY(World world, int X, int Y, int Z, boolean flying) 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 // 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; final boolean isNether = world.getEnvironment() == World.Environment.NETHER;
int limTop = isNether ? 125 : world.getMaxHeight() - 2; int limTop = isNether ? 125 : world.getMaxHeight() - 2;
@ -433,12 +407,10 @@ public class BorderData
return (double) Y; return (double) Y;
// make sure Y values are within the boundaries of the world. // make sure Y values are within the boundaries of the world.
if (Y > limTop) if (Y > limTop) {
{
if (isNether) if (isNether)
Y = limTop; // because of the roof, the nether can not rely on highestBlockBoundary, so limTop has to be used Y = limTop; // because of the roof, the nether can not rely on highestBlockBoundary, so limTop has to be used
else else {
{
if (flying) if (flying)
Y = limTop; Y = limTop;
else else
@ -455,15 +427,13 @@ public class BorderData
for (int y1 = Y, y2 = Y; (y1 > limBot) || (y2 < limTop); y1--, y2++) { for (int y1 = Y, y2 = Y; (y1 > limBot) || (y2 < limTop); y1--, y2++) {
// Look below. // Look below.
if(y1 > limBot) if (y1 > limBot) {
{
if (isSafeSpot(world, X, y1, Z, flying)) if (isSafeSpot(world, X, y1, Z, flying))
return (double) y1; return (double) y1;
} }
// Look above. // Look above.
if(y2 < limTop && y2 != y1) if (y2 < limTop && y2 != y1) {
{
if (isSafeSpot(world, X, y2, Z, flying)) if (isSafeSpot(world, X, y2, Z, flying))
return (double) y2; return (double) y2;
} }
@ -474,8 +444,7 @@ public class BorderData
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj) {
{
if (this == obj) if (this == obj)
return true; return true;
else if (obj == null || obj.getClass() != this.getClass()) else if (obj == null || obj.getClass() != this.getClass())
@ -486,8 +455,7 @@ public class BorderData
} }
@Override @Override
public int hashCode() public int hashCode() {
{
return (((int) (this.x * 10) << 4) + (int) this.z + (this.radiusX << 2) + (this.radiusZ << 3)); return (((int) (this.x * 10) << 4) + (int) this.z + (this.radiusX << 2) + (this.radiusZ << 3));
} }
} }

View File

@ -1,40 +1,31 @@
package com.wimbli.WorldBorder; package com.wimbli.WorldBorder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Effect; import org.bukkit.Effect;
import org.bukkit.entity.Player;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import java.text.DecimalFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Config public class Config {
{ private static final int currentCfgVersion = 12;
public static volatile DecimalFormat coord = new DecimalFormat("0.0");
public static volatile WorldFillTask fillTask = null;
public static volatile WorldTrimTask trimTask = null;
// private stuff used within this class // private stuff used within this class
private static WorldBorder plugin; private static WorldBorder plugin;
private static FileConfiguration cfg = null; private static FileConfiguration cfg = null;
private static Logger wbLog = null; private static Logger wbLog = null;
public static volatile DecimalFormat coord = new DecimalFormat("0.0");
private static int borderTask = -1; private static int borderTask = -1;
public static volatile WorldFillTask fillTask = null;
public static volatile WorldTrimTask trimTask = null;
private static Runtime rt = Runtime.getRuntime(); private static Runtime rt = Runtime.getRuntime();
// actual configuration values which can be changed // actual configuration values which can be changed
private static boolean shapeRound = true; private static boolean shapeRound = true;
private static Map<String, BorderData> borders = Collections.synchronizedMap(new LinkedHashMap<String, BorderData>()); private static Map<String, BorderData> borders = Collections.synchronizedMap(new LinkedHashMap<String, BorderData>());
@ -45,108 +36,96 @@ public class Config
private static boolean DEBUG = false; private static boolean DEBUG = false;
private static double knockBack = 3.0; private static double knockBack = 3.0;
private static int timerTicks = 4; private static int timerTicks = 4;
private static boolean whooshEffect = true; private static boolean whooshEffect = false;
private static boolean portalRedirection = true; private static boolean portalRedirection = true;
private static boolean dynmapEnable = true; private static boolean dynmapEnable = false;
private static String dynmapMessage; private static String dynmapMessage;
private static int dynmapPriority = 0; private static int dynmapPriority = 0;
private static boolean dynmapHideByDefault = false; private static boolean dynmapHideByDefault = false;
private static int remountDelayTicks = 0; private static int remountDelayTicks = 0;
private static boolean killPlayer = false; private static boolean killPlayer = false;
private static boolean denyEnderpearl = false; private static boolean denyEnderpearl = false;
private static int fillAutosaveFrequency = 30; private static int fillAutosaveFrequency = 45;
private static int fillMemoryTolerance = 500; private static int fillMemoryTolerance = 500;
private static boolean preventBlockPlace = false; private static boolean preventBlockPlace = false;
private static boolean preventMobSpawn = false;
// for monitoring plugin efficiency // for monitoring plugin efficiency
// public static long timeUsed = 0; // public static long timeUsed = 0;
private static boolean preventMobSpawn = false;
public static long Now() public static long Now() {
{
return System.currentTimeMillis(); return System.currentTimeMillis();
} }
public static void setBorder(String world, BorderData border, boolean logIt) {
public static void setBorder(String world, BorderData border, boolean logIt)
{
borders.put(world, border); borders.put(world, border);
if (logIt) if (logIt)
log("Border set. " + BorderDescription(world)); log("Border set. " + BorderDescription(world));
save(true); save(true);
DynMapFeatures.showBorder(world, border); DynMapFeatures.showBorder(world, border);
} }
public static void setBorder(String world, BorderData border)
{ public static void setBorder(String world, BorderData border) {
setBorder(world, border, true); setBorder(world, border, true);
} }
public static void setBorder(String world, int radiusX, int radiusZ, double x, double z, Boolean shapeRound) public static void setBorder(String world, int radiusX, int radiusZ, double x, double z, Boolean shapeRound) {
{
BorderData old = Border(world); BorderData old = Border(world);
boolean oldWrap = (old != null) && old.getWrapping(); boolean oldWrap = (old != null) && old.getWrapping();
setBorder(world, new BorderData(x, z, radiusX, radiusZ, shapeRound, oldWrap), true); setBorder(world, new BorderData(x, z, radiusX, radiusZ, shapeRound, oldWrap), true);
} }
public static void setBorder(String world, int radiusX, int radiusZ, double x, double z)
{ public static void setBorder(String world, int radiusX, int radiusZ, double x, double z) {
BorderData old = Border(world); BorderData old = Border(world);
Boolean oldShape = (old == null) ? null : old.getShape(); Boolean oldShape = (old == null) ? null : old.getShape();
boolean oldWrap = (old != null) && old.getWrapping(); boolean oldWrap = (old != null) && old.getWrapping();
setBorder(world, new BorderData(x, z, radiusX, radiusZ, oldShape, oldWrap), true); setBorder(world, new BorderData(x, z, radiusX, radiusZ, oldShape, oldWrap), true);
} }
// backwards-compatible methods from before elliptical/rectangular shapes were supported // backwards-compatible methods from before elliptical/rectangular shapes were supported
public static void setBorder(String world, int radius, double x, double z, Boolean shapeRound) public static void setBorder(String world, int radius, double x, double z, Boolean shapeRound) {
{
setBorder(world, new BorderData(x, z, radius, radius, shapeRound), true); setBorder(world, new BorderData(x, z, radius, radius, shapeRound), true);
} }
public static void setBorder(String world, int radius, double x, double z)
{ public static void setBorder(String world, int radius, double x, double z) {
setBorder(world, radius, radius, x, z); setBorder(world, radius, radius, x, z);
} }
// set border based on corner coordinates // set border based on corner coordinates
public static void setBorderCorners(String world, double x1, double z1, double x2, double z2, Boolean shapeRound, boolean wrap) public static void setBorderCorners(String world, double x1, double z1, double x2, double z2, Boolean shapeRound, boolean wrap) {
{
double radiusX = Math.abs(x1 - x2) / 2; double radiusX = Math.abs(x1 - x2) / 2;
double radiusZ = Math.abs(z1 - z2) / 2; double radiusZ = Math.abs(z1 - z2) / 2;
double x = ((x1 < x2) ? x1 : x2) + radiusX; double x = ((x1 < x2) ? x1 : x2) + radiusX;
double z = ((z1 < z2) ? z1 : z2) + radiusZ; double z = ((z1 < z2) ? z1 : z2) + radiusZ;
setBorder(world, new BorderData(x, z, (int) Math.round(radiusX), (int) Math.round(radiusZ), shapeRound, wrap), true); setBorder(world, new BorderData(x, z, (int) Math.round(radiusX), (int) Math.round(radiusZ), shapeRound, wrap), true);
} }
public static void setBorderCorners(String world, double x1, double z1, double x2, double z2, Boolean shapeRound)
{ public static void setBorderCorners(String world, double x1, double z1, double x2, double z2, Boolean shapeRound) {
setBorderCorners(world, x1, z1, x2, z2, shapeRound, false); setBorderCorners(world, x1, z1, x2, z2, shapeRound, false);
} }
public static void setBorderCorners(String world, double x1, double z1, double x2, double z2)
{ public static void setBorderCorners(String world, double x1, double z1, double x2, double z2) {
BorderData old = Border(world); BorderData old = Border(world);
Boolean oldShape = (old == null) ? null : old.getShape(); Boolean oldShape = (old == null) ? null : old.getShape();
boolean oldWrap = (old != null) && old.getWrapping(); boolean oldWrap = (old != null) && old.getWrapping();
setBorderCorners(world, x1, z1, x2, z2, oldShape, oldWrap); setBorderCorners(world, x1, z1, x2, z2, oldShape, oldWrap);
} }
public static void removeBorder(String world) {
public static void removeBorder(String world)
{
borders.remove(world); borders.remove(world);
log("Removed border for world \"" + world + "\"."); log("Removed border for world \"" + world + "\".");
save(true); save(true);
DynMapFeatures.removeBorder(world); DynMapFeatures.removeBorder(world);
} }
public static void removeAllBorders() public static void removeAllBorders() {
{
borders.clear(); borders.clear();
log("Removed all borders for all worlds."); log("Removed all borders for all worlds.");
save(true); save(true);
DynMapFeatures.removeAllBorders(); DynMapFeatures.removeAllBorders();
} }
public static String BorderDescription(String world) public static String BorderDescription(String world) {
{
BorderData border = borders.get(world); BorderData border = borders.get(world);
if (border == null) if (border == null)
return "No border was found for the world \"" + world + "\"."; return "No border was found for the world \"" + world + "\".";
@ -154,104 +133,89 @@ public class Config
return "World \"" + world + "\" has border " + border.toString(); return "World \"" + world + "\" has border " + border.toString();
} }
public static Set<String> BorderDescriptions() public static Set<String> BorderDescriptions() {
{
Set<String> output = new HashSet<String>(); Set<String> output = new HashSet<String>();
for(String worldName : borders.keySet()) for (String worldName : borders.keySet()) {
{
output.add(BorderDescription(worldName)); output.add(BorderDescription(worldName));
} }
return output; return output;
} }
public static BorderData Border(String world) public static BorderData Border(String world) {
{
return borders.get(world); return borders.get(world);
} }
public static Map<String, BorderData> getBorders() public static Map<String, BorderData> getBorders() {
{
return new LinkedHashMap<String, BorderData>(borders); return new LinkedHashMap<String, BorderData>(borders);
} }
public static void setMessage(String msg) public static void setMessage(String msg) {
{
updateMessage(msg); updateMessage(msg);
save(true); save(true);
} }
public static void updateMessage(String msg) public static void updateMessage(String msg) {
{
message = msg; message = msg;
messageFmt = replaceAmpColors(msg); messageFmt = replaceAmpColors(msg);
messageClean = stripAmpColors(msg); messageClean = stripAmpColors(msg);
} }
public static String Message() public static String Message() {
{
return messageFmt; return messageFmt;
} }
public static String MessageRaw()
{ public static String MessageRaw() {
return message; return message;
} }
public static String MessageClean()
{ public static String MessageClean() {
return messageClean; return messageClean;
} }
public static void setShape(boolean round) public static void setShape(boolean round) {
{
shapeRound = round; shapeRound = round;
log("Set default border shape to " + (ShapeName()) + "."); log("Set default border shape to " + (ShapeName()) + ".");
save(true); save(true);
DynMapFeatures.showAllBorders(); DynMapFeatures.showAllBorders();
} }
public static boolean ShapeRound() public static boolean ShapeRound() {
{
return shapeRound; return shapeRound;
} }
public static String ShapeName() public static String ShapeName() {
{
return ShapeName(shapeRound); return ShapeName(shapeRound);
} }
public static String ShapeName(Boolean round)
{ public static String ShapeName(Boolean round) {
if (round == null) if (round == null)
return "default"; return "default";
return round ? "elliptic/round" : "rectangular/square"; return round ? "elliptic/round" : "rectangular/square";
} }
public static void setDebug(boolean debugMode) public static void setDebug(boolean debugMode) {
{
DEBUG = debugMode; DEBUG = debugMode;
log("Debug mode " + (DEBUG ? "enabled" : "disabled") + "."); log("Debug mode " + (DEBUG ? "enabled" : "disabled") + ".");
save(true); save(true);
} }
public static boolean Debug() public static boolean Debug() {
{
return DEBUG; return DEBUG;
} }
public static void setWhooshEffect(boolean enable) public static void setWhooshEffect(boolean enable) {
{
whooshEffect = enable; whooshEffect = enable;
log("\"Whoosh\" knockback effect " + (enable ? "enabled" : "disabled") + "."); log("\"Whoosh\" knockback effect " + (enable ? "enabled" : "disabled") + ".");
save(true); save(true);
} }
public static boolean whooshEffect() public static boolean whooshEffect() {
{
return whooshEffect; return whooshEffect;
} }
public static void showWhooshEffect(Location loc) public static void showWhooshEffect(Location loc) {
{
if (!whooshEffect()) if (!whooshEffect())
return; return;
@ -264,8 +228,7 @@ public class Config
world.playEffect(loc, Effect.GHAST_SHOOT, 0); world.playEffect(loc, Effect.GHAST_SHOOT, 0);
} }
public static void setPreventBlockPlace(boolean enable) public static void setPreventBlockPlace(boolean enable) {
{
if (preventBlockPlace != enable) if (preventBlockPlace != enable)
WorldBorder.plugin.enableBlockPlaceListener(enable); WorldBorder.plugin.enableBlockPlaceListener(enable);
@ -274,8 +237,7 @@ public class Config
save(true); save(true);
} }
public static void setPreventMobSpawn(boolean enable) public static void setPreventMobSpawn(boolean enable) {
{
if (preventMobSpawn != enable) if (preventMobSpawn != enable)
WorldBorder.plugin.enableMobSpawnListener(enable); WorldBorder.plugin.enableMobSpawnListener(enable);
@ -284,77 +246,64 @@ public class Config
save(true); save(true);
} }
public static boolean preventBlockPlace() public static boolean preventBlockPlace() {
{
return preventBlockPlace; return preventBlockPlace;
} }
public static boolean preventMobSpawn() public static boolean preventMobSpawn() {
{
return preventMobSpawn; return preventMobSpawn;
} }
public static boolean getIfPlayerKill() public static boolean getIfPlayerKill() {
{
return killPlayer; return killPlayer;
} }
public static boolean getDenyEnderpearl() public static boolean getDenyEnderpearl() {
{
return denyEnderpearl; return denyEnderpearl;
} }
public static void setDenyEnderpearl(boolean enable) public static void setDenyEnderpearl(boolean enable) {
{
denyEnderpearl = enable; denyEnderpearl = enable;
log("Direct cancellation of ender pearls thrown past the border " + (enable ? "enabled" : "disabled") + "."); log("Direct cancellation of ender pearls thrown past the border " + (enable ? "enabled" : "disabled") + ".");
save(true); save(true);
} }
public static void setPortalRedirection(boolean enable) public static void setPortalRedirection(boolean enable) {
{
portalRedirection = enable; portalRedirection = enable;
log("Portal redirection " + (enable ? "enabled" : "disabled") + "."); log("Portal redirection " + (enable ? "enabled" : "disabled") + ".");
save(true); save(true);
} }
public static boolean portalRedirection() public static boolean portalRedirection() {
{
return portalRedirection; return portalRedirection;
} }
public static void setKnockBack(double numBlocks) public static void setKnockBack(double numBlocks) {
{
knockBack = numBlocks; knockBack = numBlocks;
log("Knockback set to " + knockBack + " blocks inside the border."); log("Knockback set to " + knockBack + " blocks inside the border.");
save(true); save(true);
} }
public static double KnockBack() public static double KnockBack() {
{
return knockBack; return knockBack;
} }
public static void setTimerTicks(int ticks) public static void setTimerTicks(int ticks) {
{
timerTicks = ticks; timerTicks = ticks;
log("Timer delay set to " + timerTicks + " tick(s). That is roughly " + (timerTicks * 50) + "ms / " + (((double) timerTicks * 50.0) / 1000.0) + " seconds."); log("Timer delay set to " + timerTicks + " tick(s). That is roughly " + (timerTicks * 50) + "ms / " + (((double) timerTicks * 50.0) / 1000.0) + " seconds.");
StartBorderTimer(); StartBorderTimer();
save(true); save(true);
} }
public static int TimerTicks() public static int TimerTicks() {
{
return timerTicks; return timerTicks;
} }
public static void setRemountTicks(int ticks) public static void setRemountTicks(int ticks) {
{
remountDelayTicks = ticks; remountDelayTicks = ticks;
if (remountDelayTicks == 0) if (remountDelayTicks == 0)
log("Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle."); log("Remount delay set to 0. Players will be left dismounted when knocked back from the border while on a vehicle.");
else else {
{
log("Remount delay set to " + remountDelayTicks + " tick(s). That is roughly " + (remountDelayTicks * 50) + "ms / " + (((double) remountDelayTicks * 50.0) / 1000.0) + " seconds."); log("Remount delay set to " + remountDelayTicks + " tick(s). That is roughly " + (remountDelayTicks * 50) + "ms / " + (((double) remountDelayTicks * 50.0) / 1000.0) + " seconds.");
if (ticks < 10) if (ticks < 10)
logWarn("setting the remount delay to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches."); logWarn("setting the remount delay to less than 10 (and greater than 0) is not recommended. This can lead to nasty client glitches.");
@ -362,13 +311,11 @@ public class Config
save(true); save(true);
} }
public static int RemountTicks() public static int RemountTicks() {
{
return remountDelayTicks; return remountDelayTicks;
} }
public static void setFillAutosaveFrequency(int seconds) public static void setFillAutosaveFrequency(int seconds) {
{
fillAutosaveFrequency = seconds; fillAutosaveFrequency = seconds;
if (fillAutosaveFrequency == 0) if (fillAutosaveFrequency == 0)
log("World autosave frequency during Fill process set to 0, disabling it. 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."); log("World autosave frequency during Fill process set to 0, disabling it. 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.");
@ -377,50 +324,41 @@ public class Config
save(true); save(true);
} }
public static int FillAutosaveFrequency() public static int FillAutosaveFrequency() {
{
return fillAutosaveFrequency; return fillAutosaveFrequency;
} }
public static void setDynmapBorderEnabled(boolean enable) {
public static void setDynmapBorderEnabled(boolean enable)
{
dynmapEnable = enable; dynmapEnable = enable;
log("DynMap border display is now " + (enable ? "enabled" : "disabled") + "."); log("DynMap border display is now " + (enable ? "enabled" : "disabled") + ".");
save(true); save(true);
DynMapFeatures.showAllBorders(); DynMapFeatures.showAllBorders();
} }
public static boolean DynmapBorderEnabled() public static boolean DynmapBorderEnabled() {
{
return dynmapEnable; return dynmapEnable;
} }
public static void setDynmapMessage(String msg) public static void setDynmapMessage(String msg) {
{
dynmapMessage = msg; dynmapMessage = msg;
log("DynMap border label is now set to: " + msg); log("DynMap border label is now set to: " + msg);
save(true); save(true);
DynMapFeatures.showAllBorders(); DynMapFeatures.showAllBorders();
} }
public static String DynmapMessage() public static String DynmapMessage() {
{
return dynmapMessage; return dynmapMessage;
} }
public static boolean DynmapHideByDefault() public static boolean DynmapHideByDefault() {
{
return dynmapHideByDefault; return dynmapHideByDefault;
} }
public static int DynmapPriority() public static int DynmapPriority() {
{
return dynmapPriority; return dynmapPriority;
} }
public static void setPlayerBypass(UUID player, boolean bypass) public static void setPlayerBypass(UUID player, boolean bypass) {
{
if (bypass) if (bypass)
bypassPlayers.add(player); bypassPlayers.add(player);
else else
@ -428,43 +366,35 @@ public class Config
save(true); save(true);
} }
public static boolean isPlayerBypassing(UUID player) public static boolean isPlayerBypassing(UUID player) {
{
return bypassPlayers.contains(player); return bypassPlayers.contains(player);
} }
public static ArrayList<UUID> getPlayerBypassList() public static ArrayList<UUID> getPlayerBypassList() {
{
return new ArrayList<>(bypassPlayers); return new ArrayList<>(bypassPlayers);
} }
// for converting bypass UUID list to/from String list, for storage in config // for converting bypass UUID list to/from String list, for storage in config
private static void importBypassStringList(List<String> strings) private static void importBypassStringList(List<String> strings) {
{ for (String string : strings) {
for (String string: strings)
{
bypassPlayers.add(UUID.fromString(string)); bypassPlayers.add(UUID.fromString(string));
} }
} }
private static ArrayList<String> exportBypassStringList()
{ private static ArrayList<String> exportBypassStringList() {
ArrayList<String> strings = new ArrayList<String>(); ArrayList<String> strings = new ArrayList<String>();
for (UUID uuid: bypassPlayers) for (UUID uuid : bypassPlayers) {
{
strings.add(uuid.toString()); strings.add(uuid.toString());
} }
return strings; return strings;
} }
public static boolean isBorderTimerRunning() {
public static boolean isBorderTimerRunning()
{
if (borderTask == -1) return false; if (borderTask == -1) return false;
return (plugin.getServer().getScheduler().isQueued(borderTask) || plugin.getServer().getScheduler().isCurrentlyRunning(borderTask)); return (plugin.getServer().getScheduler().isQueued(borderTask) || plugin.getServer().getScheduler().isCurrentlyRunning(borderTask));
} }
public static void StartBorderTimer() public static void StartBorderTimer() {
{
StopBorderTimer(false); StopBorderTimer(false);
borderTask = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new BorderCheckTask(), timerTicks, timerTicks); borderTask = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new BorderCheckTask(), timerTicks, timerTicks);
@ -475,12 +405,11 @@ public class Config
logConfig("Border-checking timed task started."); logConfig("Border-checking timed task started.");
} }
public static void StopBorderTimer() public static void StopBorderTimer() {
{
StopBorderTimer(true); StopBorderTimer(true);
} }
public static void StopBorderTimer(boolean logIt)
{ public static void StopBorderTimer(boolean logIt) {
if (borderTask == -1) return; if (borderTask == -1) return;
plugin.getServer().getScheduler().cancelTask(borderTask); plugin.getServer().getScheduler().cancelTask(borderTask);
@ -489,62 +418,51 @@ public class Config
logConfig("Border-checking timed task stopped."); logConfig("Border-checking timed task stopped.");
} }
public static void StopFillTask() public static void StopFillTask() {
{
if (fillTask != null && fillTask.valid()) if (fillTask != null && fillTask.valid())
fillTask.cancel(); fillTask.cancel();
} }
public static void StoreFillTask() public static void StoreFillTask() {
{
save(false, true); save(false, true);
} }
public static void UnStoreFillTask()
{ public static void UnStoreFillTask() {
save(false); save(false);
} }
public static void RestoreFillTask(String world, int fillDistance, int chunksPerRun, int tickFrequency, int x, int z, int length, int total, boolean forceLoad) public static void RestoreFillTask(String world, int fillDistance, int chunksPerRun, int tickFrequency, int x, int z, int length, int total, boolean forceLoad) {
{
fillTask = new WorldFillTask(plugin.getServer(), null, world, fillDistance, chunksPerRun, tickFrequency, forceLoad); fillTask = new WorldFillTask(plugin.getServer(), null, world, fillDistance, chunksPerRun, tickFrequency, forceLoad);
if (fillTask.valid()) if (fillTask.valid()) {
{
fillTask.continueProgress(x, z, length, total); fillTask.continueProgress(x, z, length, total);
int task = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, fillTask, 20, tickFrequency); int task = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, fillTask, 20, tickFrequency);
fillTask.setTaskID(task); fillTask.setTaskID(task);
} }
} }
// for backwards compatibility // for backwards compatibility
public static void RestoreFillTask(String world, int fillDistance, int chunksPerRun, int tickFrequency, int x, int z, int length, int total) public static void RestoreFillTask(String world, int fillDistance, int chunksPerRun, int tickFrequency, int x, int z, int length, int total) {
{
RestoreFillTask(world, fillDistance, chunksPerRun, tickFrequency, x, z, length, total, false); RestoreFillTask(world, fillDistance, chunksPerRun, tickFrequency, x, z, length, total, false);
} }
public static void StopTrimTask() {
public static void StopTrimTask()
{
if (trimTask != null && trimTask.valid()) if (trimTask != null && trimTask.valid())
trimTask.cancel(); trimTask.cancel();
} }
public static int AvailableMemory() {
public static int AvailableMemory()
{
return (int) ((rt.maxMemory() - rt.totalMemory() + rt.freeMemory()) / 1048576); // 1024*1024 = 1048576 (bytes in 1 MB) return (int) ((rt.maxMemory() - rt.totalMemory() + rt.freeMemory()) / 1048576); // 1024*1024 = 1048576 (bytes in 1 MB)
} }
public static boolean AvailableMemoryTooLow() public static boolean AvailableMemoryTooLow() {
{
return AvailableMemory() < fillMemoryTolerance; return AvailableMemory() < fillMemoryTolerance;
} }
public static boolean HasPermission(Player player, String request) {
public static boolean HasPermission(Player player, String request)
{
return HasPermission(player, request, true); return HasPermission(player, request, true);
} }
public static boolean HasPermission(Player player, String request, boolean notify)
{ public static boolean HasPermission(Player player, String request, boolean notify) {
if (player == null) // console, always permitted if (player == null) // console, always permitted
return true; return true;
@ -557,40 +475,32 @@ public class Config
return false; return false;
} }
public static String replaceAmpColors(String message) {
public static String replaceAmpColors (String message)
{
return ChatColor.translateAlternateColorCodes('&', message); return ChatColor.translateAlternateColorCodes('&', message);
} }
// adapted from code posted by Sleaker // adapted from code posted by Sleaker
public static String stripAmpColors (String message) public static String stripAmpColors(String message) {
{
return message.replaceAll("(?i)&([a-fk-or0-9])", ""); return message.replaceAll("(?i)&([a-fk-or0-9])", "");
} }
public static void log(Level lvl, String text) {
public static void log(Level lvl, String text)
{
wbLog.log(lvl, text); wbLog.log(lvl, text);
} }
public static void log(String text)
{ public static void log(String text) {
log(Level.INFO, text); log(Level.INFO, text);
} }
public static void logWarn(String text)
{ public static void logWarn(String text) {
log(Level.WARNING, text); log(Level.WARNING, text);
} }
public static void logConfig(String text)
{ public static void logConfig(String text) {
log(Level.INFO, "[CONFIG] " + text); log(Level.INFO, "[CONFIG] " + text);
} }
public static void load(WorldBorder master, boolean logIt) { // load config from file
private static final int currentCfgVersion = 11;
public static void load(WorldBorder master, boolean logIt)
{ // load config from file
plugin = master; plugin = master;
wbLog = plugin.getLogger(); wbLog = plugin.getLogger();
@ -625,8 +535,7 @@ public class Config
borders.clear(); borders.clear();
// if empty border message, assume no config // if empty border message, assume no config
if (msg == null || msg.isEmpty()) if (msg == null || msg.isEmpty()) { // store defaults
{ // store defaults
logConfig("Configuration not present, creating new file."); logConfig("Configuration not present, creating new file.");
msg = "&cYou have reached the edge of this world."; msg = "&cYou have reached the edge of this world.";
updateMessage(msg); updateMessage(msg);
@ -649,12 +558,10 @@ public class Config
cfg.set("bypass-list", null); cfg.set("bypass-list", null);
ConfigurationSection worlds = cfg.getConfigurationSection("worlds"); ConfigurationSection worlds = cfg.getConfigurationSection("worlds");
if (worlds != null) if (worlds != null) {
{
Set<String> worldNames = worlds.getKeys(false); Set<String> worldNames = worlds.getKeys(false);
for(String worldName : worldNames) for (String worldName : worldNames) {
{
ConfigurationSection bord = worlds.getConfigurationSection(worldName); ConfigurationSection bord = worlds.getConfigurationSection(worldName);
// we're swapping "<" to "." at load since periods denote configuration nodes without a working way to change that, so world names with periods wreak havoc and are thus modified for storage // we're swapping "<" to "." at load since periods denote configuration nodes without a working way to change that, so world names with periods wreak havoc and are thus modified for storage
@ -662,8 +569,7 @@ public class Config
worldName = worldName.replace("<", "."); worldName = worldName.replace("<", ".");
// backwards compatibility for config from before elliptical/rectangular borders were supported // backwards compatibility for config from before elliptical/rectangular borders were supported
if (bord.isSet("radius") && !bord.isSet("radiusX")) if (bord.isSet("radius") && !bord.isSet("radiusX")) {
{
int radius = bord.getInt("radius"); int radius = bord.getInt("radius");
bord.set("radiusX", radius); bord.set("radiusX", radius);
bord.set("radiusZ", radius); bord.set("radiusZ", radius);
@ -679,8 +585,7 @@ public class Config
// if we have an unfinished fill task stored from a previous run, load it up // if we have an unfinished fill task stored from a previous run, load it up
ConfigurationSection storedFillTask = cfg.getConfigurationSection("fillTask"); ConfigurationSection storedFillTask = cfg.getConfigurationSection("fillTask");
if (storedFillTask != null) if (storedFillTask != null) {
{
String worldName = storedFillTask.getString("world"); String worldName = storedFillTask.getString("world");
int fillDistance = storedFillTask.getInt("fillDistance", 176); int fillDistance = storedFillTask.getInt("fillDistance", 176);
int chunksPerRun = storedFillTask.getInt("chunksPerRun", 5); int chunksPerRun = storedFillTask.getInt("chunksPerRun", 5);
@ -701,12 +606,11 @@ public class Config
save(false); save(false);
} }
public static void save(boolean logIt) public static void save(boolean logIt) {
{
save(logIt, false); save(logIt, false);
} }
public static void save(boolean logIt, boolean storeFillTask)
{ // save config to file public static void save(boolean logIt, boolean storeFillTask) { // save config to file
if (cfg == null) return; if (cfg == null) return;
cfg.set("cfg-version", currentCfgVersion); cfg.set("cfg-version", currentCfgVersion);
@ -731,8 +635,7 @@ public class Config
cfg.set("prevent-mob-spawn", preventMobSpawn); cfg.set("prevent-mob-spawn", preventMobSpawn);
cfg.set("worlds", null); cfg.set("worlds", null);
for(Entry<String, BorderData> stringBorderDataEntry : borders.entrySet()) for (Entry<String, BorderData> stringBorderDataEntry : borders.entrySet()) {
{
String name = stringBorderDataEntry.getKey().replace(".", "<"); String name = stringBorderDataEntry.getKey().replace(".", "<");
BorderData bord = stringBorderDataEntry.getValue(); BorderData bord = stringBorderDataEntry.getValue();
@ -746,8 +649,7 @@ public class Config
cfg.set("worlds." + name + ".shape-round", bord.getShape()); cfg.set("worlds." + name + ".shape-round", bord.getShape());
} }
if (storeFillTask && fillTask != null && fillTask.valid()) if (storeFillTask && fillTask != null && fillTask.valid()) {
{
cfg.set("fillTask.world", fillTask.refWorld()); cfg.set("fillTask.world", fillTask.refWorld());
cfg.set("fillTask.fillDistance", fillTask.refFillDistance()); cfg.set("fillTask.fillDistance", fillTask.refFillDistance());
cfg.set("fillTask.chunksPerRun", fillTask.refChunksPerRun()); cfg.set("fillTask.chunksPerRun", fillTask.refChunksPerRun());
@ -757,8 +659,7 @@ public class Config
cfg.set("fillTask.length", fillTask.refLength()); cfg.set("fillTask.length", fillTask.refLength());
cfg.set("fillTask.total", fillTask.refTotal()); cfg.set("fillTask.total", fillTask.refTotal());
cfg.set("fillTask.forceLoad", fillTask.refForceLoad()); cfg.set("fillTask.forceLoad", fillTask.refForceLoad());
} } else
else
cfg.set("fillTask", null); cfg.set("fillTask", null);
plugin.saveConfig(); plugin.saveConfig();

View File

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

View File

@ -1,76 +1,71 @@
package com.wimbli.WorldBorder; 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.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.dynmap.DynmapAPI; import org.dynmap.DynmapAPI;
import org.dynmap.markers.AreaMarker; import org.dynmap.markers.AreaMarker;
import org.dynmap.markers.CircleMarker; import org.dynmap.markers.CircleMarker;
import org.dynmap.markers.MarkerAPI; import org.dynmap.markers.MarkerAPI;
import org.dynmap.markers.MarkerSet; import org.dynmap.markers.MarkerSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class DynMapFeatures
{ public class DynMapFeatures {
private static DynmapAPI api; private static DynmapAPI api;
private static MarkerAPI markApi; private static MarkerAPI markApi;
private static MarkerSet markSet; private static MarkerSet markSet;
private static int lineWeight = 3; private static int lineWeight = 3;
private static double lineOpacity = 1.0; private static double lineOpacity = 1.0;
private static int lineColor = 0xFF0000; 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>();
// Whether re-rendering functionality is available // Whether re-rendering functionality is available
public static boolean renderEnabled() public static boolean renderEnabled() {
{
return api != null; return api != null;
} }
/*
* 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.
*/
// Whether circular border markers are available // Whether circular border markers are available
public static boolean borderEnabled() public static boolean borderEnabled() {
{
return markApi != null; return markApi != null;
} }
public static void setup() public static void setup() {
{
Plugin test = Bukkit.getServer().getPluginManager().getPlugin("dynmap"); Plugin test = Bukkit.getServer().getPluginManager().getPlugin("dynmap");
if (test == null || !test.isEnabled()) return; if (test == null || !test.isEnabled()) return;
api = (DynmapAPI) test; api = (DynmapAPI) test;
// make sure DynMap version is new enough to include circular markers // make sure DynMap version is new enough to include circular markers
try try {
{
Class.forName("org.dynmap.markers.CircleMarker"); 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) // 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-")) if (api.getDynmapVersion().startsWith("0.35-"))
throw new ClassNotFoundException(); throw new ClassNotFoundException();
} } catch (ClassNotFoundException ex) {
catch (ClassNotFoundException ex)
{
Config.logConfig("DynMap is available, but border display is currently disabled: you need DynMap v0.36 or newer."); Config.logConfig("DynMap is available, but border display is currently disabled: you need DynMap v0.36 or newer.");
return; return;
} } catch (NullPointerException ex) {
catch (NullPointerException ex)
{
Config.logConfig("DynMap is present, but an NPE (type 1) was encountered while trying to integrate. Border display disabled."); Config.logConfig("DynMap is present, but an NPE (type 1) was encountered while trying to integrate. Border display disabled.");
return; return;
} }
try try {
{
markApi = api.getMarkerAPI(); markApi = api.getMarkerAPI();
if (markApi == null) return; if (markApi == null) return;
} } catch (NullPointerException ex) {
catch (NullPointerException ex)
{
Config.logConfig("DynMap is present, but an NPE (type 2) was encountered while trying to integrate. Border display disabled."); Config.logConfig("DynMap is present, but an NPE (type 2) was encountered while trying to integrate. Border display disabled.");
return; return;
} }
@ -81,14 +76,7 @@ public class DynMapFeatures
Config.logConfig("Successfully hooked into DynMap for the ability to display borders."); Config.logConfig("Successfully hooked into DynMap for the ability to display borders.");
} }
public static void renderRegion(String worldName, CoordXZ coord) {
/*
* 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; if (!renderEnabled()) return;
World world = Bukkit.getWorld(worldName); World world = Bukkit.getWorld(worldName);
@ -98,21 +86,23 @@ public class DynMapFeatures
api.triggerRenderOfVolume(worldName, x, 0, z, x + 511, y, z + 511); api.triggerRenderOfVolume(worldName, x, 0, z, x + 511, y, z + 511);
} }
public static void renderChunks(String worldName, List<CoordXZ> coords)
{ /*
* Methods for displaying our borders on DynMap's world maps
*/
public static void renderChunks(String worldName, List<CoordXZ> coords) {
if (!renderEnabled()) return; if (!renderEnabled()) return;
World world = Bukkit.getWorld(worldName); World world = Bukkit.getWorld(worldName);
int y = (world != null) ? world.getMaxHeight() : 255; int y = (world != null) ? world.getMaxHeight() : 255;
for (CoordXZ coord : coords) for (CoordXZ coord : coords) {
{
renderChunk(worldName, coord, y); renderChunk(worldName, coord, y);
} }
} }
public static void renderChunk(String worldName, CoordXZ coord, int maxY) public static void renderChunk(String worldName, CoordXZ coord, int maxY) {
{
if (!renderEnabled()) return; if (!renderEnabled()) return;
int x = CoordXZ.chunkToBlock(coord.x); int x = CoordXZ.chunkToBlock(coord.x);
@ -120,23 +110,13 @@ public class DynMapFeatures
api.triggerRenderOfVolume(worldName, x, 0, z, x + 15, maxY, z + 15); api.triggerRenderOfVolume(worldName, x, 0, z, x + 15, maxY, z + 15);
} }
public static void showAllBorders() {
/*
* Methods for displaying our borders on DynMap's world maps
*/
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; if (!borderEnabled()) return;
// in case any borders are already shown // in case any borders are already shown
removeAllBorders(); removeAllBorders();
if (!Config.DynmapBorderEnabled()) if (!Config.DynmapBorderEnabled()) {
{
// don't want to show the marker set in DynMap if our integration is disabled // don't want to show the marker set in DynMap if our integration is disabled
if (markSet != null) if (markSet != null)
markSet.deleteMarkerSet(); markSet.deleteMarkerSet();
@ -153,16 +133,14 @@ public class DynMapFeatures
markSet.setLayerPriority(Config.DynmapPriority()); markSet.setLayerPriority(Config.DynmapPriority());
markSet.setHideByDefault(Config.DynmapHideByDefault()); markSet.setHideByDefault(Config.DynmapHideByDefault());
Map<String, BorderData> borders = Config.getBorders(); Map<String, BorderData> borders = Config.getBorders();
for(Entry<String, BorderData> stringBorderDataEntry : borders.entrySet()) for (Entry<String, BorderData> stringBorderDataEntry : borders.entrySet()) {
{
String worldName = stringBorderDataEntry.getKey(); String worldName = stringBorderDataEntry.getKey();
BorderData border = stringBorderDataEntry.getValue(); BorderData border = stringBorderDataEntry.getValue();
showBorder(worldName, border); showBorder(worldName, border);
} }
} }
public static void showBorder(String worldName, BorderData border) public static void showBorder(String worldName, BorderData border) {
{
if (!borderEnabled()) return; if (!borderEnabled()) return;
if (!Config.DynmapBorderEnabled()) return; if (!Config.DynmapBorderEnabled()) return;
@ -173,8 +151,7 @@ public class DynMapFeatures
showSquareBorder(worldName, border); showSquareBorder(worldName, border);
} }
private static void showRoundBorder(String worldName, BorderData border) private static void showRoundBorder(String worldName, BorderData border) {
{
if (squareBorders.containsKey(worldName)) if (squareBorders.containsKey(worldName))
removeBorder(worldName); removeBorder(worldName);
@ -182,22 +159,18 @@ public class DynMapFeatures
int y = (world != null) ? world.getMaxHeight() : 255; int y = (world != null) ? world.getMaxHeight() : 255;
CircleMarker marker = roundBorders.get(worldName); CircleMarker marker = roundBorders.get(worldName);
if (marker == null) if (marker == null) {
{
marker = markSet.createCircleMarker("worldborder_" + worldName, Config.DynmapMessage(), false, worldName, border.getX(), y, border.getZ(), border.getRadiusX(), border.getRadiusZ(), true); 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.setLineStyle(lineWeight, lineOpacity, lineColor);
marker.setFillStyle(0.0, 0x000000); marker.setFillStyle(0.0, 0x000000);
roundBorders.put(worldName, marker); roundBorders.put(worldName, marker);
} } else {
else
{
marker.setCenter(worldName, border.getX(), y, border.getZ()); marker.setCenter(worldName, border.getX(), y, border.getZ());
marker.setRadius(border.getRadiusX(), border.getRadiusZ()); marker.setRadius(border.getRadiusX(), border.getRadiusZ());
} }
} }
private static void showSquareBorder(String worldName, BorderData border) private static void showSquareBorder(String worldName, BorderData border) {
{
if (roundBorders.containsKey(worldName)) if (roundBorders.containsKey(worldName))
removeBorder(worldName); removeBorder(worldName);
@ -206,38 +179,31 @@ public class DynMapFeatures
double[] zVals = {border.getZ() - border.getRadiusZ(), border.getZ() + border.getRadiusZ()}; double[] zVals = {border.getZ() - border.getRadiusZ(), border.getZ() + border.getRadiusZ()};
AreaMarker marker = squareBorders.get(worldName); AreaMarker marker = squareBorders.get(worldName);
if (marker == null) if (marker == null) {
{
marker = markSet.createAreaMarker("worldborder_" + worldName, Config.DynmapMessage(), false, worldName, xVals, zVals, true); marker = markSet.createAreaMarker("worldborder_" + worldName, Config.DynmapMessage(), false, worldName, xVals, zVals, true);
marker.setLineStyle(3, 1.0, 0xFF0000); marker.setLineStyle(3, 1.0, 0xFF0000);
marker.setFillStyle(0.0, 0x000000); marker.setFillStyle(0.0, 0x000000);
squareBorders.put(worldName, marker); squareBorders.put(worldName, marker);
} } else {
else
{
marker.setCornerLocations(xVals, zVals); marker.setCornerLocations(xVals, zVals);
} }
} }
public static void removeAllBorders() public static void removeAllBorders() {
{
if (!borderEnabled()) return; if (!borderEnabled()) return;
for(CircleMarker marker : roundBorders.values()) for (CircleMarker marker : roundBorders.values()) {
{
marker.deleteMarker(); marker.deleteMarker();
} }
roundBorders.clear(); roundBorders.clear();
for(AreaMarker marker : squareBorders.values()) for (AreaMarker marker : squareBorders.values()) {
{
marker.deleteMarker(); marker.deleteMarker();
} }
squareBorders.clear(); squareBorders.clear();
} }
public static void removeBorder(String worldName) public static void removeBorder(String worldName) {
{
if (!borderEnabled()) return; if (!borderEnabled()) return;
CircleMarker marker = roundBorders.remove(worldName); CircleMarker marker = roundBorders.remove(worldName);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,6 +4,9 @@
package com.wimbli.WorldBorder.UUID; package com.wimbli.WorldBorder.UUID;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
@ -16,24 +19,18 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class UUIDFetcher { public class UUIDFetcher {
/** /**
* Date when name changes were introduced * Date when name changes were introduced
*
* @see UUIDFetcher#getUUIDAt(String, long) * @see UUIDFetcher#getUUIDAt(String, long)
*/ */
public static final long FEBRUARY_2015 = 1422748800000L; 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 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 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<String, UUID> uuidCache = new HashMap<String, UUID>();
private static Map<UUID, String> nameCache = new HashMap<UUID, String>(); private static Map<UUID, String> nameCache = new HashMap<UUID, String>();
@ -140,8 +137,7 @@ public class UUIDFetcher {
public static Map<UUID, String> getNameList(ArrayList<UUID> uuids) { public static Map<UUID, String> getNameList(ArrayList<UUID> uuids) {
Map<UUID, String> uuidStringMap = new HashMap<>(); Map<UUID, String> uuidStringMap = new HashMap<>();
for (UUID uuid: uuids) for (UUID uuid : uuids) {
{
uuidStringMap.put(uuid, getName(uuid)); uuidStringMap.put(uuid, getName(uuid));
} }
return uuidStringMap; return uuidStringMap;

View File

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

View File

@ -1,31 +1,24 @@
package com.wimbli.WorldBorder; package com.wimbli.WorldBorder;
import java.util.ArrayList; import com.wimbli.WorldBorder.cmd.*;
import java.util.Arrays; import org.bukkit.command.Command;
import java.util.Iterator; import org.bukkit.command.CommandExecutor;
import java.util.LinkedHashMap; import org.bukkit.command.CommandSender;
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 org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.cmd.*; import java.util.*;
public class WBCommand implements CommandExecutor public class WBCommand implements CommandExecutor {
{
// map of all sub-commands with the command name (string) for quick reference // map of all sub-commands with the command name (string) for quick reference
public Map<String, WBCmd> subCommands = new LinkedHashMap<String, WBCmd>(); 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) // 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 Set<String> subCommandsWithWorldNames = new LinkedHashSet<String>();
private boolean wasWorldQuotation = false;
// constructor // constructor
public WBCommand () public WBCommand() {
{
addCmd(new CmdHelp()); // 1 example addCmd(new CmdHelp()); // 1 example
addCmd(new CmdSet()); // 4 examples for player, 3 for console addCmd(new CmdSet()); // 4 examples for player, 3 for console
addCmd(new CmdSetcorners()); // 1 addCmd(new CmdSetcorners()); // 1
@ -63,17 +56,14 @@ public class WBCommand implements CommandExecutor
addCmd(new CmdCommands()); addCmd(new CmdCommands());
} }
private void addCmd(WBCmd cmd) {
private void addCmd(WBCmd cmd)
{
subCommands.put(cmd.name, cmd); subCommands.put(cmd.name, cmd);
if (cmd.hasWorldNameInput) if (cmd.hasWorldNameInput)
subCommandsWithWorldNames.add(cmd.name); subCommandsWithWorldNames.add(cmd.name);
} }
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] split) public boolean onCommand(CommandSender sender, Command command, String label, String[] split) {
{
Player player = (sender instanceof Player) ? (Player) sender : null; 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[] // if world name is passed inside quotation marks, handle that, and get List<String> instead of String[]
@ -97,15 +87,11 @@ public class WBCommand implements CommandExecutor
params.remove(0); params.remove(0);
// make sure command is recognized, default to showing command examples / help if not; also check for specified page number // make sure command is recognized, default to showing command examples / help if not; also check for specified page number
if (!subCommands.containsKey(cmdName)) if (!subCommands.containsKey(cmdName)) {
{
int page = (player == null) ? 0 : 1; int page = (player == null) ? 0 : 1;
try try {
{
page = Integer.parseInt(cmdName); page = Integer.parseInt(cmdName);
} } catch (NumberFormatException ignored) {
catch(NumberFormatException ignored)
{
sender.sendMessage(WBCmd.C_ERR + "Command not recognized. Showing command list."); sender.sendMessage(WBCmd.C_ERR + "Command not recognized. Showing command list.");
} }
cmdName = "commands"; cmdName = "commands";
@ -119,16 +105,14 @@ public class WBCommand implements CommandExecutor
return true; return true;
// if command requires world name when run by console, make sure that's in place // if command requires world name when run by console, make sure that's in place
if (player == null && subCommand.hasWorldNameInput && subCommand.consoleRequiresWorldName && worldName == null) 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."); sender.sendMessage(WBCmd.C_ERR + "This command requires a world to be specified if run by the console.");
subCommand.sendCmdHelp(sender); subCommand.sendCmdHelp(sender);
return true; return true;
} }
// make sure valid number of parameters has been provided // make sure valid number of parameters has been provided
if (params.size() < subCommand.minParams || params.size() > subCommand.maxParams) if (params.size() < subCommand.minParams || params.size() > subCommand.maxParams) {
{
if (subCommand.maxParams == 0) if (subCommand.maxParams == 0)
sender.sendMessage(WBCmd.C_ERR + "This command does not accept any parameters."); sender.sendMessage(WBCmd.C_ERR + "This command does not accept any parameters.");
else else
@ -143,21 +127,15 @@ public class WBCommand implements CommandExecutor
return true; return true;
} }
private boolean wasWorldQuotation = false;
// if world name is surrounded by quotation marks, combine it down and flag wasWorldQuotation if it's first param. // 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[] // also return List<String> instead of input primitive String[]
private List<String> concatenateQuotedWorldName(String[] split) private List<String> concatenateQuotedWorldName(String[] split) {
{
wasWorldQuotation = false; wasWorldQuotation = false;
List<String> args = new ArrayList<String>(Arrays.asList(split)); List<String> args = new ArrayList<String>(Arrays.asList(split));
int startIndex = -1; int startIndex = -1;
for (int i = 0; i < args.size(); i++) for (int i = 0; i < args.size(); i++) {
{ if (args.get(i).startsWith("\"")) {
if (args.get(i).startsWith("\""))
{
startIndex = i; startIndex = i;
break; break;
} }
@ -165,32 +143,26 @@ public class WBCommand implements CommandExecutor
if (startIndex == -1) if (startIndex == -1)
return args; return args;
if (args.get(startIndex).endsWith("\"")) if (args.get(startIndex).endsWith("\"")) {
{
args.set(startIndex, args.get(startIndex).substring(1, args.get(startIndex).length() - 1)); args.set(startIndex, args.get(startIndex).substring(1, args.get(startIndex).length() - 1));
if (startIndex == 0) if (startIndex == 0)
wasWorldQuotation = true; wasWorldQuotation = true;
} } else {
else
{
List<String> concat = new ArrayList<String>(args); List<String> concat = new ArrayList<String>(args);
Iterator<String> concatI = concat.iterator(); Iterator<String> concatI = concat.iterator();
// skip past any parameters in front of the one we're starting on // skip past any parameters in front of the one we're starting on
for (int i = 1; i < startIndex + 1; i++) for (int i = 1; i < startIndex + 1; i++) {
{
concatI.next(); concatI.next();
} }
StringBuilder quote = new StringBuilder(concatI.next()); StringBuilder quote = new StringBuilder(concatI.next());
while (concatI.hasNext()) while (concatI.hasNext()) {
{
String next = concatI.next(); String next = concatI.next();
concatI.remove(); concatI.remove();
quote.append(" "); quote.append(" ");
quote.append(next); quote.append(next);
if (next.endsWith("\"")) if (next.endsWith("\"")) {
{
concat.set(startIndex, quote.substring(1, quote.length() - 1)); concat.set(startIndex, quote.substring(1, quote.length() - 1));
args = concat; args = concat;
if (startIndex == 0) if (startIndex == 0)
@ -202,8 +174,7 @@ public class WBCommand implements CommandExecutor
return args; return args;
} }
public Set<String> getCommandNames() public Set<String> getCommandNames() {
{
// using TreeSet to sort alphabetically // using TreeSet to sort alphabetically
Set<String> commands = new TreeSet<>(subCommands.keySet()); Set<String> commands = new TreeSet<>(subCommands.keySet());
// removing default "commands" command as it's not normally shown or run like other commands // removing default "commands" command as it's not normally shown or run like other commands

View File

@ -1,21 +1,19 @@
package com.wimbli.WorldBorder; package com.wimbli.WorldBorder;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.Location;
public class WBListener implements Listener public class WBListener implements Listener {
{
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event) public void onPlayerTeleport(PlayerTeleportEvent event) {
{
// if knockback is set to 0, simply return // if knockback is set to 0, simply return
if (Config.KnockBack() == 0.0) if (Config.KnockBack() == 0.0)
return; return;
@ -24,10 +22,8 @@ public class WBListener implements Listener
Config.log("Teleport cause: " + event.getCause().toString()); Config.log("Teleport cause: " + event.getCause().toString());
Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, true); Location newLoc = BorderCheckTask.checkPlayer(event.getPlayer(), event.getTo(), true, true);
if (newLoc != null) if (newLoc != null) {
{ if (event.getCause() == PlayerTeleportEvent.TeleportCause.ENDER_PEARL && Config.getDenyEnderpearl()) {
if(event.getCause() == PlayerTeleportEvent.TeleportCause.ENDER_PEARL && Config.getDenyEnderpearl())
{
event.setCancelled(true); event.setCancelled(true);
return; return;
} }
@ -37,8 +33,7 @@ public class WBListener implements Listener
} }
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerPortal(PlayerPortalEvent event) public void onPlayerPortal(PlayerPortalEvent event) {
{
// if knockback is set to 0, or portal redirection is disabled, simply return // if knockback is set to 0, or portal redirection is disabled, simply return
if (Config.KnockBack() == 0.0 || !Config.portalRedirection()) if (Config.KnockBack() == 0.0 || !Config.portalRedirection())
return; return;
@ -49,8 +44,7 @@ public class WBListener implements Listener
} }
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onChunkLoad(ChunkLoadEvent event) public void onChunkLoad(ChunkLoadEvent event) {
{
/* // tested, found to spam pretty rapidly as client repeatedly requests the same chunks since they're not being sent /* // 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 // 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 // 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
@ -77,8 +71,7 @@ public class WBListener implements Listener
* and track if chunk was somehow on unload prevention list * and track if chunk was somehow on unload prevention list
*/ */
@EventHandler @EventHandler
public void onChunkUnload(ChunkUnloadEvent e) public void onChunkUnload(ChunkUnloadEvent e) {
{
if (Config.fillTask == null) if (Config.fillTask == null)
return; return;

View File

@ -4,16 +4,14 @@ import org.bukkit.Location;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
public class WorldBorder extends JavaPlugin public class WorldBorder extends JavaPlugin {
{
public static volatile WorldBorder plugin = null; public static volatile WorldBorder plugin = null;
public static volatile WBCommand wbCommand = null; public static volatile WBCommand wbCommand = null;
private BlockPlaceListener blockPlaceListener = null; private BlockPlaceListener blockPlaceListener = null;
private MobSpawnListener mobSpawnListener = null; private MobSpawnListener mobSpawnListener = null;
@Override @Override
public void onEnable() public void onEnable() {
{
if (plugin == null) if (plugin == null)
plugin = this; plugin = this;
if (wbCommand == null) if (wbCommand == null)
@ -43,8 +41,7 @@ public class WorldBorder extends JavaPlugin
} }
@Override @Override
public void onDisable() public void onDisable() {
{
DynMapFeatures.removeAllBorders(); DynMapFeatures.removeAllBorders();
Config.StopBorderTimer(); Config.StopBorderTimer();
Config.StoreFillTask(); Config.StoreFillTask();
@ -52,8 +49,7 @@ public class WorldBorder extends JavaPlugin
} }
// for other plugins to hook into // for other plugins to hook into
public BorderData getWorldBorder(String worldName) public BorderData getWorldBorder(String worldName) {
{
return Config.Border(worldName); return Config.Border(worldName);
} }
@ -61,21 +57,18 @@ public class WorldBorder extends JavaPlugin
* @deprecated Replaced by {@link #getWorldBorder(String worldName)}; * @deprecated Replaced by {@link #getWorldBorder(String worldName)};
* this method name starts with an uppercase letter, which it shouldn't * this method name starts with an uppercase letter, which it shouldn't
*/ */
public BorderData GetWorldBorder(String worldName) public BorderData GetWorldBorder(String worldName) {
{
return getWorldBorder(worldName); return getWorldBorder(worldName);
} }
public void enableBlockPlaceListener(boolean enable) public void enableBlockPlaceListener(boolean enable) {
{
if (enable) if (enable)
getServer().getPluginManager().registerEvents(this.blockPlaceListener = new BlockPlaceListener(), this); getServer().getPluginManager().registerEvents(this.blockPlaceListener = new BlockPlaceListener(), this);
else if (blockPlaceListener != null) else if (blockPlaceListener != null)
blockPlaceListener.unregister(); blockPlaceListener.unregister();
} }
public void enableMobSpawnListener(boolean enable) public void enableMobSpawnListener(boolean enable) {
{
if (enable) if (enable)
getServer().getPluginManager().registerEvents(this.mobSpawnListener = new MobSpawnListener(), this); getServer().getPluginManager().registerEvents(this.mobSpawnListener = new MobSpawnListener(), this);
else if (mobSpawnListener != null) else if (mobSpawnListener != null)

View File

@ -1,54 +1,51 @@
package com.wimbli.WorldBorder; 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.io.*;
import java.util.ArrayList;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.*;
import org.bukkit.entity.Player;
import org.bukkit.World;
// image output stuff, for debugging method at bottom of this file // 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/ // 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 public class WorldFileData {
{
private transient World world; private transient World world;
private transient File regionFolder = null; private transient File regionFolder = null;
private transient File[] regionFiles = null; private transient File[] regionFiles = null;
private transient Player notifyPlayer = null; private transient Player notifyPlayer = null;
private transient Map<CoordXZ, List<Boolean>> regionChunkExistence = Collections.synchronizedMap(new HashMap<CoordXZ, List<Boolean>>()); private transient Map<CoordXZ, List<Boolean>> regionChunkExistence = Collections.synchronizedMap(new HashMap<CoordXZ, List<Boolean>>());
// 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;
}
// 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. // 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) public static WorldFileData create(World world, Player notifyPlayer) {
{
WorldFileData newData = new WorldFileData(world, notifyPlayer); WorldFileData newData = new WorldFileData(world, notifyPlayer);
newData.regionFolder = new File(newData.world.getWorldFolder(), "region"); newData.regionFolder = new File(newData.world.getWorldFolder(), "region");
if (!newData.regionFolder.exists() || !newData.regionFolder.isDirectory()) 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) // 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()); File[] possibleDimFolders = newData.world.getWorldFolder().listFiles(new DimFolderFileFilter());
for (File possibleDimFolder : possibleDimFolders) for (File possibleDimFolder : possibleDimFolders) {
{
File possible = new File(newData.world.getWorldFolder(), possibleDimFolder.getName() + File.separator + "region"); File possible = new File(newData.world.getWorldFolder(), possibleDimFolder.getName() + File.separator + "region");
if (possible.exists() && possible.isDirectory()) if (possible.exists() && possible.isDirectory()) {
{
newData.regionFolder = possible; newData.regionFolder = possible;
break; break;
} }
} }
if (!newData.regionFolder.exists() || !newData.regionFolder.isDirectory()) 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."); 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 null;
} }
@ -56,11 +53,9 @@ public class WorldFileData
// Accepted region file formats: MCR is from late beta versions through 1.1, MCA is from 1.2+ // 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")); newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCA"));
if (newData.regionFiles == null || newData.regionFiles.length == 0) if (newData.regionFiles == null || newData.regionFiles.length == 0) {
{
newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCR")); newData.regionFiles = newData.regionFolder.listFiles(new ExtFileFilter(".MCR"));
if (newData.regionFiles == null || newData.regionFiles.length == 0) if (newData.regionFiles == null || newData.regionFiles.length == 0) {
{
newData.sendMessage("Could not find any region files. Looked in: " + newData.regionFolder.getPath()); newData.sendMessage("Could not find any region files. Looked in: " + newData.regionFolder.getPath());
return null; return null;
} }
@ -69,54 +64,38 @@ public class WorldFileData
return newData; return newData;
} }
// 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;
}
// number of region files this world has // number of region files this world has
public int regionFileCount() public int regionFileCount() {
{
return regionFiles.length; return regionFiles.length;
} }
// folder where world's region files are located // folder where world's region files are located
public File regionFolder() public File regionFolder() {
{
return regionFolder; return regionFolder;
} }
// return entire list of region files // return entire list of region files
public File[] regionFiles() public File[] regionFiles() {
{
return regionFiles.clone(); return regionFiles.clone();
} }
// return a region file by index // return a region file by index
public File regionFile(int index) public File regionFile(int index) {
{
if (regionFiles.length < index) if (regionFiles.length < index)
return null; return null;
return regionFiles[index]; return regionFiles[index];
} }
// get the X and Z world coordinates of the region from the filename // get the X and Z world coordinates of the region from the filename
public CoordXZ regionFileCoordinates(int index) public CoordXZ regionFileCoordinates(int index) {
{
File regionFile = this.regionFile(index); File regionFile = this.regionFile(index);
String[] coords = regionFile.getName().split("\\."); String[] coords = regionFile.getName().split("\\.");
int x, z; int x, z;
try try {
{
x = Integer.parseInt(coords[1]); x = Integer.parseInt(coords[1]);
z = Integer.parseInt(coords[2]); z = Integer.parseInt(coords[2]);
return new CoordXZ(x, z); return new CoordXZ(x, z);
} } catch (Exception ex) {
catch(Exception ex)
{
sendMessage("Error! Region file found with abnormal name: " + regionFile.getName()); sendMessage("Error! Region file found with abnormal name: " + regionFile.getName());
return null; return null;
} }
@ -124,8 +103,7 @@ public class WorldFileData
// Find out if the chunk at the given coordinates exists. // Find out if the chunk at the given coordinates exists.
public boolean doesChunkExist(int x, int z) public boolean doesChunkExist(int x, int z) {
{
CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z)); CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z));
List<Boolean> regionChunks = this.getRegionData(region); List<Boolean> regionChunks = this.getRegionData(region);
// Bukkit.getLogger().info("x: "+x+" z: "+z+" offset: "+coordToRegionOffset(x, z)); // Bukkit.getLogger().info("x: "+x+" z: "+z+" offset: "+coordToRegionOffset(x, z));
@ -134,13 +112,10 @@ public class WorldFileData
// Find out if the chunk at the given coordinates has been fully generated. // 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. // Minecraft only fully generates a chunk when adjacent chunks are also loaded.
public boolean isChunkFullyGenerated(int x, int z) 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
{ // 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 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 xx = x - 3; xx <= x + 3; xx++) {
{ for (int zz = z - 3; zz <= z + 3; zz++) {
for (int zz = z-3; zz <= z+3; zz++)
{
if (!doesChunkExist(xx, zz)) if (!doesChunkExist(xx, zz))
return false; return false;
} }
@ -149,19 +124,16 @@ public class WorldFileData
} }
// Method to let us know a chunk has been generated, to update our region map. // Method to let us know a chunk has been generated, to update our region map.
public void chunkExistsNow(int x, int z) public void chunkExistsNow(int x, int z) {
{
CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z)); CoordXZ region = new CoordXZ(CoordXZ.chunkToRegion(x), CoordXZ.chunkToRegion(z));
List<Boolean> regionChunks = this.getRegionData(region); List<Boolean> regionChunks = this.getRegionData(region);
regionChunks.set(coordToRegionOffset(x, z), true); 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) // 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 // 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) private int coordToRegionOffset(int x, int z) {
{
// "%" modulus is used to convert potential world coordinates to definitely be local region coordinates // "%" modulus is used to convert potential world coordinates to definitely be local region coordinates
x = x % 32; x = x % 32;
z = z % 32; z = z % 32;
@ -172,35 +144,30 @@ public class WorldFileData
return (x + (z * 32)); return (x + (z * 32));
} }
private List<Boolean> getRegionData(CoordXZ region) private List<Boolean> getRegionData(CoordXZ region) {
{
List<Boolean> data = regionChunkExistence.get(region); List<Boolean> data = regionChunkExistence.get(region);
if (data != null) if (data != null)
return data; 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 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); data = new ArrayList<Boolean>(1024);
for (int i = 0; i < 1024; i++) for (int i = 0; i < 1024; i++) {
{
data.add(Boolean.FALSE); data.add(Boolean.FALSE);
} }
for (int i = 0; i < regionFiles.length; i++) for (int i = 0; i < regionFiles.length; i++) {
{
CoordXZ coord = regionFileCoordinates(i); CoordXZ coord = regionFileCoordinates(i);
// is this region file the one we're looking for? // is this region file the one we're looking for?
if (!coord.equals(region)) if (!coord.equals(region))
continue; continue;
try try {
{
RandomAccessFile regionData = new RandomAccessFile(this.regionFile(i), "r"); RandomAccessFile regionData = new RandomAccessFile(this.regionFile(i), "r");
// Use of ByteBuffer+IntBuffer for reading file headers to improve performance, as suggested by aikar, reference: // 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 // 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); ByteBuffer header = ByteBuffer.allocate(8192);
while (header.hasRemaining()) while (header.hasRemaining()) {
{
if (regionData.getChannel().read(header) == -1) if (regionData.getChannel().read(header) == -1)
throw new EOFException(); throw new EOFException();
} }
@ -208,27 +175,21 @@ public class WorldFileData
IntBuffer headerAsInts = header.asIntBuffer(); 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) // 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++) for (int j = 0; j < 1024; j++) {
{
// if chunk pointer data is 0, chunk doesn't exist yet; otherwise, it does // if chunk pointer data is 0, chunk doesn't exist yet; otherwise, it does
if (headerAsInts.get() != 0) if (headerAsInts.get() != 0)
data.set(j, true); data.set(j, true);
} }
// Read timestamps // Read timestamps
for (int j = 0; j < 1024; j++) for (int j = 0; j < 1024; j++) {
{
// if timestamp is zero, it is protochunk (ignore it) // if timestamp is zero, it is protochunk (ignore it)
if ((headerAsInts.get() == 0) && data.get(j)) if ((headerAsInts.get() == 0) && data.get(j))
data.set(j, false); data.set(j, false);
} }
regionData.close(); regionData.close();
} } catch (FileNotFoundException ex) {
catch (FileNotFoundException ex)
{
sendMessage("Error! Could not open region file to find generated chunks: " + this.regionFile(i).getName()); sendMessage("Error! Could not open region file to find generated chunks: " + this.regionFile(i).getName());
} } catch (IOException ex) {
catch (IOException ex)
{
sendMessage("Error! Could not read region file to find generated chunks: " + this.regionFile(i).getName()); sendMessage("Error! Could not read region file to find generated chunks: " + this.regionFile(i).getName());
} }
} }
@ -238,48 +199,12 @@ public class WorldFileData
} }
// send a message to the server console/log and possibly to an in-game player // send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text) private void sendMessage(String text) {
{
Config.log("[WorldData] " + text); Config.log("[WorldData] " + text);
if (notifyPlayer != null && notifyPlayer.isOnline()) if (notifyPlayer != null && notifyPlayer.isOnline())
notifyPlayer.sendMessage("[WorldData] " + text); 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();
}
@Override
public boolean accept(File file)
{
return (
file.exists()
&& file.isFile()
&& file.getName().toLowerCase().endsWith(ext)
);
}
}
// 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")
);
}
}
// crude chunk map PNG image output, for debugging // crude chunk map PNG image output, for debugging
private void testImage(CoordXZ region, List<Boolean> data) { private void testImage(CoordXZ region, List<Boolean> data) {
int width = 32; int width = 32;
@ -289,10 +214,8 @@ public class WorldFileData
int current = 0; int current = 0;
g2.setColor(Color.BLACK); g2.setColor(Color.BLACK);
for (int x = 0; x < 32; x++) for (int x = 0; x < 32; x++) {
{ for (int z = 0; z < 32; z++) {
for (int z = 0; z < 32; z++)
{
if (data.get(current).booleanValue()) if (data.get(current).booleanValue())
g2.fillRect(x, z, x + 1, z + 1); g2.fillRect(x, z, x + 1, z + 1);
current++; current++;
@ -308,4 +231,34 @@ public class WorldFileData
Config.log("[SEVERE]" + ex.getLocalizedMessage()); Config.log("[SEVERE]" + ex.getLocalizedMessage());
} }
} }
// file filter used for region files
private static class ExtFileFilter implements FileFilter {
String ext;
public ExtFileFilter(String extension) {
this.ext = extension.toLowerCase();
}
@Override
public boolean accept(File file) {
return (
file.exists()
&& file.isFile()
&& file.getName().toLowerCase().endsWith(ext)
);
}
}
// 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")
);
}
}
} }

View File

@ -1,25 +1,22 @@
package com.wimbli.WorldBorder; package com.wimbli.WorldBorder;
import java.util.concurrent.CompletableFuture; import com.wimbli.WorldBorder.Events.WorldBorderFillFinishedEvent;
import com.wimbli.WorldBorder.Events.WorldBorderFillStartEvent;
import io.papermc.lib.PaperLib;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.entity.Player;
import org.bukkit.Server;
import org.bukkit.World;
import io.papermc.lib.PaperLib;
import com.wimbli.WorldBorder.Events.WorldBorderFillFinishedEvent;
import com.wimbli.WorldBorder.Events.WorldBorderFillStartEvent;
public class WorldFillTask implements Runnable public class WorldFillTask implements Runnable {
{
// general task-related reference data // general task-related reference data
private transient Server server = null; private transient Server server = null;
private transient World world = null; private transient World world = null;
@ -67,45 +64,7 @@ public class WorldFillTask implements Runnable
// several others. // several others.
private transient Set<UnloadDependency> preventUnload; private transient Set<UnloadDependency> preventUnload;
private class UnloadDependency public WorldFillTask(Server theServer, Player player, String worldName, int fillDistance, int chunksPerRun, int tickFrequency, boolean forceLoad) {
{
int neededX, neededZ;
int forX, forZ;
UnloadDependency(int neededX, int neededZ, int forX, int forZ)
{
this.neededX=neededX;
this.neededZ=neededZ;
this.forX=forX;
this.forZ=forZ;
}
@Override
public boolean equals(Object other)
{
if (other == null || !(other instanceof UnloadDependency))
return false;
return this.neededX == ((UnloadDependency) other).neededX
&& this.neededZ == ((UnloadDependency) other).neededZ
&& this.forX == ((UnloadDependency) other).forX
&& this.forZ == ((UnloadDependency) other).forZ;
}
@Override
public int hashCode()
{
int hash = 7;
hash = 79 * hash + this.neededX;
hash = 79 * hash + this.neededZ;
hash = 79 * hash + this.forX;
hash = 79 * hash + this.forZ;
return hash;
}
}
public WorldFillTask(Server theServer, Player player, String worldName, int fillDistance, int chunksPerRun, int tickFrequency, boolean forceLoad)
{
this.server = theServer; this.server = theServer;
this.notifyPlayer = player; this.notifyPlayer = player;
this.fillDistance = fillDistance; this.fillDistance = fillDistance;
@ -114,8 +73,7 @@ public class WorldFillTask implements Runnable
this.forceLoad = forceLoad; this.forceLoad = forceLoad;
this.world = server.getWorld(worldName); this.world = server.getWorld(worldName);
if (this.world == null) if (this.world == null) {
{
if (worldName.isEmpty()) if (worldName.isEmpty())
sendMessage("You must specify a world!"); sendMessage("You must specify a world!");
else else
@ -125,8 +83,7 @@ public class WorldFillTask implements Runnable
} }
this.border = (Config.Border(worldName) == null) ? null : Config.Border(worldName).copy(); this.border = (Config.Border(worldName) == null) ? null : Config.Border(worldName).copy();
if (this.border == null) if (this.border == null) {
{
sendMessage("No border found for world \"" + worldName + "\"!"); sendMessage("No border found for world \"" + worldName + "\"!");
this.stop(); this.stop();
return; return;
@ -134,8 +91,7 @@ public class WorldFillTask implements Runnable
// load up a new WorldFileData for the world in question, used to scan region files for which chunks are already fully generated and such // load up a new WorldFileData for the world in question, used to scan region files for which chunks are already fully generated and such
worldData = WorldFileData.create(world, notifyPlayer); worldData = WorldFileData.create(world, notifyPlayer);
if (worldData == null) if (worldData == null) {
{
this.stop(); this.stop();
return; return;
} }
@ -162,29 +118,24 @@ public class WorldFillTask implements Runnable
} }
// for backwards compatibility // for backwards compatibility
public WorldFillTask(Server theServer, Player player, String worldName, int fillDistance, int chunksPerRun, int tickFrequency) public WorldFillTask(Server theServer, Player player, String worldName, int fillDistance, int chunksPerRun, int tickFrequency) {
{
this(theServer, player, worldName, fillDistance, chunksPerRun, tickFrequency, false); this(theServer, player, worldName, fillDistance, chunksPerRun, tickFrequency, false);
} }
public void setTaskID(int ID) public void setTaskID(int ID) {
{
if (ID == -1) this.stop(); if (ID == -1) this.stop();
this.taskID = ID; this.taskID = ID;
} }
@Override @Override
public void run() public void run() {
{ if (continueNotice) { // notify user that task has continued automatically
if (continueNotice)
{ // notify user that task has continued automatically
continueNotice = false; continueNotice = false;
sendMessage("World map generation task automatically continuing."); sendMessage("World map generation task automatically continuing.");
sendMessage("Reminder: you can cancel at any time with \"wb fill cancel\", or pause/unpause with \"wb fill pause\"."); sendMessage("Reminder: you can cancel at any time with \"wb fill cancel\", or pause/unpause with \"wb fill pause\".");
} }
if (pausedForMemory) if (pausedForMemory) { // if available memory gets too low, we automatically pause, so handle that
{ // if available memory gets too low, we automatically pause, so handle that
if (Config.AvailableMemoryTooLow()) if (Config.AvailableMemoryTooLow())
return; return;
@ -210,18 +161,15 @@ public class WorldFillTask implements Runnable
int chunksProcessedLastTick = 0; int chunksProcessedLastTick = 0;
Map<CompletableFuture<Void>, CoordXZ> newPendingChunks = new HashMap<>(); Map<CompletableFuture<Void>, CoordXZ> newPendingChunks = new HashMap<>();
Set<CoordXZ> chunksToUnload = new HashSet<>(); Set<CoordXZ> chunksToUnload = new HashSet<>();
for (CompletableFuture<Void> cf : pendingChunks.keySet()) for (CompletableFuture<Void> cf : pendingChunks.keySet()) {
{ if (cf.isDone()) {
if (cf.isDone())
{
++chunksProcessedLastTick; ++chunksProcessedLastTick;
// If cf.get() returned the chunk reliably, pendingChunks could // If cf.get() returned the chunk reliably, pendingChunks could
// be a set and we wouldn't have to map CFs to coords ... // be a set and we wouldn't have to map CFs to coords ...
CoordXZ xz = pendingChunks.get(cf); CoordXZ xz = pendingChunks.get(cf);
worldData.chunkExistsNow(xz.x, xz.z); worldData.chunkExistsNow(xz.x, xz.z);
chunksToUnload.add(xz); chunksToUnload.add(xz);
} } else
else
newPendingChunks.put(cf, pendingChunks.get(cf)); newPendingChunks.put(cf, pendingChunks.get(cf));
} }
pendingChunks = newPendingChunks; pendingChunks = newPendingChunks;
@ -229,8 +177,7 @@ public class WorldFillTask implements Runnable
// Next, check which chunks had been loaded because a to-be-generated // Next, check which chunks had been loaded because a to-be-generated
// chunk needed them, and don't have to remain in memory any more. // chunk needed them, and don't have to remain in memory any more.
Set<UnloadDependency> newPreventUnload = new HashSet<>(); Set<UnloadDependency> newPreventUnload = new HashSet<>();
for (UnloadDependency dependency : preventUnload) for (UnloadDependency dependency : preventUnload) {
{
if (worldData.doesChunkExist(dependency.forX, dependency.forZ)) if (worldData.doesChunkExist(dependency.forX, dependency.forZ))
chunksToUnload.add(new CoordXZ(dependency.neededX, dependency.neededZ)); chunksToUnload.add(new CoordXZ(dependency.neededX, dependency.neededZ));
else else
@ -244,10 +191,8 @@ public class WorldFillTask implements Runnable
// The ChunkUnloadListener checks this anyway, but it doesn't hurt to // The ChunkUnloadListener checks this anyway, but it doesn't hurt to
// save a few µs by not even requesting the unload. // save a few µs by not even requesting the unload.
for (CoordXZ unload : chunksToUnload) for (CoordXZ unload : chunksToUnload) {
{ if (!chunkOnUnloadPreventionList(unload.x, unload.z)) {
if (!chunkOnUnloadPreventionList(unload.x, unload.z))
{
world.setChunkForceLoaded(unload.x, unload.z, false); world.setChunkForceLoaded(unload.x, unload.z, false);
world.unloadChunkRequest(unload.x, unload.z); world.unloadChunkRequest(unload.x, unload.z);
} }
@ -261,16 +206,14 @@ public class WorldFillTask implements Runnable
// long queue of fill-generations. // long queue of fill-generations.
int chunksToProcess = chunksPerRun; int chunksToProcess = chunksPerRun;
if (chunksProcessedLastTick > 0 || pendingChunks.size() > 0) if (chunksProcessedLastTick > 0 || pendingChunks.size() > 0) {
{
// Note we generally queue 3 chunks, so real numbers are 1/3 of chunksProcessedLastTick and pendingchunks.size // Note we generally queue 3 chunks, so real numbers are 1/3 of chunksProcessedLastTick and pendingchunks.size
int chunksExpectedToGetProcessed = (chunksProcessedLastTick - pendingChunks.size()) / 3 + 3; int chunksExpectedToGetProcessed = (chunksProcessedLastTick - pendingChunks.size()) / 3 + 3;
if (chunksExpectedToGetProcessed < chunksToProcess) if (chunksExpectedToGetProcessed < chunksToProcess)
chunksToProcess = chunksExpectedToGetProcessed; chunksToProcess = chunksExpectedToGetProcessed;
} }
for (int loop = 0; loop < chunksToProcess; loop++) for (int loop = 0; loop < chunksToProcess; loop++) {
{
// in case the task has been paused while we're repeating... // in case the task has been paused while we're repeating...
if (paused || pausedForMemory) if (paused || pausedForMemory)
return; return;
@ -282,33 +225,28 @@ public class WorldFillTask implements Runnable
reportProgress(); reportProgress();
// if this iteration has been running for 45ms (almost 1 tick) or more, stop to take a breather // if this iteration has been running for 45ms (almost 1 tick) or more, stop to take a breather
if (now > loopStartTime + 45) if (now > loopStartTime + 45) {
{
readyToGo = true; readyToGo = true;
return; return;
} }
// if we've made it at least partly outside the border, skip past any such chunks // if we've made it at least partly outside the border, skip past any such chunks
while (!border.insideBorder(CoordXZ.chunkToBlock(x) + 8, CoordXZ.chunkToBlock(z) + 8)) while (!border.insideBorder(CoordXZ.chunkToBlock(x) + 8, CoordXZ.chunkToBlock(z) + 8)) {
{
if (!moveToNext()) if (!moveToNext())
return; return;
} }
insideBorder = true; insideBorder = true;
if (!forceLoad) if (!forceLoad) {
{
// skip past any chunks which are confirmed as fully generated using our super-special isChunkFullyGenerated routine // skip past any chunks which are confirmed as fully generated using our super-special isChunkFullyGenerated routine
int rLoop = 0; int rLoop = 0;
while (worldData.isChunkFullyGenerated(x, z)) while (worldData.isChunkFullyGenerated(x, z)) {
{
rLoop++; rLoop++;
insideBorder = true; insideBorder = true;
if (!moveToNext()) if (!moveToNext())
return; return;
if (rLoop > 255) if (rLoop > 255) { // only skim through max 256 chunks (~8 region files) at a time here, to allow process to take a break if needed
{ // only skim through max 256 chunks (~8 region files) at a time here, to allow process to take a break if needed
readyToGo = true; readyToGo = true;
return; return;
} }
@ -338,18 +276,15 @@ public class WorldFillTask implements Runnable
} }
// step through chunks in spiral pattern from center; returns false if we're done, otherwise returns true // step through chunks in spiral pattern from center; returns false if we're done, otherwise returns true
public boolean moveToNext() public boolean moveToNext() {
{
if (paused || pausedForMemory) if (paused || pausedForMemory)
return false; return false;
reportNum++; reportNum++;
// keep track of progress in case we need to save to config for restoring progress after server restart // keep track of progress in case we need to save to config for restoring progress after server restart
if (!isNeg && current == 0 && length > 3) if (!isNeg && current == 0 && length > 3) {
{ if (!isZLeg) {
if (!isZLeg)
{
lastLegX = x; lastLegX = x;
lastLegZ = z; lastLegZ = z;
lastLegTotal = reportTotal + reportNum; lastLegTotal = reportTotal + reportNum;
@ -364,12 +299,10 @@ public class WorldFillTask implements Runnable
// make sure of the direction we're moving (X or Z? negative or positive?) // make sure of the direction we're moving (X or Z? negative or positive?)
if (current < length) if (current < length)
current++; current++;
else else { // one leg/side of the spiral down...
{ // one leg/side of the spiral down...
current = 0; current = 0;
isZLeg ^= true; isZLeg ^= true;
if (isZLeg) if (isZLeg) { // every second leg (between X and Z legs, negative or positive), length increases
{ // every second leg (between X and Z legs, negative or positive), length increases
isNeg ^= true; isNeg ^= true;
length++; length++;
} }
@ -386,10 +319,8 @@ public class WorldFillTask implements Runnable
x += (isNeg) ? -1 : 1; x += (isNeg) ? -1 : 1;
// if we've been around one full loop (4 legs)... // if we've been around one full loop (4 legs)...
if (isZLeg && isNeg && current == 0) if (isZLeg && isNeg && current == 0) { // see if we've been outside the border for the whole loop
{ // see if we've been outside the border for the whole loop if (!insideBorder) { // and finish if so
if (!insideBorder)
{ // and finish if so
finish(); finish();
return false; return false;
} // otherwise, reset the "inside border" flag } // otherwise, reset the "inside border" flag
@ -412,8 +343,7 @@ public class WorldFillTask implements Runnable
} }
// for successful completion // for successful completion
public void finish() public void finish() {
{
this.paused = true; this.paused = true;
reportProgress(); reportProgress();
world.save(); world.save();
@ -423,14 +353,12 @@ public class WorldFillTask implements Runnable
} }
// for cancelling prematurely // for cancelling prematurely
public void cancel() public void cancel() {
{
this.stop(); this.stop();
} }
// we're done, whether finished or cancelled // we're done, whether finished or cancelled
private void stop() private void stop() {
{
if (server == null) if (server == null)
return; return;
@ -442,12 +370,10 @@ public class WorldFillTask implements Runnable
// go ahead and unload any chunks we still have loaded // go ahead and unload any chunks we still have loaded
// Set preventUnload to empty first so the ChunkUnloadEvent Listener // Set preventUnload to empty first so the ChunkUnloadEvent Listener
// doesn't get in our way // doesn't get in our way
if (preventUnload != null) if (preventUnload != null) {
{
Set<UnloadDependency> tempPreventUnload = preventUnload; Set<UnloadDependency> tempPreventUnload = preventUnload;
preventUnload = null; preventUnload = null;
for (UnloadDependency entry: tempPreventUnload) for (UnloadDependency entry : tempPreventUnload) {
{
world.setChunkForceLoaded(entry.neededX, entry.neededZ, false); world.setChunkForceLoaded(entry.neededX, entry.neededZ, false);
world.unloadChunkRequest(entry.neededX, entry.neededZ); world.unloadChunkRequest(entry.neededX, entry.neededZ);
} }
@ -455,44 +381,37 @@ public class WorldFillTask implements Runnable
} }
// is this task still valid/workable? // is this task still valid/workable?
public boolean valid() public boolean valid() {
{
return this.server != null; return this.server != null;
} }
// handle pausing/unpausing the task // handle pausing/unpausing the task
public void pause() public void pause() {
{
if (this.pausedForMemory) if (this.pausedForMemory)
pause(false); pause(false);
else else
pause(!this.paused); pause(!this.paused);
} }
public void pause(boolean pause)
{ public void pause(boolean pause) {
if (this.pausedForMemory && !pause) if (this.pausedForMemory && !pause)
this.pausedForMemory = false; this.pausedForMemory = false;
else else
this.paused = pause; this.paused = pause;
if (this.paused) if (this.paused) {
{
Config.StoreFillTask(); Config.StoreFillTask();
reportProgress(); reportProgress();
} } else
else
Config.UnStoreFillTask(); Config.UnStoreFillTask();
} }
public boolean isPaused()
{ public boolean isPaused() {
return this.paused || this.pausedForMemory; return this.paused || this.pausedForMemory;
} }
public boolean chunkOnUnloadPreventionList(int x, int z) public boolean chunkOnUnloadPreventionList(int x, int z) {
{ if (preventUnload != null) {
if (preventUnload != null) for (UnloadDependency entry : preventUnload) {
{
for (UnloadDependency entry: preventUnload)
{
if (entry.neededX == x && entry.neededZ == z) if (entry.neededX == x && entry.neededZ == z)
return true; return true;
} }
@ -500,14 +419,12 @@ public class WorldFillTask implements Runnable
return false; return false;
} }
public World getWorld() public World getWorld() {
{
return world; return world;
} }
// let the user know how things are coming along // let the user know how things are coming along
private void reportProgress() private void reportProgress() {
{
lastReport = Config.Now(); lastReport = Config.Now();
double perc = getPercentageCompleted(); double perc = getPercentageCompleted();
if (perc > 100) perc = 100; if (perc > 100) perc = 100;
@ -516,8 +433,7 @@ public class WorldFillTask implements Runnable
reportNum = 0; reportNum = 0;
// go ahead and save world to disk every 30 seconds or so by default, just in case; can take a couple of seconds or more, so we don't want to run it too often // go ahead and save world to disk every 30 seconds or so by default, just in case; can take a couple of seconds or more, so we don't want to run it too often
if (Config.FillAutosaveFrequency() > 0 && lastAutosave + (Config.FillAutosaveFrequency() * 1000) < lastReport) if (Config.FillAutosaveFrequency() > 0 && lastAutosave + (Config.FillAutosaveFrequency() * 1000) < lastReport) {
{
lastAutosave = lastReport; lastAutosave = lastReport;
sendMessage("Saving the world to disk, just to be on the safe side."); sendMessage("Saving the world to disk, just to be on the safe side.");
world.save(); world.save();
@ -525,8 +441,7 @@ public class WorldFillTask implements Runnable
} }
// send a message to the server console/log and possibly to an in-game player // send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text) private void sendMessage(String text) {
{
// Due to chunk generation eating up memory and Java being too slow about GC, we need to track memory availability // Due to chunk generation eating up memory and Java being too slow about GC, we need to track memory availability
int availMem = Config.AvailableMemory(); int availMem = Config.AvailableMemory();
@ -534,8 +449,7 @@ public class WorldFillTask implements Runnable
if (notifyPlayer != null) if (notifyPlayer != null)
notifyPlayer.sendMessage("[Fill] " + text); notifyPlayer.sendMessage("[Fill] " + text);
if (availMem < 200) if (availMem < 200) { // running low on memory, auto-pause
{ // running low on memory, auto-pause
pausedForMemory = true; pausedForMemory = true;
Config.StoreFillTask(); Config.StoreFillTask();
text = "Available memory is very low, task is pausing. A cleanup will be attempted now, and the task will automatically continue if/when sufficient memory is freed up.\n Alternatively, if you restart the server, this task will automatically continue once the server is back up."; text = "Available memory is very low, task is pausing. A cleanup will be attempted now, and the task will automatically continue if/when sufficient memory is freed up.\n Alternatively, if you restart the server, this task will automatically continue once the server is back up.";
@ -549,48 +463,47 @@ public class WorldFillTask implements Runnable
} }
// stuff for saving / restoring progress // stuff for saving / restoring progress
public void continueProgress(int x, int z, int length, int totalDone) public void continueProgress(int x, int z, int length, int totalDone) {
{
this.x = x; this.x = x;
this.z = z; this.z = z;
this.length = length; this.length = length;
this.reportTotal = totalDone; this.reportTotal = totalDone;
this.continueNotice = true; this.continueNotice = true;
} }
public int refX()
{ public int refX() {
return refX; return refX;
} }
public int refZ()
{ public int refZ() {
return refZ; return refZ;
} }
public int refLength()
{ public int refLength() {
return refLength; return refLength;
} }
public int refTotal()
{ public int refTotal() {
return refTotal; return refTotal;
} }
public int refFillDistance()
{ public int refFillDistance() {
return fillDistance; return fillDistance;
} }
public int refTickFrequency()
{ public int refTickFrequency() {
return tickFrequency; return tickFrequency;
} }
public int refChunksPerRun()
{ public int refChunksPerRun() {
return chunksPerRun; return chunksPerRun;
} }
public String refWorld()
{ public String refWorld() {
return world.getName(); return world.getName();
} }
public boolean refForceLoad()
{ public boolean refForceLoad() {
return forceLoad; return forceLoad;
} }
@ -599,8 +512,7 @@ public class WorldFillTask implements Runnable
* *
* @return Percentage * @return Percentage
*/ */
public double getPercentageCompleted() public double getPercentageCompleted() {
{
return ((double) (reportTotal + reportNum) / (double) reportTarget) * 100; return ((double) (reportTotal + reportNum) / (double) reportTarget) * 100;
} }
@ -609,8 +521,7 @@ public class WorldFillTask implements Runnable
* *
* @return Number of chunks processed. * @return Number of chunks processed.
*/ */
public int getChunksCompleted() public int getChunksCompleted() {
{
return reportTotal; return reportTotal;
} }
@ -619,17 +530,14 @@ public class WorldFillTask implements Runnable
* *
* @return Number of chunks that need to be processed. * @return Number of chunks that need to be processed.
*/ */
public int getChunksTotal() public int getChunksTotal() {
{
return reportTarget; return reportTarget;
} }
private CompletableFuture<Void> getPaperLibChunk(World world, int x, int z, boolean gen) private CompletableFuture<Void> getPaperLibChunk(World world, int x, int z, boolean gen) {
{
return PaperLib.getChunkAtAsync(world, x, z, gen).thenAccept((Chunk chunk) -> return PaperLib.getChunkAtAsync(world, x, z, gen).thenAccept((Chunk chunk) ->
{ {
if (chunk != null) if (chunk != null) {
{
// toggle "force loaded" flag on for chunk to prevent it from being unloaded while we need it // toggle "force loaded" flag on for chunk to prevent it from being unloaded while we need it
world.setChunkForceLoaded(x, z, true); world.setChunkForceLoaded(x, z, true);
@ -638,4 +546,37 @@ public class WorldFillTask implements Runnable
} }
}); });
} }
private class UnloadDependency {
int neededX, neededZ;
int forX, forZ;
UnloadDependency(int neededX, int neededZ, int forX, int forZ) {
this.neededX = neededX;
this.neededZ = neededZ;
this.forX = forX;
this.forZ = forZ;
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof UnloadDependency))
return false;
return this.neededX == ((UnloadDependency) other).neededX
&& this.neededZ == ((UnloadDependency) other).neededZ
&& this.forX == ((UnloadDependency) other).forX
&& this.forZ == ((UnloadDependency) other).forZ;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + this.neededX;
hash = 79 * hash + this.neededZ;
hash = 79 * hash + this.forX;
hash = 79 * hash + this.forZ;
return hash;
}
}
} }

View File

@ -1,5 +1,12 @@
package com.wimbli.WorldBorder; 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.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@ -7,17 +14,8 @@ import java.io.RandomAccessFile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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; public class WorldTrimTask implements Runnable {
import com.wimbli.WorldBorder.Events.WorldBorderTrimStartEvent;
public class WorldTrimTask implements Runnable
{
// general task-related reference data // general task-related reference data
private transient Server server = null; private transient Server server = null;
private transient World world = null; private transient World world = null;
@ -46,15 +44,13 @@ public class WorldTrimTask implements Runnable
private transient int reportTrimmedChunks = 0; private transient int reportTrimmedChunks = 0;
public WorldTrimTask(Server theServer, Player player, String worldName, int trimDistance, int chunksPerRun) public WorldTrimTask(Server theServer, Player player, String worldName, int trimDistance, int chunksPerRun) {
{
this.server = theServer; this.server = theServer;
this.notifyPlayer = player; this.notifyPlayer = player;
this.chunksPerRun = chunksPerRun; this.chunksPerRun = chunksPerRun;
this.world = server.getWorld(worldName); this.world = server.getWorld(worldName);
if (this.world == null) if (this.world == null) {
{
if (worldName.isEmpty()) if (worldName.isEmpty())
sendMessage("You must specify a world!"); sendMessage("You must specify a world!");
else else
@ -64,8 +60,7 @@ public class WorldTrimTask implements Runnable
} }
this.border = (Config.Border(worldName) == null) ? null : Config.Border(worldName).copy(); this.border = (Config.Border(worldName) == null) ? null : Config.Border(worldName).copy();
if (this.border == null) if (this.border == null) {
{
sendMessage("No border found for world \"" + worldName + "\"!"); sendMessage("No border found for world \"" + worldName + "\"!");
this.stop(); this.stop();
return; return;
@ -75,8 +70,7 @@ public class WorldTrimTask implements Runnable
this.border.setRadiusZ(border.getRadiusZ() + trimDistance); this.border.setRadiusZ(border.getRadiusZ() + trimDistance);
worldData = WorldFileData.create(world, notifyPlayer); worldData = WorldFileData.create(world, notifyPlayer);
if (worldData == null) if (worldData == null) {
{
this.stop(); this.stop();
return; return;
} }
@ -92,14 +86,12 @@ public class WorldTrimTask implements Runnable
Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimStartEvent(this)); Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimStartEvent(this));
} }
public void setTaskID(int ID) public void setTaskID(int ID) {
{
this.taskID = ID; this.taskID = ID;
} }
public void run() public void run() {
{
if (server == null || !readyToGo || paused) if (server == null || !readyToGo || paused)
return; return;
@ -109,8 +101,7 @@ public class WorldTrimTask implements Runnable
long loopStartTime = Config.Now(); long loopStartTime = Config.Now();
counter = 0; counter = 0;
while (counter <= chunksPerRun) while (counter <= chunksPerRun) {
{
// in case the task has been paused while we're repeating... // in case the task has been paused while we're repeating...
if (paused) if (paused)
return; return;
@ -122,48 +113,38 @@ public class WorldTrimTask implements Runnable
reportProgress(); 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 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) if (now > loopStartTime + 45) {
{
readyToGo = true; readyToGo = true;
return; return;
} }
if (regionChunks.isEmpty()) if (regionChunks.isEmpty())
addCornerChunks(); addCornerChunks();
else if (currentChunk == 4) else if (currentChunk == 4) { // determine if region is completely _inside_ border based on corner chunks
{ // 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
if (trimChunks.isEmpty())
{ // it is, so skip it and move on to next file
counter += 4; counter += 4;
nextFile(); nextFile();
continue; continue;
} }
addEdgeChunks(); addEdgeChunks();
addInnerChunks(); 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
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; counter += 16;
trimChunks = regionChunks; trimChunks = regionChunks;
unloadChunks(); unloadChunks();
reportTrimmedRegions++; reportTrimmedRegions++;
File regionFile = worldData.regionFile(currentRegion); File regionFile = worldData.regionFile(currentRegion);
if (!regionFile.delete()) if (!regionFile.delete()) {
{
sendMessage("Error! Region file which is outside the border could not be deleted: " + regionFile.getName()); sendMessage("Error! Region file which is outside the border could not be deleted: " + regionFile.getName());
wipeChunks(); wipeChunks();
} } else {
else
{
// if DynMap is installed, re-render the trimmed region ... disabled since it's not currently working, oh well // 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)); // DynMapFeatures.renderRegion(world.getName(), new CoordXZ(regionX, regionZ));
} }
nextFile(); nextFile();
continue; continue;
} } else if (currentChunk == 1024) { // last chunk of the region has been checked, time to wipe out whichever chunks are outside the border
else if (currentChunk == 1024)
{ // last chunk of the region has been checked, time to wipe out whichever chunks are outside the border
counter += 32; counter += 32;
unloadChunks(); unloadChunks();
wipeChunks(); wipeChunks();
@ -187,8 +168,7 @@ public class WorldTrimTask implements Runnable
} }
// Advance to the next region file. Returns true if successful, false if the next file isn't accessible for any reason // Advance to the next region file. Returns true if successful, false if the next file isn't accessible for any reason
private boolean nextFile() private boolean nextFile() {
{
reportTotal = currentRegion * 3072; reportTotal = currentRegion * 3072;
currentRegion++; currentRegion++;
regionX = regionZ = currentChunk = 0; regionX = regionZ = currentChunk = 0;
@ -196,8 +176,7 @@ public class WorldTrimTask implements Runnable
trimChunks = new ArrayList<CoordXZ>(1024); trimChunks = new ArrayList<CoordXZ>(1024);
// have we already handled all region files? // have we already handled all region files?
if (currentRegion >= worldData.regionFileCount()) if (currentRegion >= worldData.regionFileCount()) { // hey, we're done
{ // hey, we're done
paused = true; paused = true;
readyToGo = false; readyToGo = false;
finish(); finish();
@ -217,8 +196,7 @@ public class WorldTrimTask implements Runnable
} }
// add just the 4 corner chunks of the region; can determine if entire region is _inside_ the border // add just the 4 corner chunks of the region; can determine if entire region is _inside_ the border
private void addCornerChunks() private void addCornerChunks() {
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ))); 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) + 31, CoordXZ.regionToChunk(regionZ)));
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ) + 31)); regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX), CoordXZ.regionToChunk(regionZ) + 31));
@ -226,39 +204,31 @@ public class WorldTrimTask implements Runnable
} }
// add all chunks along the 4 edges of the region (minus the corners); can determine if entire region is _outside_ the border // 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() private void addEdgeChunks() {
{
int chunkX = 0, chunkZ; int chunkX = 0, chunkZ;
for (chunkZ = 1; chunkZ < 31; chunkZ++) for (chunkZ = 1; chunkZ < 31; chunkZ++) {
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ)); regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
} }
chunkX = 31; chunkX = 31;
for (chunkZ = 1; chunkZ < 31; chunkZ++) for (chunkZ = 1; chunkZ < 31; chunkZ++) {
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ)); regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
} }
chunkZ = 0; chunkZ = 0;
for (chunkX = 1; chunkX < 31; chunkX++) for (chunkX = 1; chunkX < 31; chunkX++) {
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ)); regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
} }
chunkZ = 31; chunkZ = 31;
for (chunkX = 1; chunkX < 31; chunkX++) for (chunkX = 1; chunkX < 31; chunkX++) {
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ)); regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
} }
counter += 4; counter += 4;
} }
// add the remaining interior chunks (after corners and edges) // add the remaining interior chunks (after corners and edges)
private void addInnerChunks() private void addInnerChunks() {
{ for (int chunkX = 1; chunkX < 31; chunkX++) {
for (int chunkX = 1; chunkX < 31; chunkX++) for (int chunkZ = 1; chunkZ < 31; chunkZ++) {
{
for (int chunkZ = 1; chunkZ < 31; chunkZ++)
{
regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ)); regionChunks.add(new CoordXZ(CoordXZ.regionToChunk(regionX) + chunkX, CoordXZ.regionToChunk(regionZ) + chunkZ));
} }
} }
@ -266,10 +236,8 @@ public class WorldTrimTask implements Runnable
} }
// make sure chunks set to be trimmed are not currently loaded by the server // make sure chunks set to be trimmed are not currently loaded by the server
private void unloadChunks() private void unloadChunks() {
{ for (CoordXZ unload : trimChunks) {
for (CoordXZ unload : trimChunks)
{
if (world.isChunkLoaded(unload.x, unload.z)) if (world.isChunkLoaded(unload.x, unload.z))
world.unloadChunk(unload.x, unload.z, false); world.unloadChunk(unload.x, unload.z, false);
} }
@ -277,16 +245,13 @@ public class WorldTrimTask implements Runnable
} }
// edit region file to wipe all chunk pointers for chunks outside the border // edit region file to wipe all chunk pointers for chunks outside the border
private void wipeChunks() private void wipeChunks() {
{
File regionFile = worldData.regionFile(currentRegion); File regionFile = worldData.regionFile(currentRegion);
if (!regionFile.canWrite()) if (!regionFile.canWrite()) {
{
if (!regionFile.setWritable(true)) if (!regionFile.setWritable(true))
throw new RuntimeException(); throw new RuntimeException();
if (!regionFile.canWrite()) if (!regionFile.canWrite()) {
{
sendMessage("Error! region file is locked and can't be trimmed: " + regionFile.getName()); sendMessage("Error! region file is locked and can't be trimmed: " + regionFile.getName());
return; return;
} }
@ -298,11 +263,9 @@ public class WorldTrimTask implements Runnable
long wipePos = 0; long wipePos = 0;
int chunkCount = 0; int chunkCount = 0;
try try {
{
RandomAccessFile unChunk = new RandomAccessFile(regionFile, "rwd"); RandomAccessFile unChunk = new RandomAccessFile(regionFile, "rwd");
for (CoordXZ wipe : trimChunks) for (CoordXZ wipe : trimChunks) {
{
// if the chunk pointer is empty (chunk doesn't technically exist), no need to wipe the already empty pointer // 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)) if (!worldData.doesChunkExist(wipe.x, wipe.z))
continue; continue;
@ -320,26 +283,20 @@ public class WorldTrimTask implements Runnable
// DynMapFeatures.renderChunks(world.getName(), trimChunks); // DynMapFeatures.renderChunks(world.getName(), trimChunks);
reportTrimmedChunks += chunkCount; reportTrimmedChunks += chunkCount;
} } catch (FileNotFoundException ex) {
catch (FileNotFoundException ex)
{
sendMessage("Error! Could not open region file to wipe individual chunks: " + regionFile.getName()); sendMessage("Error! Could not open region file to wipe individual chunks: " + regionFile.getName());
} } catch (IOException ex) {
catch (IOException ex)
{
sendMessage("Error! Could not modify region file to wipe individual chunks: " + regionFile.getName()); sendMessage("Error! Could not modify region file to wipe individual chunks: " + regionFile.getName());
} }
counter += trimChunks.size(); counter += trimChunks.size();
} }
private boolean isChunkInsideBorder(CoordXZ chunk) private boolean isChunkInsideBorder(CoordXZ chunk) {
{
return border.insideBorder(CoordXZ.chunkToBlock(chunk.x) + 8, CoordXZ.chunkToBlock(chunk.z) + 8); return border.insideBorder(CoordXZ.chunkToBlock(chunk.x) + 8, CoordXZ.chunkToBlock(chunk.z) + 8);
} }
// for successful completion // for successful completion
public void finish() public void finish() {
{
reportTotal = reportTarget; reportTotal = reportTarget;
reportProgress(); reportProgress();
Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimFinishedEvent(world, reportTotal)); Bukkit.getServer().getPluginManager().callEvent(new WorldBorderTrimFinishedEvent(world, reportTotal));
@ -348,14 +305,12 @@ public class WorldTrimTask implements Runnable
} }
// for cancelling prematurely // for cancelling prematurely
public void cancel() public void cancel() {
{
this.stop(); this.stop();
} }
// we're done, whether finished or cancelled // we're done, whether finished or cancelled
private void stop() private void stop() {
{
if (server == null) if (server == null)
return; return;
@ -370,38 +325,34 @@ public class WorldTrimTask implements Runnable
} }
// is this task still valid/workable? // is this task still valid/workable?
public boolean valid() public boolean valid() {
{
return this.server != null; return this.server != null;
} }
// handle pausing/unpausing the task // handle pausing/unpausing the task
public void pause() public void pause() {
{
pause(!this.paused); pause(!this.paused);
} }
public void pause(boolean pause)
{ public void pause(boolean pause) {
this.paused = pause; this.paused = pause;
if (pause) if (pause)
reportProgress(); reportProgress();
} }
public boolean isPaused()
{ public boolean isPaused() {
return this.paused; return this.paused;
} }
// let the user know how things are coming along // let the user know how things are coming along
private void reportProgress() private void reportProgress() {
{
lastReport = Config.Now(); lastReport = Config.Now();
double perc = getPercentageCompleted(); double perc = getPercentageCompleted();
sendMessage(reportTrimmedRegions + " entire region(s) and " + reportTrimmedChunks + " individual chunk(s) trimmed so far (" + Config.coord.format(perc) + "% done" + ")"); sendMessage(reportTrimmedRegions + " entire region(s) and " + reportTrimmedChunks + " individual chunk(s) trimmed so far (" + Config.coord.format(perc) + "% done" + ")");
} }
// send a message to the server console/log and possibly to an in-game player // send a message to the server console/log and possibly to an in-game player
private void sendMessage(String text) private void sendMessage(String text) {
{
Config.log("[Trim] " + text); Config.log("[Trim] " + text);
if (notifyPlayer != null) if (notifyPlayer != null)
notifyPlayer.sendMessage("[Trim] " + text); notifyPlayer.sendMessage("[Trim] " + text);

View File

@ -1,20 +1,18 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdBypass extends WBCmd {
import com.wimbli.WorldBorder.UUID.UUIDFetcher; public CmdBypass() {
public class CmdBypass extends WBCmd
{
public CmdBypass()
{
name = permission = "bypass"; name = permission = "bypass";
minParams = 0; minParams = 0;
maxParams = 2; maxParams = 2;
@ -27,8 +25,7 @@ public class CmdBypass extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
if (!(sender instanceof Player)) if (!(sender instanceof Player))
return; return;
@ -37,45 +34,33 @@ public class CmdBypass extends WBCmd
} }
@Override @Override
public void execute(final CommandSender sender, final Player player, final List<String> params, String worldName) public void execute(final CommandSender sender, final Player player, final List<String> params, String worldName) {
{ if (player == null && params.isEmpty()) {
if (player == null && params.isEmpty())
{
sendErrorAndHelp(sender, "When running this command from console, you must specify a player."); sendErrorAndHelp(sender, "When running this command from console, you must specify a player.");
return; return;
} }
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() {
{
@Override @Override
public void run() public void run() {
{
final String sPlayer = (params.isEmpty()) ? player.getName() : params.get(0); final String sPlayer = (params.isEmpty()) ? player.getName() : params.get(0);
UUID uPlayer = (params.isEmpty()) ? player.getUniqueId() : null; UUID uPlayer = (params.isEmpty()) ? player.getUniqueId() : null;
if (uPlayer == null) if (uPlayer == null) {
{
Player p = Bukkit.getPlayer(sPlayer); Player p = Bukkit.getPlayer(sPlayer);
if (p != null) if (p != null) {
{
uPlayer = p.getUniqueId(); uPlayer = p.getUniqueId();
} } else {
else
{
// only do UUID lookup using Mojang server if specified player isn't online // only do UUID lookup using Mojang server if specified player isn't online
try try {
{
uPlayer = UUIDFetcher.getUUID(sPlayer); uPlayer = UUIDFetcher.getUUID(sPlayer);
} } catch (Exception ex) {
catch(Exception ex)
{
sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified. " + ex.getLocalizedMessage()); sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified. " + ex.getLocalizedMessage());
return; return;
} }
} }
} }
if (uPlayer == null) if (uPlayer == null) {
{
sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified; null value returned."); sendErrorAndHelp(sender, "Failed to look up UUID for the player name you specified; null value returned.");
return; return;
} }

View File

@ -1,22 +1,20 @@
package com.wimbli.WorldBorder.cmd; 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdBypasslist extends WBCmd {
import com.wimbli.WorldBorder.UUID.UUIDFetcher; public CmdBypasslist() {
public class CmdBypasslist extends WBCmd
{
public CmdBypasslist()
{
name = permission = "bypasslist"; name = permission = "bypasslist";
minParams = maxParams = 0; minParams = maxParams = 0;
@ -26,29 +24,22 @@ public class CmdBypasslist extends WBCmd
} }
@Override @Override
public void execute(final CommandSender sender, Player player, List<String> params, String worldName) public void execute(final CommandSender sender, Player player, List<String> params, String worldName) {
{
final ArrayList<UUID> uuids = Config.getPlayerBypassList(); final ArrayList<UUID> uuids = Config.getPlayerBypassList();
if (uuids == null || uuids.isEmpty()) if (uuids == null || uuids.isEmpty()) {
{
sender.sendMessage("Players with border bypass enabled: <none>"); sender.sendMessage("Players with border bypass enabled: <none>");
return; return;
} }
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(WorldBorder.plugin, new Runnable() {
{
@Override @Override
public void run() public void run() {
{ try {
try
{
Map<UUID, String> names = UUIDFetcher.getNameList(uuids); Map<UUID, String> names = UUIDFetcher.getNameList(uuids);
String nameString = names.values().toString(); String nameString = names.values().toString();
sender.sendMessage("Players with border bypass enabled: " + nameString.substring(1, nameString.length() - 1)); sender.sendMessage("Players with border bypass enabled: " + nameString.substring(1, nameString.length() - 1));
} } catch (Exception ex) {
catch(Exception ex)
{
sendErrorAndHelp(sender, "Failed to look up names for the UUIDs in the border bypass list. " + ex.getLocalizedMessage()); sendErrorAndHelp(sender, "Failed to look up names for the UUIDs in the border bypass list. " + ex.getLocalizedMessage());
return; return;
} }

View File

@ -1,17 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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 java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdClear extends WBCmd {
public CmdClear() {
public class CmdClear extends WBCmd
{
public CmdClear()
{
name = permission = "clear"; name = permission = "clear";
hasWorldNameInput = true; hasWorldNameInput = true;
consoleRequiresWorldName = false; consoleRequiresWorldName = false;
@ -25,13 +23,10 @@ public class CmdClear extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
// handle "clear all" command separately // handle "clear all" command separately
if (params.size() == 1 && params.get(0).equalsIgnoreCase("all")) if (params.size() == 1 && params.get(0).equalsIgnoreCase("all")) {
{ if (worldName != null) {
if (worldName != null)
{
sendErrorAndHelp(sender, "You should not specify a world with \"clear all\"."); sendErrorAndHelp(sender, "You should not specify a world with \"clear all\".");
return; return;
} }
@ -43,10 +38,8 @@ public class CmdClear extends WBCmd
return; return;
} }
if (worldName == null) if (worldName == null) {
{ if (player == null) {
if (player == null)
{
sendErrorAndHelp(sender, "You must specify a world name from console if not using \"clear all\"."); sendErrorAndHelp(sender, "You must specify a world name from console if not using \"clear all\".");
return; return;
} }
@ -54,8 +47,7 @@ public class CmdClear extends WBCmd
} }
BorderData border = Config.Border(worldName); BorderData border = Config.Border(worldName);
if (border == null) if (border == null) {
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set."); sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return; return;
} }

View File

@ -1,36 +1,30 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdCommands extends WBCmd {
public class CmdCommands extends WBCmd
{
private static int pageSize = 8; // examples to list per page; 10 lines available, 1 for header, 1 for footer private static int pageSize = 8; // examples to list per page; 10 lines available, 1 for header, 1 for footer
public CmdCommands() public CmdCommands() {
{
name = "commands"; name = "commands";
permission = "help"; permission = "help";
hasWorldNameInput = false; hasWorldNameInput = false;
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
// determine which page we're viewing // determine which page we're viewing
int page = (player == null) ? 0 : 1; int page = (player == null) ? 0 : 1;
if (!params.isEmpty()) if (!params.isEmpty()) {
{ try {
try
{
page = Integer.parseInt(params.get(0)); page = Integer.parseInt(params.get(0));
} catch (NumberFormatException ignored) {
} }
catch(NumberFormatException ignored) {}
} }
// see whether we're showing examples to player or to console, and determine number of pages available // see whether we're showing examples to player or to console, and determine number of pages available
@ -45,13 +39,11 @@ public class CmdCommands extends WBCmd
sender.sendMessage(C_HEAD + WorldBorder.plugin.getDescription().getFullName() + " - key: " + sender.sendMessage(C_HEAD + WorldBorder.plugin.getDescription().getFullName() + " - key: " +
commandEmphasized("command") + C_REQ + "<required> " + C_OPT + "[optional]"); commandEmphasized("command") + C_REQ + "<required> " + C_OPT + "[optional]");
if (page > 0) if (page > 0) {
{
// send examples for this page // send examples for this page
int first = ((page - 1) * pageSize); int first = ((page - 1) * pageSize);
int count = Math.min(pageSize, examples.size() - first); int count = Math.min(pageSize, examples.size() - first);
for(int i = first; i < first + count; i++) for (int i = first; i < first + count; i++) {
{
sender.sendMessage(examples.get(i)); sender.sendMessage(examples.get(i));
} }
@ -61,12 +53,9 @@ public class CmdCommands extends WBCmd
sender.sendMessage(footer + Integer.toString(page + 1) + C_DESC + " - view next page of commands."); sender.sendMessage(footer + Integer.toString(page + 1) + C_DESC + " - view next page of commands.");
else if (page > 1) else if (page > 1)
sender.sendMessage(footer + C_DESC + "- view first page of commands."); sender.sendMessage(footer + C_DESC + "- view first page of commands.");
} } else {
else
{
// if page "0" is specified, send all examples; done by default for console but can be specified by player // if page "0" is specified, send all examples; done by default for console but can be specified by player
for (String example : examples) for (String example : examples) {
{
sender.sendMessage(example); sender.sendMessage(example);
} }
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdDebug extends WBCmd {
public CmdDebug() {
public class CmdDebug extends WBCmd
{
public CmdDebug()
{
name = permission = "debug"; name = permission = "debug";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -21,18 +18,15 @@ public class CmdDebug extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "Debug mode is " + enabledColored(Config.Debug()) + C_HEAD + "."); sender.sendMessage(C_HEAD + "Debug mode is " + enabledColored(Config.Debug()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setDebug(strAsBool(params.get(0))); Config.setDebug(strAsBool(params.get(0)));
if (player != null) if (player != null) {
{
Config.log((Config.Debug() ? "Enabled" : "Disabled") + " debug output at the command of player \"" + player.getName() + "\"."); Config.log((Config.Debug() ? "Enabled" : "Disabled") + " debug output at the command of player \"" + player.getName() + "\".");
cmdStatus(sender); cmdStatus(sender);
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdDelay extends WBCmd {
public CmdDelay() {
public class CmdDelay extends WBCmd
{
public CmdDelay()
{
name = permission = "delay"; name = permission = "delay";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -21,24 +18,19 @@ public class CmdDelay extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
int delay = Config.TimerTicks(); int delay = Config.TimerTicks();
sender.sendMessage(C_HEAD + "Timer delay is set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms."); sender.sendMessage(C_HEAD + "Timer delay is set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms.");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
int delay = 0; int delay = 0;
try try {
{
delay = Integer.parseInt(params.get(0)); delay = Integer.parseInt(params.get(0));
if (delay < 1) if (delay < 1)
throw new NumberFormatException(); throw new NumberFormatException();
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The timer delay must be an integer of 1 or higher."); sendErrorAndHelp(sender, "The timer delay must be an integer of 1 or higher.");
return; return;
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdDenypearl extends WBCmd {
public CmdDenypearl() {
public class CmdDenypearl extends WBCmd
{
public CmdDenypearl()
{
name = permission = "denypearl"; name = permission = "denypearl";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -22,19 +19,16 @@ public class CmdDenypearl extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "Direct cancellation of ender pearls thrown past the border is " + sender.sendMessage(C_HEAD + "Direct cancellation of ender pearls thrown past the border is " +
enabledColored(Config.getDenyEnderpearl()) + C_HEAD + "."); enabledColored(Config.getDenyEnderpearl()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setDenyEnderpearl(strAsBool(params.get(0))); Config.setDenyEnderpearl(strAsBool(params.get(0)));
if (player != null) 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() + "\"."); Config.log((Config.getDenyEnderpearl() ? "Enabled" : "Disabled") + " direct cancellation of ender pearls thrown past the border at the command of player \"" + player.getName() + "\".");
cmdStatus(sender); cmdStatus(sender);
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdDynmap extends WBCmd {
public CmdDynmap() {
public class CmdDynmap extends WBCmd
{
public CmdDynmap()
{
name = permission = "dynmap"; name = permission = "dynmap";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -21,18 +18,15 @@ public class CmdDynmap extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "DynMap border display is " + enabledColored(Config.DynmapBorderEnabled()) + C_HEAD + "."); sender.sendMessage(C_HEAD + "DynMap border display is " + enabledColored(Config.DynmapBorderEnabled()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setDynmapBorderEnabled(strAsBool(params.get(0))); Config.setDynmapBorderEnabled(strAsBool(params.get(0)));
if (player != null) if (player != null) {
{
cmdStatus(sender); cmdStatus(sender);
Config.log((Config.DynmapBorderEnabled() ? "Enabled" : "Disabled") + " DynMap border display at the command of player \"" + player.getName() + "\"."); Config.log((Config.DynmapBorderEnabled() ? "Enabled" : "Disabled") + " DynMap border display at the command of player \"" + player.getName() + "\".");
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdDynmapmsg extends WBCmd {
public CmdDynmapmsg() {
public class CmdDynmapmsg extends WBCmd
{
public CmdDynmapmsg()
{
name = permission = "dynmapmsg"; name = permission = "dynmapmsg";
minParams = 1; minParams = 1;
@ -22,18 +19,15 @@ public class CmdDynmapmsg extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "DynMap border label is set to: " + C_ERR + Config.DynmapMessage()); sender.sendMessage(C_HEAD + "DynMap border label is set to: " + C_ERR + Config.DynmapMessage());
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
boolean first = true; boolean first = true;
for (String param : params) for (String param : params) {
{
if (!first) if (!first)
message.append(" "); message.append(" ");
message.append(param); message.append(param);

View File

@ -1,18 +1,28 @@
package com.wimbli.WorldBorder.cmd; 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 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() {
public class CmdFill extends WBCmd
{
public CmdFill()
{
name = permission = "fill"; name = permission = "fill";
hasWorldNameInput = true; hasWorldNameInput = true;
consoleRequiresWorldName = false; consoleRequiresWorldName = false;
@ -27,25 +37,20 @@ public class CmdFill extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
boolean confirm = false; boolean confirm = false;
// check for "cancel", "pause", or "confirm" // check for "cancel", "pause", or "confirm"
if (params.size() >= 1) if (params.size() >= 1) {
{
String check = params.get(0).toLowerCase(); String check = params.get(0).toLowerCase();
if (check.equals("cancel") || check.equals("stop")) if (check.equals("cancel") || check.equals("stop")) {
{
if (!makeSureFillIsRunning(sender)) if (!makeSureFillIsRunning(sender))
return; return;
sender.sendMessage(C_HEAD + "Cancelling the world map generation task."); sender.sendMessage(C_HEAD + "Cancelling the world map generation task.");
fillDefaults(); fillDefaults();
Config.StopFillTask(); Config.StopFillTask();
return; return;
} } else if (check.equals("pause")) {
else if (check.equals("pause"))
{
if (!makeSureFillIsRunning(sender)) if (!makeSureFillIsRunning(sender))
return; return;
Config.fillTask.pause(); Config.fillTask.pause();
@ -57,12 +62,10 @@ public class CmdFill extends WBCmd
} }
// if not just confirming, make sure a world name is available // if not just confirming, make sure a world name is available
if (worldName == null && !confirm) if (worldName == null && !confirm) {
{
if (player != null) if (player != null)
worldName = player.getWorld().getName(); worldName = player.getWorld().getName();
else else {
{
sendErrorAndHelp(sender, "You must specify a world!"); sendErrorAndHelp(sender, "You must specify a world!");
return; return;
} }
@ -72,29 +75,24 @@ public class CmdFill extends WBCmd
String cmd = cmd(sender) + nameEmphasized() + C_CMD; String cmd = cmd(sender) + nameEmphasized() + C_CMD;
// make sure Fill isn't already running // make sure Fill isn't already running
if (Config.fillTask != null && Config.fillTask.valid()) if (Config.fillTask != null && Config.fillTask.valid()) {
{
sender.sendMessage(C_ERR + "The world map generation task is already running."); 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 + "."); sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
return; return;
} }
// set frequency and/or padding if those were specified // set frequency and/or padding if those were specified
try try {
{
if (params.size() >= 1 && !confirm) if (params.size() >= 1 && !confirm)
fillFrequency = Math.abs(Integer.parseInt(params.get(0))); fillFrequency = Math.abs(Integer.parseInt(params.get(0)));
if (params.size() >= 2 && !confirm) if (params.size() >= 2 && !confirm)
fillPadding = Math.abs(Integer.parseInt(params.get(1))); fillPadding = Math.abs(Integer.parseInt(params.get(1)));
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The frequency and padding values must be integers."); sendErrorAndHelp(sender, "The frequency and padding values must be integers.");
fillDefaults(); fillDefaults();
return; return;
} }
if (fillFrequency <= 0) if (fillFrequency <= 0) {
{
sendErrorAndHelp(sender, "The frequency value must be greater than zero."); sendErrorAndHelp(sender, "The frequency value must be greater than zero.");
fillDefaults(); fillDefaults();
return; return;
@ -108,10 +106,8 @@ public class CmdFill extends WBCmd
if (worldName != null) if (worldName != null)
fillWorld = worldName; fillWorld = worldName;
if (confirm) if (confirm) { // command confirmed, go ahead with it
{ // command confirmed, go ahead with it if (fillWorld.isEmpty()) {
if (fillWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first use this command successfully without confirming."); sendErrorAndHelp(sender, "You must first use this command successfully without confirming.");
return; return;
} }
@ -125,23 +121,19 @@ public class CmdFill extends WBCmd
else else
ticks = 20 / fillFrequency; ticks = 20 / fillFrequency;
/* */ Config.log("world: " + fillWorld + " padding: " + fillPadding + " repeats: " + repeats + " ticks: " + ticks); /* */
Config.log("world: " + fillWorld + " padding: " + fillPadding + " repeats: " + repeats + " ticks: " + ticks);
Config.fillTask = new WorldFillTask(Bukkit.getServer(), player, fillWorld, fillPadding, repeats, ticks, fillForceLoad); Config.fillTask = new WorldFillTask(Bukkit.getServer(), player, fillWorld, fillPadding, repeats, ticks, fillForceLoad);
if (Config.fillTask.valid()) if (Config.fillTask.valid()) {
{
int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.fillTask, ticks, ticks); int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.fillTask, ticks, ticks);
Config.fillTask.setTaskID(task); Config.fillTask.setTaskID(task);
sender.sendMessage("WorldBorder map generation task for world \"" + fillWorld + "\" started."); sender.sendMessage("WorldBorder map generation task for world \"" + fillWorld + "\" started.");
} } else
else
sender.sendMessage(C_ERR + "The world map generation task failed to start."); sender.sendMessage(C_ERR + "The world map generation task failed to start.");
fillDefaults(); fillDefaults();
} } else {
else if (fillWorld.isEmpty()) {
{
if (fillWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first specify a valid world."); sendErrorAndHelp(sender, "You must first specify a valid world.");
return; return;
} }
@ -153,28 +145,14 @@ public class CmdFill extends WBCmd
} }
} }
private void fillDefaults() {
/* 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 = ""; fillWorld = "";
fillFrequency = 20; fillFrequency = 20;
fillPadding = defaultPadding; fillPadding = defaultPadding;
fillForceLoad = false; fillForceLoad = false;
} }
private boolean makeSureFillIsRunning(CommandSender sender) private boolean makeSureFillIsRunning(CommandSender sender) {
{
if (Config.fillTask != null && Config.fillTask.valid()) if (Config.fillTask != null && Config.fillTask.valid())
return true; return true;
sendErrorAndHelp(sender, "The world map generation task is not currently running."); sendErrorAndHelp(sender, "The world map generation task is not currently running.");

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdFillautosave extends WBCmd {
public CmdFillautosave() {
public class CmdFillautosave extends WBCmd
{
public CmdFillautosave()
{
name = permission = "fillautosave"; name = permission = "fillautosave";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -20,17 +17,13 @@ public class CmdFillautosave extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
int seconds = Config.FillAutosaveFrequency(); int seconds = Config.FillAutosaveFrequency();
if (seconds == 0) if (seconds == 0) {
{
sender.sendMessage(C_HEAD + "World autosave frequency during Fill process is set to 0, disabling it."); 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 " + 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."); "the world generation process from Bukkit or any world generation plugin you use.");
} } else {
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 + "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 " + 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."); "this often to prevent loss of progress due to bugs or crashes in the world generation process.");
@ -38,17 +31,13 @@ public class CmdFillautosave extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
int seconds = 0; int seconds = 0;
try try {
{
seconds = Integer.parseInt(params.get(0)); seconds = Integer.parseInt(params.get(0));
if (seconds < 0) if (seconds < 0)
throw new NumberFormatException(); throw new NumberFormatException();
} } catch (NumberFormatException ex) {
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."); 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; return;
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdGetmsg extends WBCmd {
public CmdGetmsg() {
public class CmdGetmsg extends WBCmd
{
public CmdGetmsg()
{
name = permission = "getmsg"; name = permission = "getmsg";
minParams = maxParams = 0; minParams = maxParams = 0;
@ -20,8 +17,7 @@ public class CmdGetmsg extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
sender.sendMessage("Border message is currently set to:"); sender.sendMessage("Border message is currently set to:");
sender.sendMessage(Config.MessageRaw()); sender.sendMessage(Config.MessageRaw());
sender.sendMessage("Formatted border message:"); sender.sendMessage("Formatted border message:");

View File

@ -1,18 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import java.util.Set; import java.util.Set;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdHelp extends WBCmd {
public CmdHelp() {
public class CmdHelp extends WBCmd
{
public CmdHelp()
{
name = permission = "help"; name = permission = "help";
minParams = 0; minParams = 0;
maxParams = 10; maxParams = 10;
@ -22,8 +19,7 @@ public class CmdHelp extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
String commands = WorldBorder.wbCommand.getCommandNames().toString().replace(", ", C_DESC + ", " + C_CMD); 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(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("Example, for info on \"set\" command: " + cmd(sender) + nameEmphasized() + C_CMD + "set");
@ -31,19 +27,15 @@ public class CmdHelp extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{ if (params.isEmpty()) {
if (params.isEmpty())
{
sendCmdHelp(sender); sendCmdHelp(sender);
return; return;
} }
Set<String> commands = WorldBorder.wbCommand.getCommandNames(); Set<String> commands = WorldBorder.wbCommand.getCommandNames();
for (String param : params) for (String param : params) {
{ if (commands.contains(param.toLowerCase())) {
if (commands.contains(param.toLowerCase()))
{
WorldBorder.wbCommand.subCommands.get(param.toLowerCase()).sendCmdHelp(sender); WorldBorder.wbCommand.subCommands.get(param.toLowerCase()).sendCmdHelp(sender);
return; return;
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdKnockback extends WBCmd {
public CmdKnockback() {
public class CmdKnockback extends WBCmd
{
public CmdKnockback()
{
name = permission = "knockback"; name = permission = "knockback";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -20,8 +17,7 @@ public class CmdKnockback extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
double kb = Config.KnockBack(); double kb = Config.KnockBack();
if (kb < 1) if (kb < 1)
sender.sendMessage(C_HEAD + "Knockback is set to 0, disabling border enforcement."); sender.sendMessage(C_HEAD + "Knockback is set to 0, disabling border enforcement.");
@ -30,17 +26,13 @@ public class CmdKnockback extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
double numBlocks = 0.0; double numBlocks = 0.0;
try try {
{
numBlocks = Double.parseDouble(params.get(0)); numBlocks = Double.parseDouble(params.get(0));
if (numBlocks < 0.0 || (numBlocks > 0.0 && numBlocks < 1.0)) if (numBlocks < 0.0 || (numBlocks > 0.0 && numBlocks < 1.0))
throw new NumberFormatException(); throw new NumberFormatException();
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The knockback must be a decimal value of at least 1.0, or it can be 0."); sendErrorAndHelp(sender, "The knockback must be a decimal value of at least 1.0, or it can be 0.");
return; return;
} }

View File

@ -1,18 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import java.util.Set; import java.util.Set;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdList extends WBCmd {
public CmdList() {
public class CmdList extends WBCmd
{
public CmdList()
{
name = permission = "list"; name = permission = "list";
minParams = maxParams = 0; minParams = maxParams = 0;
@ -22,20 +19,17 @@ public class CmdList extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
sender.sendMessage("Default border shape for all worlds is \"" + Config.ShapeName() + "\"."); sender.sendMessage("Default border shape for all worlds is \"" + Config.ShapeName() + "\".");
Set<String> list = Config.BorderDescriptions(); Set<String> list = Config.BorderDescriptions();
if (list.isEmpty()) if (list.isEmpty()) {
{
sender.sendMessage("There are no borders currently set."); sender.sendMessage("There are no borders currently set.");
return; return;
} }
for(String borderDesc : list) for (String borderDesc : list) {
{
sender.sendMessage(borderDesc); sender.sendMessage(borderDesc);
} }
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdPortal extends WBCmd {
public CmdPortal() {
public class CmdPortal extends WBCmd
{
public CmdPortal()
{
name = permission = "portal"; name = permission = "portal";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -22,18 +19,15 @@ public class CmdPortal extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "Portal redirection is " + enabledColored(Config.portalRedirection()) + C_HEAD + "."); sender.sendMessage(C_HEAD + "Portal redirection is " + enabledColored(Config.portalRedirection()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setPortalRedirection(strAsBool(params.get(0))); Config.setPortalRedirection(strAsBool(params.get(0)));
if (player != null) if (player != null) {
{
Config.log((Config.portalRedirection() ? "Enabled" : "Disabled") + " portal redirection at the command of player \"" + player.getName() + "\"."); Config.log((Config.portalRedirection() ? "Enabled" : "Disabled") + " portal redirection at the command of player \"" + player.getName() + "\".");
cmdStatus(sender); cmdStatus(sender);
} }

View File

@ -1,11 +1,10 @@
package com.wimbli.WorldBorder.cmd; package com.wimbli.WorldBorder.cmd;
import java.util.List; import com.wimbli.WorldBorder.Config;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.Config; import java.util.List;
public class CmdPreventPlace extends WBCmd { public class CmdPreventPlace extends WBCmd {
@ -18,18 +17,15 @@ public class CmdPreventPlace extends WBCmd {
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "Prevention of block placement outside the border is " + enabledColored(Config.preventBlockPlace()) + C_HEAD + "."); sender.sendMessage(C_HEAD + "Prevention of block placement outside the border is " + enabledColored(Config.preventBlockPlace()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setPreventBlockPlace(strAsBool(params.get(0))); Config.setPreventBlockPlace(strAsBool(params.get(0)));
if (player != null) if (player != null) {
{
Config.log((Config.preventBlockPlace() ? "Enabled" : "Disabled") + " preventblockplace at the command of player \"" + player.getName() + "\"."); Config.log((Config.preventBlockPlace() ? "Enabled" : "Disabled") + " preventblockplace at the command of player \"" + player.getName() + "\".");
cmdStatus(sender); cmdStatus(sender);
} }

View File

@ -17,18 +17,15 @@ public class CmdPreventSpawn extends WBCmd {
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "Prevention of mob spawning outside the border is " + enabledColored(Config.preventMobSpawn()) + C_HEAD + "."); sender.sendMessage(C_HEAD + "Prevention of mob spawning outside the border is " + enabledColored(Config.preventMobSpawn()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setPreventMobSpawn(strAsBool(params.get(0))); Config.setPreventMobSpawn(strAsBool(params.get(0)));
if (player != null) if (player != null) {
{
Config.log((Config.preventMobSpawn() ? "Enabled" : "Disabled") + " preventmobspawn at the command of player \"" + player.getName() + "\"."); Config.log((Config.preventMobSpawn() ? "Enabled" : "Disabled") + " preventmobspawn at the command of player \"" + player.getName() + "\".");
cmdStatus(sender); cmdStatus(sender);
} }

View File

@ -1,17 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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 java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdRadius extends WBCmd {
public CmdRadius() {
public class CmdRadius extends WBCmd
{
public CmdRadius()
{
name = permission = "radius"; name = permission = "radius";
hasWorldNameInput = true; hasWorldNameInput = true;
minParams = 1; minParams = 1;
@ -24,14 +22,12 @@ public class CmdRadius extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
if (worldName == null) if (worldName == null)
worldName = player.getWorld().getName(); worldName = player.getWorld().getName();
BorderData border = Config.Border(worldName); BorderData border = Config.Border(worldName);
if (border == null) if (border == null) {
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") must first have a border set normally."); sendErrorAndHelp(sender, "This world (\"" + worldName + "\") must first have a border set normally.");
return; return;
} }
@ -40,45 +36,32 @@ public class CmdRadius extends WBCmd
double z = border.getZ(); double z = border.getZ();
int radiusX; int radiusX;
int radiusZ; int radiusZ;
try try {
{ if (params.get(0).startsWith("+")) {
if (params.get(0).startsWith("+"))
{
// Add to the current radius // Add to the current radius
radiusX = border.getRadiusX(); radiusX = border.getRadiusX();
radiusX += Integer.parseInt(params.get(0).substring(1)); radiusX += Integer.parseInt(params.get(0).substring(1));
} } else if (params.get(0).startsWith("-")) {
else if(params.get(0).startsWith("-"))
{
// Subtract from the current radius // Subtract from the current radius
radiusX = border.getRadiusX(); radiusX = border.getRadiusX();
radiusX -= Integer.parseInt(params.get(0).substring(1)); radiusX -= Integer.parseInt(params.get(0).substring(1));
} } else
else
radiusX = Integer.parseInt(params.get(0)); radiusX = Integer.parseInt(params.get(0));
if (params.size() == 2) if (params.size() == 2) {
{ if (params.get(1).startsWith("+")) {
if (params.get(1).startsWith("+"))
{
// Add to the current radius // Add to the current radius
radiusZ = border.getRadiusZ(); radiusZ = border.getRadiusZ();
radiusZ += Integer.parseInt(params.get(1).substring(1)); radiusZ += Integer.parseInt(params.get(1).substring(1));
} } else if (params.get(1).startsWith("-")) {
else if(params.get(1).startsWith("-"))
{
// Subtract from the current radius // Subtract from the current radius
radiusZ = border.getRadiusZ(); radiusZ = border.getRadiusZ();
radiusZ -= Integer.parseInt(params.get(1).substring(1)); radiusZ -= Integer.parseInt(params.get(1).substring(1));
} } else
else
radiusZ = Integer.parseInt(params.get(1)); radiusZ = Integer.parseInt(params.get(1));
} } else
else
radiusZ = radiusX; radiusZ = radiusX;
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The radius value(s) must be integers."); sendErrorAndHelp(sender, "The radius value(s) must be integers.");
return; return;
} }

View File

@ -1,17 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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 java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdReload extends WBCmd {
public CmdReload() {
public class CmdReload extends WBCmd
{
public CmdReload()
{
name = permission = "reload"; name = permission = "reload";
minParams = maxParams = 0; minParams = maxParams = 0;
@ -21,8 +19,7 @@ public class CmdReload extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
if (player != null) if (player != null)
Config.log("Reloading config file at the command of player \"" + player.getName() + "\"."); Config.log("Reloading config file at the command of player \"" + player.getName() + "\".");

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdRemount extends WBCmd {
public CmdRemount() {
public class CmdRemount extends WBCmd
{
public CmdRemount()
{
name = permission = "remount"; name = permission = "remount";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -22,13 +19,11 @@ public class CmdRemount extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
int delay = Config.RemountTicks(); int delay = Config.RemountTicks();
if (delay == 0) 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."); 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 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."); 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) 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."); 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.");
@ -36,17 +31,13 @@ public class CmdRemount extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
int delay = 0; int delay = 0;
try try {
{
delay = Integer.parseInt(params.get(0)); delay = Integer.parseInt(params.get(0));
if (delay < 0) if (delay < 0)
throw new NumberFormatException(); throw new NumberFormatException();
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The remount delay must be an integer of 0 or higher. Setting to 0 will disable remounting."); sendErrorAndHelp(sender, "The remount delay must be an integer of 0 or higher. Setting to 0 will disable remounting.");
return; return;
} }

View File

@ -1,20 +1,17 @@
package com.wimbli.WorldBorder.cmd; 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 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() {
public class CmdSet extends WBCmd
{
public CmdSet()
{
name = permission = "set"; name = permission = "set";
hasWorldNameInput = true; hasWorldNameInput = true;
consoleRequiresWorldName = false; consoleRequiresWorldName = false;
@ -31,29 +28,23 @@ public class CmdSet extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
// passsing a single parameter (radiusX) is only acceptable from player // passsing a single parameter (radiusX) is only acceptable from player
if ((params.size() == 1) && player == null) if ((params.size() == 1) && player == null) {
{
sendErrorAndHelp(sender, "You have not provided a sufficient number of parameters."); sendErrorAndHelp(sender, "You have not provided a sufficient number of parameters.");
return; return;
} }
// "set" command from player or console, world specified // "set" command from player or console, world specified
if (worldName != null) 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
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."); sendErrorAndHelp(sender, "You have not provided a sufficient number of arguments.");
return; return;
} }
World world = sender.getServer().getWorld(worldName); World world = sender.getServer().getWorld(worldName);
if (world == null) if (world == null) {
{ if (params.get(params.size() - 1).equalsIgnoreCase("spawn")) {
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."); sendErrorAndHelp(sender, "The world you specified (\"" + worldName + "\") could not be found on the server, so the spawn point cannot be determined.");
return; return;
} }
@ -61,18 +52,14 @@ public class CmdSet extends WBCmd
} }
} }
// "set" command from player using current world since it isn't specified, or allowed from console only if player name is specified // "set" command from player using current world since it isn't specified, or allowed from console only if player name is specified
else else {
{ if (player == null) {
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
{
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."); sendErrorAndHelp(sender, "You must specify a world name from console if not specifying a player name.");
return; return;
} }
player = Bukkit.getPlayer(params.get(params.size() - 1)); player = Bukkit.getPlayer(params.get(params.size() - 1));
if (player == null || ! player.isOnline()) if (player == null || !player.isOnline()) {
{
sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online."); sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online.");
return; return;
} }
@ -84,20 +71,15 @@ public class CmdSet extends WBCmd
double x, z; double x, z;
int radiusCount = params.size(); int radiusCount = params.size();
try try {
{ if (params.get(params.size() - 1).equalsIgnoreCase("spawn")) { // "spawn" specified for x/z coordinates
if (params.get(params.size() - 1).equalsIgnoreCase("spawn"))
{ // "spawn" specified for x/z coordinates
Location loc = sender.getServer().getWorld(worldName).getSpawnLocation(); Location loc = sender.getServer().getWorld(worldName).getSpawnLocation();
x = loc.getX(); x = loc.getX();
z = loc.getZ(); z = loc.getZ();
radiusCount -= 1; radiusCount -= 1;
} } else if (params.size() > 2 && params.get(params.size() - 2).equalsIgnoreCase("player")) { // player name specified for x/z coordinates
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)); Player playerT = Bukkit.getPlayer(params.get(params.size() - 1));
if (playerT == null || ! playerT.isOnline()) if (playerT == null || !playerT.isOnline()) {
{
sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online."); sendErrorAndHelp(sender, "The player you specified (\"" + params.get(params.size() - 1) + "\") does not appear to be online.");
return; return;
} }
@ -105,17 +87,12 @@ public class CmdSet extends WBCmd
x = playerT.getLocation().getX(); x = playerT.getLocation().getX();
z = playerT.getLocation().getZ(); z = playerT.getLocation().getZ();
radiusCount -= 2; radiusCount -= 2;
} } else {
else if (player == null || radiusCount > 2) { // x and z specified
{
if (player == null || radiusCount > 2)
{ // x and z specified
x = Double.parseDouble(params.get(params.size() - 2)); x = Double.parseDouble(params.get(params.size() - 2));
z = Double.parseDouble(params.get(params.size() - 1)); z = Double.parseDouble(params.get(params.size() - 1));
radiusCount -= 2; radiusCount -= 2;
} } else { // using coordinates of command sender (player)
else
{ // using coordinates of command sender (player)
x = player.getLocation().getX(); x = player.getLocation().getX();
z = player.getLocation().getZ(); z = player.getLocation().getZ();
} }
@ -127,14 +104,11 @@ public class CmdSet extends WBCmd
else else
radiusZ = Integer.parseInt(params.get(1)); radiusZ = Integer.parseInt(params.get(1));
if (radiusX < Config.KnockBack() || radiusZ < Config.KnockBack()) if (radiusX < Config.KnockBack() || radiusZ < Config.KnockBack()) {
{
sendErrorAndHelp(sender, "Radius value(s) must be more than the knockback distance."); sendErrorAndHelp(sender, "Radius value(s) must be more than the knockback distance.");
return; return;
} }
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "Radius value(s) must be integers and x and z values must be numerical."); sendErrorAndHelp(sender, "Radius value(s) must be integers and x and z values must be numerical.");
return; return;
} }

View File

@ -1,18 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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 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() {
public class CmdSetcorners extends WBCmd
{
public CmdSetcorners()
{
name = "setcorners"; name = "setcorners";
permission = "set"; permission = "set";
hasWorldNameInput = true; hasWorldNameInput = true;
@ -25,29 +22,22 @@ public class CmdSetcorners extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{ if (worldName == null) {
if (worldName == null)
{
worldName = player.getWorld().getName(); worldName = player.getWorld().getName();
} } else {
else
{
World worldTest = sender.getServer().getWorld(worldName); World worldTest = sender.getServer().getWorld(worldName);
if (worldTest == null) 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."); sender.sendMessage("The world you specified (\"" + worldName + "\") could not be found on the server, but data for it will be stored anyway.");
} }
try try {
{
double x1 = Double.parseDouble(params.get(0)); double x1 = Double.parseDouble(params.get(0));
double z1 = Double.parseDouble(params.get(1)); double z1 = Double.parseDouble(params.get(1));
double x2 = Double.parseDouble(params.get(2)); double x2 = Double.parseDouble(params.get(2));
double z2 = Double.parseDouble(params.get(3)); double z2 = Double.parseDouble(params.get(3));
Config.setBorderCorners(worldName, x1, z1, x2, z2); Config.setBorderCorners(worldName, x1, z1, x2, z2);
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The x1, z1, x2, and z2 coordinate values must be numerical."); sendErrorAndHelp(sender, "The x1, z1, x2, and z2 coordinate values must be numerical.");
return; return;
} }

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdSetmsg extends WBCmd {
public CmdSetmsg() {
public class CmdSetmsg extends WBCmd
{
public CmdSetmsg()
{
name = permission = "setmsg"; name = permission = "setmsg";
minParams = 1; minParams = 1;
@ -20,8 +17,7 @@ public class CmdSetmsg extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "Border message is set to:"); sender.sendMessage(C_HEAD + "Border message is set to:");
sender.sendMessage(Config.MessageRaw()); sender.sendMessage(Config.MessageRaw());
sender.sendMessage(C_HEAD + "Formatted border message:"); sender.sendMessage(C_HEAD + "Formatted border message:");
@ -29,12 +25,10 @@ public class CmdSetmsg extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
StringBuilder message = new StringBuilder(); StringBuilder message = new StringBuilder();
boolean first = true; boolean first = true;
for (String param : params) for (String param : params) {
{
if (!first) if (!first)
message.append(" "); message.append(" ");
message.append(param); message.append(param);

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdShape extends WBCmd {
public CmdShape() {
public class CmdShape extends WBCmd
{
public CmdShape()
{
name = permission = "shape"; name = permission = "shape";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -24,21 +21,18 @@ public class CmdShape extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "The default border shape for all worlds is currently set to \"" + Config.ShapeName() + "\"."); sender.sendMessage(C_HEAD + "The default border shape for all worlds is currently set to \"" + Config.ShapeName() + "\".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
String shape = params.get(0).toLowerCase(); String shape = params.get(0).toLowerCase();
if (shape.equals("rectangular") || shape.equals("square")) if (shape.equals("rectangular") || shape.equals("square"))
Config.setShape(false); Config.setShape(false);
else if (shape.equals("elliptic") || shape.equals("round")) else if (shape.equals("elliptic") || shape.equals("round"))
Config.setShape(true); Config.setShape(true);
else else {
{
sendErrorAndHelp(sender, "You must specify one of the 4 valid shape names below."); sendErrorAndHelp(sender, "You must specify one of the 4 valid shape names below.");
return; return;
} }

View File

@ -1,18 +1,27 @@
package com.wimbli.WorldBorder.cmd; 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 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() {
public class CmdTrim extends WBCmd
{
public CmdTrim()
{
name = permission = "trim"; name = permission = "trim";
hasWorldNameInput = true; hasWorldNameInput = true;
consoleRequiresWorldName = false; consoleRequiresWorldName = false;
@ -26,25 +35,20 @@ public class CmdTrim extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
boolean confirm = false; boolean confirm = false;
// check for "cancel", "pause", or "confirm" // check for "cancel", "pause", or "confirm"
if (params.size() >= 1) if (params.size() >= 1) {
{
String check = params.get(0).toLowerCase(); String check = params.get(0).toLowerCase();
if (check.equals("cancel") || check.equals("stop")) if (check.equals("cancel") || check.equals("stop")) {
{
if (!makeSureTrimIsRunning(sender)) if (!makeSureTrimIsRunning(sender))
return; return;
sender.sendMessage(C_HEAD + "Cancelling the world map trimming task."); sender.sendMessage(C_HEAD + "Cancelling the world map trimming task.");
trimDefaults(); trimDefaults();
Config.StopTrimTask(); Config.StopTrimTask();
return; return;
} } else if (check.equals("pause")) {
else if (check.equals("pause"))
{
if (!makeSureTrimIsRunning(sender)) if (!makeSureTrimIsRunning(sender))
return; return;
Config.trimTask.pause(); Config.trimTask.pause();
@ -56,12 +60,10 @@ public class CmdTrim extends WBCmd
} }
// if not just confirming, make sure a world name is available // if not just confirming, make sure a world name is available
if (worldName == null && !confirm) if (worldName == null && !confirm) {
{
if (player != null) if (player != null)
worldName = player.getWorld().getName(); worldName = player.getWorld().getName();
else else {
{
sendErrorAndHelp(sender, "You must specify a world!"); sendErrorAndHelp(sender, "You must specify a world!");
return; return;
} }
@ -71,29 +73,24 @@ public class CmdTrim extends WBCmd
String cmd = cmd(sender) + nameEmphasized() + C_CMD; String cmd = cmd(sender) + nameEmphasized() + C_CMD;
// make sure Trim isn't already running // make sure Trim isn't already running
if (Config.trimTask != null && Config.trimTask.valid()) if (Config.trimTask != null && Config.trimTask.valid()) {
{
sender.sendMessage(C_ERR + "The world map trimming task is already running."); 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 + "."); sender.sendMessage(C_DESC + "You can cancel at any time with " + cmd + "cancel" + C_DESC + ", or pause/unpause with " + cmd + "pause" + C_DESC + ".");
return; return;
} }
// set frequency and/or padding if those were specified // set frequency and/or padding if those were specified
try try {
{
if (params.size() >= 1 && !confirm) if (params.size() >= 1 && !confirm)
trimFrequency = Math.abs(Integer.parseInt(params.get(0))); trimFrequency = Math.abs(Integer.parseInt(params.get(0)));
if (params.size() >= 2 && !confirm) if (params.size() >= 2 && !confirm)
trimPadding = Math.abs(Integer.parseInt(params.get(1))); trimPadding = Math.abs(Integer.parseInt(params.get(1)));
} } catch (NumberFormatException ex) {
catch(NumberFormatException ex)
{
sendErrorAndHelp(sender, "The frequency and padding values must be integers."); sendErrorAndHelp(sender, "The frequency and padding values must be integers.");
trimDefaults(); trimDefaults();
return; return;
} }
if (trimFrequency <= 0) if (trimFrequency <= 0) {
{
sendErrorAndHelp(sender, "The frequency value must be greater than zero."); sendErrorAndHelp(sender, "The frequency value must be greater than zero.");
trimDefaults(); trimDefaults();
return; return;
@ -103,10 +100,8 @@ public class CmdTrim extends WBCmd
if (worldName != null) if (worldName != null)
trimWorld = worldName; trimWorld = worldName;
if (confirm) if (confirm) { // command confirmed, go ahead with it
{ // command confirmed, go ahead with it if (trimWorld.isEmpty()) {
if (trimWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first use this command successfully without confirming."); sendErrorAndHelp(sender, "You must first use this command successfully without confirming.");
return; return;
} }
@ -121,21 +116,16 @@ public class CmdTrim extends WBCmd
ticks = 20 / trimFrequency; ticks = 20 / trimFrequency;
Config.trimTask = new WorldTrimTask(Bukkit.getServer(), player, trimWorld, trimPadding, repeats); Config.trimTask = new WorldTrimTask(Bukkit.getServer(), player, trimWorld, trimPadding, repeats);
if (Config.trimTask.valid()) if (Config.trimTask.valid()) {
{
int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.trimTask, ticks, ticks); int task = Bukkit.getServer().getScheduler().scheduleSyncRepeatingTask(WorldBorder.plugin, Config.trimTask, ticks, ticks);
Config.trimTask.setTaskID(task); Config.trimTask.setTaskID(task);
sender.sendMessage("WorldBorder map trimming task for world \"" + trimWorld + "\" started."); sender.sendMessage("WorldBorder map trimming task for world \"" + trimWorld + "\" started.");
} } else
else
sender.sendMessage(C_ERR + "The world map trimming task failed to start."); sender.sendMessage(C_ERR + "The world map trimming task failed to start.");
trimDefaults(); trimDefaults();
} } else {
else if (trimWorld.isEmpty()) {
{
if (trimWorld.isEmpty())
{
sendErrorAndHelp(sender, "You must first specify a valid world."); sendErrorAndHelp(sender, "You must first specify a valid world.");
return; return;
} }
@ -147,26 +137,13 @@ public class CmdTrim extends WBCmd
} }
} }
private void trimDefaults() {
/* 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 = ""; trimWorld = "";
trimFrequency = 5000; trimFrequency = 5000;
trimPadding = defaultPadding; trimPadding = defaultPadding;
} }
private boolean makeSureTrimIsRunning(CommandSender sender) private boolean makeSureTrimIsRunning(CommandSender sender) {
{
if (Config.trimTask != null && Config.trimTask.valid()) if (Config.trimTask != null && Config.trimTask.valid())
return true; return true;
sendErrorAndHelp(sender, "The world map trimming task is not currently running."); sendErrorAndHelp(sender, "The world map trimming task is not currently running.");

View File

@ -1,17 +1,14 @@
package com.wimbli.WorldBorder.cmd; 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.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdWhoosh extends WBCmd {
public CmdWhoosh() {
public class CmdWhoosh extends WBCmd
{
public CmdWhoosh()
{
name = permission = "whoosh"; name = permission = "whoosh";
minParams = maxParams = 1; minParams = maxParams = 1;
@ -21,18 +18,15 @@ public class CmdWhoosh extends WBCmd
} }
@Override @Override
public void cmdStatus(CommandSender sender) public void cmdStatus(CommandSender sender) {
{
sender.sendMessage(C_HEAD + "\"Whoosh\" knockback effect is " + enabledColored(Config.whooshEffect()) + C_HEAD + "."); sender.sendMessage(C_HEAD + "\"Whoosh\" knockback effect is " + enabledColored(Config.whooshEffect()) + C_HEAD + ".");
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{
Config.setWhooshEffect(strAsBool(params.get(0))); Config.setWhooshEffect(strAsBool(params.get(0)));
if (player != null) if (player != null) {
{
Config.log((Config.whooshEffect() ? "Enabled" : "Disabled") + " \"whoosh\" knockback effect at the command of player \"" + player.getName() + "\"."); Config.log((Config.whooshEffect() ? "Enabled" : "Disabled") + " \"whoosh\" knockback effect at the command of player \"" + player.getName() + "\".");
cmdStatus(sender); cmdStatus(sender);
} }

View File

@ -1,17 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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 java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdWrap extends WBCmd {
public CmdWrap() {
public class CmdWrap extends WBCmd
{
public CmdWrap()
{
name = permission = "wrap"; name = permission = "wrap";
minParams = 1; minParams = 1;
maxParams = 2; maxParams = 2;
@ -23,10 +21,8 @@ public class CmdWrap extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{ if (player == null && params.size() == 1) {
if (player == null && params.size() == 1)
{
sendErrorAndHelp(sender, "When running this command from console, you must specify a world."); sendErrorAndHelp(sender, "When running this command from console, you must specify a world.");
return; return;
} }
@ -34,21 +30,18 @@ public class CmdWrap extends WBCmd
boolean wrap = false; boolean wrap = false;
// world and wrap on/off specified // world and wrap on/off specified
if (params.size() == 2) if (params.size() == 2) {
{
worldName = params.get(0); worldName = params.get(0);
wrap = strAsBool(params.get(1)); wrap = strAsBool(params.get(1));
} }
// no world specified, just wrap on/off // no world specified, just wrap on/off
else else {
{
worldName = player.getWorld().getName(); worldName = player.getWorld().getName();
wrap = strAsBool(params.get(0)); wrap = strAsBool(params.get(0));
} }
BorderData border = Config.Border(worldName); BorderData border = Config.Border(worldName);
if (border == null) if (border == null) {
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set."); sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return; return;
} }

View File

@ -1,17 +1,15 @@
package com.wimbli.WorldBorder.cmd; 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 java.util.List;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import com.wimbli.WorldBorder.*; public class CmdWshape extends WBCmd {
public CmdWshape() {
public class CmdWshape extends WBCmd
{
public CmdWshape()
{
name = permission = "wshape"; name = permission = "wshape";
minParams = 1; minParams = 1;
maxParams = 2; maxParams = 2;
@ -25,10 +23,8 @@ public class CmdWshape extends WBCmd
} }
@Override @Override
public void execute(CommandSender sender, Player player, List<String> params, String worldName) public void execute(CommandSender sender, Player player, List<String> params, String worldName) {
{ if (player == null && params.size() == 1) {
if (player == null && params.size() == 1)
{
sendErrorAndHelp(sender, "When running this command from console, you must specify a world."); sendErrorAndHelp(sender, "When running this command from console, you must specify a world.");
return; return;
} }
@ -36,21 +32,18 @@ public class CmdWshape extends WBCmd
String shapeName = ""; String shapeName = "";
// world and shape specified // world and shape specified
if (params.size() == 2) if (params.size() == 2) {
{
worldName = params.get(0); worldName = params.get(0);
shapeName = params.get(1).toLowerCase(); shapeName = params.get(1).toLowerCase();
} }
// no world specified, just shape // no world specified, just shape
else else {
{
worldName = player.getWorld().getName(); worldName = player.getWorld().getName();
shapeName = params.get(0).toLowerCase(); shapeName = params.get(0).toLowerCase();
} }
BorderData border = Config.Border(worldName); BorderData border = Config.Border(worldName);
if (border == null) if (border == null) {
{
sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set."); sendErrorAndHelp(sender, "This world (\"" + worldName + "\") does not have a border set.");
return; return;
} }

View File

@ -1,49 +1,18 @@
package com.wimbli.WorldBorder.cmd; 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.ArrayList;
import java.util.List; import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
public abstract class WBCmd {
public abstract class WBCmd
{
/* /*
* Primary variables, should be set as needed in constructors for the subclassed commands * Primary variables, should be set as needed in constructors for the subclassed commands
*/ */
// 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;
/*
* 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);
/*
* This is an optional override, used to provide some extra command status info, like the currently set value
*/
public void cmdStatus(CommandSender sender) {}
/*
* Helper variables and methods
*/
// color values for strings // color values for strings
public final static String C_CMD = ChatColor.AQUA.toString(); // main commands 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_DESC = ChatColor.WHITE.toString(); // command descriptions
@ -51,40 +20,60 @@ public abstract class WBCmd
public final static String C_HEAD = ChatColor.YELLOW.toString(); // command listing header 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_OPT = ChatColor.DARK_GREEN.toString(); // optional values
public final static String C_REQ = ChatColor.GREEN.toString(); // required values public final static String C_REQ = ChatColor.GREEN.toString(); // required values
// colorized root command, for console and for player // colorized root command, for console and for player
public final static String CMD_C = C_CMD + "wb "; public final static String CMD_C = C_CMD + "wb ";
public final static String CMD_P = 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
/*
* 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 // 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 // ... 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> cmdExamplePlayer = new ArrayList<String>();
public List<String> cmdExampleConsole = new ArrayList<String>(); public List<String> cmdExampleConsole = new ArrayList<String>();
// 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 * The guts of the command run in here; needs to be overriden in the subclassed commands
public final static List<String> cmdExamplesPlayer = new ArrayList<String>(48); // still, could need to increase later */
public abstract void execute(CommandSender sender, Player player, List<String> params, String worldName);
/*
* This is an optional override, used to provide some extra command status info, like the currently set value
*/
public void cmdStatus(CommandSender sender) {
}
// add command examples for use the default "/wb" command list and for internal usage reference, formatted and colorized // add command examples for use the default "/wb" command list and for internal usage reference, formatted and colorized
public void addCmdExample(String example) public void addCmdExample(String example) {
{
addCmdExample(example, true, true, true); addCmdExample(example, true, true, true);
} }
public void addCmdExample(String example, boolean forPlayer, boolean forConsole, boolean prefix)
{ public void addCmdExample(String example, boolean forPlayer, boolean forConsole, boolean prefix) {
// go ahead and colorize required "<>" and optional "[]" parameters, extra command words, and description // 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 + "- "); example = example.replace("<", C_REQ + "<").replace("[", C_OPT + "[").replace("^", C_CMD).replace("- ", C_DESC + "- ");
// all "{}" are replaced by "[]" (optional) for player, "<>" (required) for console // all "{}" are replaced by "[]" (optional) for player, "<>" (required) for console
if (forPlayer) if (forPlayer) {
{
String exampleP = (prefix ? CMD_P : "") + example.replace("{", C_OPT + "[").replace("}", "]"); String exampleP = (prefix ? CMD_P : "") + example.replace("{", C_OPT + "[").replace("}", "]");
cmdExamplePlayer.add(exampleP); cmdExamplePlayer.add(exampleP);
cmdExamplesPlayer.add(exampleP); cmdExamplesPlayer.add(exampleP);
} }
if (forConsole) if (forConsole) {
{
String exampleC = (prefix ? CMD_C : "") + example.replace("{", C_REQ + "<").replace("}", ">"); String exampleC = (prefix ? CMD_C : "") + example.replace("{", C_REQ + "<").replace("}", ">");
cmdExampleConsole.add(exampleC); cmdExampleConsole.add(exampleC);
cmdExamplesConsole.add(exampleC); cmdExamplesConsole.add(exampleC);
@ -92,38 +81,32 @@ public abstract class WBCmd
} }
// return root command formatted for player or console, based on sender // return root command formatted for player or console, based on sender
public String cmd(CommandSender sender) public String cmd(CommandSender sender) {
{
return (sender instanceof Player) ? CMD_P : CMD_C; return (sender instanceof Player) ? CMD_P : CMD_C;
} }
// formatted and colorized text, intended for marking command name // formatted and colorized text, intended for marking command name
public String commandEmphasized(String text) public String commandEmphasized(String text) {
{
return C_CMD + ChatColor.UNDERLINE + text + ChatColor.RESET + " "; return C_CMD + ChatColor.UNDERLINE + text + ChatColor.RESET + " ";
} }
// returns green "enabled" or red "disabled" text // returns green "enabled" or red "disabled" text
public String enabledColored(boolean enabled) public String enabledColored(boolean enabled) {
{
return enabled ? C_REQ + "enabled" : C_ERR + "disabled"; return enabled ? C_REQ + "enabled" : C_ERR + "disabled";
} }
// formatted and colorized command name, optionally prefixed with "[world]" (for player) / "<world>" (for console) // formatted and colorized command name, optionally prefixed with "[world]" (for player) / "<world>" (for console)
public String nameEmphasized() public String nameEmphasized() {
{
return commandEmphasized(name); return commandEmphasized(name);
} }
public String nameEmphasizedW()
{ public String nameEmphasizedW() {
return "{world} " + nameEmphasized(); return "{world} " + nameEmphasized();
} }
// send command example message(s) and other helpful info // send command example message(s) and other helpful info
public void sendCmdHelp(CommandSender sender) public void sendCmdHelp(CommandSender sender) {
{ for (String example : ((sender instanceof Player) ? cmdExamplePlayer : cmdExampleConsole)) {
for (String example : ((sender instanceof Player) ? cmdExamplePlayer : cmdExampleConsole))
{
sender.sendMessage(example); sender.sendMessage(example);
} }
cmdStatus(sender); cmdStatus(sender);
@ -132,15 +115,13 @@ public abstract class WBCmd
} }
// send error message followed by command example message(s) // send error message followed by command example message(s)
public void sendErrorAndHelp(CommandSender sender, String error) public void sendErrorAndHelp(CommandSender sender, String error) {
{
sender.sendMessage(C_ERR + error); sender.sendMessage(C_ERR + error);
sendCmdHelp(sender); sendCmdHelp(sender);
} }
// interpret string as boolean value (yes/no, true/false, on/off, +/-, 1/0) // interpret string as boolean value (yes/no, true/false, on/off, +/-, 1/0)
public boolean strAsBool(String str) public boolean strAsBool(String str) {
{
str = str.toLowerCase(); str = str.toLowerCase();
return str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1"); return str.startsWith("y") || str.startsWith("t") || str.startsWith("on") || str.startsWith("+") || str.startsWith("1");
} }

View File

@ -1,7 +1,7 @@
name: WorldBorder name: WorldBorder
author: Brettflan authors: [Brettflan, PryPurity]
description: Efficient, feature-rich plugin for limiting the size of your worlds. 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 api-version: 1.13
main: com.wimbli.WorldBorder.WorldBorder main: com.wimbli.WorldBorder.WorldBorder
softdepend: softdepend: