Several changes for v1.1 release:

moved border checking routine out into a timed task, for even lower server impact
added new configurable "delay" value, for the number of ticks it will wait between border checks, the default is 4 (~200ms); each server tick is ~50ms or so
added new configurable "knockback" value, for how far back inside the border to move a stray player, the default is 3.0
changed world-name encoding method for worlds with dots in the name to use an uncommon high-range ASCII character ("¨"), since forward-slash ("/") can occur in world names; don't worry, old configurations which have the "."->"/" encoding will be automatically and safely converted
split command listing to 2 pages for players, since there are too many commands to fit on the screen now
This commit is contained in:
Brettflan 2011-04-09 09:55:57 -05:00
parent dddceca7f1
commit 36e6d2ce2b
7 changed files with 208 additions and 141 deletions

View File

@ -11,4 +11,4 @@ This plugin is intended to be able to supersede BorderGuard Lite and rBorder in
More Info
=========
<a href="http://forums.bukkit.org/threads/worldborder-617.11309/">Bukkit Forum Topic</a>
<a href="http://forums.bukkit.org/threads/worldborder.11309/">Bukkit Forum Topic</a>

View File

@ -1,8 +1,7 @@
package com.wimbli.WorldBorder;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.LinkedHashSet;
import org.bukkit.Location;
import org.bukkit.World;
@ -112,13 +111,13 @@ public class BorderData
if (!round)
{
if (xLoc <= minX)
xLoc = minX + 3;
xLoc = minX + Config.KnockBack();
else if (xLoc >= maxX)
xLoc = maxX - 3;
xLoc = maxX - Config.KnockBack();
if (zLoc <= minZ)
zLoc = minZ + 3;
zLoc = minZ + Config.KnockBack();
else if (zLoc >= maxZ)
zLoc = maxZ - 3;
zLoc = maxZ - Config.KnockBack();
}
// round border
@ -128,8 +127,8 @@ public class BorderData
double vX = xLoc - x;
double vZ = zLoc - z;
double magV = Math.sqrt(vX*vX + vZ*vZ);
xLoc = x + vX / magV * (radius - 3);
zLoc = z + vZ / magV * (radius - 3);
xLoc = x + vX / magV * (radius - Config.KnockBack());
zLoc = z + vZ / magV * (radius - Config.KnockBack());
}
yLoc = getSafeY(loc.getWorld(), Location.locToBlock(xLoc), Location.locToBlock(yLoc), Location.locToBlock(zLoc));
@ -140,12 +139,12 @@ public class BorderData
}
//these material IDs are acceptable for places to teleport player; breathable blocks and water
private static Set<Integer> acceptableBlocks = new HashSet<Integer>(Arrays.asList(
private static LinkedHashSet<Integer> acceptableBlocks = new LinkedHashSet<Integer>(Arrays.asList(
new Integer[] {0, 6, 8, 9, 37, 38, 39, 40, 50, 55, 59, 63, 64, 65, 66, 68, 69, 70, 71, 72, 75, 76, 77, 83, 93, 94}
));
//these material IDs are ones we don't want to drop the player onto
private static Set<Integer> painfulBlocks = new HashSet<Integer>(Arrays.asList(
//these material IDs are ones we don't want to drop the player onto, like cactus or lava
private static LinkedHashSet<Integer> painfulBlocks = new LinkedHashSet<Integer>(Arrays.asList(
new Integer[] {10, 11, 81}
));

View File

@ -2,7 +2,7 @@ package com.wimbli.WorldBorder;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.logging.Level;
@ -30,12 +30,16 @@ public class Config
private static GroupManager GroupPlugin = null;
private static final Logger mcLog = Logger.getLogger("Minecraft");
public static DecimalFormat coord = new DecimalFormat("0.0");
private static int borderTask = -1;
public static Set<String> movedPlayers = Collections.synchronizedSet(new HashSet<String>());
// actual configuration values which can be changed
private static boolean shapeRound = false;
private static Map<String, BorderData> borders = Collections.synchronizedMap(new HashMap<String, BorderData>());
private static Map<String, BorderData> borders = Collections.synchronizedMap(new LinkedHashMap<String, BorderData>());
private static String message;
private static boolean DEBUG = false;
private static double knockBack = 3.0;
private static int timerTicks = 4;
public static void setBorder(String world, BorderData border)
{
@ -125,6 +129,54 @@ public class Config
return DEBUG;
}
public static void setKnockBack(double numBlocks)
{
knockBack = numBlocks;
Log("Knockback set to " + knockBack + " blocks inside the border.");
save(true);
}
public static double KnockBack()
{
return knockBack;
}
public static void setTimerTicks(int ticks)
{
timerTicks = ticks;
Log("Timer delay set to " + timerTicks + " tick(s). That is roughly " + (timerTicks * 50) + "ms.");
StartBorderTimer();
save(true);
}
public static int TimerTicks()
{
return timerTicks;
}
public static void StartBorderTimer()
{
StopBorderTimer();
borderTask = plugin.getServer().getScheduler().scheduleSyncRepeatingTask(plugin, new BorderCheckTask(plugin.getServer()), timerTicks, timerTicks);
if (borderTask == -1)
LogWarn("Failed to start timed border-checking task! This will prevent the plugin from working. Try restarting Bukkit.");
LogConfig("Border-checking timed task started.");
}
public static void StopBorderTimer()
{
if (borderTask == -1) return;
plugin.getServer().getScheduler().cancelTask(borderTask);
borderTask = -1;
LogConfig("Border-checking timed task stopped.");
}
public static void loadPermissions(WorldBorder plugin)
{
if (GroupPlugin != null || Permissions != null || plugin == null)
@ -179,6 +231,7 @@ public class Config
return true;
}
public static void Log(Level lvl, String text)
{
String name = (plugin == null) ? "WorldBorder" : plugin.getDescription().getName();
@ -197,18 +250,33 @@ public class Config
Log(Level.INFO, "[CONFIG] " + text);
}
public static void load(WorldBorder master, boolean logIt)
{ // load config from file
plugin = master;
cfg = plugin.getConfiguration();
int cfgVersion = cfg.getInt("cfg-version", 1);
message = cfg.getString("message");
shapeRound = cfg.getBoolean("round-border", false);
LogConfig("Using " + (shapeRound ? "round" : "square") + " border shape.");
DEBUG = cfg.getBoolean("debug-mode", false);
knockBack = cfg.getDouble("knock-back-dist", 3.0);
timerTicks = cfg.getInt("timer-delay-ticks", 5);
LogConfig("Using " + (shapeRound ? "round" : "square") + " border, knockback of " + knockBack + " blocks, and timer delay of " + timerTicks + ".");
StartBorderTimer();
borders.clear();
if (message == null || message.isEmpty())
{ // store defaults
LogConfig("Configuration not present, creating new file.");
message = "You have reached the edge of this world.";
save(false);
return;
}
Map<String, ConfigurationNode> worlds = cfg.getNodes("worlds");
if (worlds != null)
{
@ -216,7 +284,14 @@ public class Config
while(world.hasNext())
{
Entry wdata = (Entry)world.next();
String name = ((String)wdata.getKey()).replace("/", ".");
String name = null;
// we're swapping "¨" (from extended ASCII set) and "." back and forth at save and load since periods denote configuration nodes, and world names with periods otherwise wreak havoc
if (cfgVersion > 1)
name = ((String)wdata.getKey()).replace("¨", ".");
else // old v1 format, periods encoded as slashes, which had problems
name = ((String)wdata.getKey()).replace("/", ".");
ConfigurationNode bord = (ConfigurationNode)wdata.getValue();
BorderData border = new BorderData(bord.getDouble("x", 0), bord.getDouble("z", 0), bord.getInt("radius", 0));
borders.put(name, border);
@ -224,24 +299,23 @@ public class Config
}
}
if (message == null || message.isEmpty())
{ // store defaults
LogConfig("Configuration not present, creating new file.");
message = "You have reached the edge of this world.";
shapeRound = false;
save(false);
}
else if (logIt)
if (logIt)
LogConfig("Configuration loaded.");
if (cfgVersion < 2)
save(false);
}
public static void save(boolean logIt)
{ // save config to file
if (cfg == null) return;
cfg.setProperty("cfg-version", 2);
cfg.setProperty("message", message);
cfg.setProperty("round-border", shapeRound);
cfg.setProperty("debug-mode", DEBUG);
cfg.setProperty("knock-back-dist", knockBack);
cfg.setProperty("timer-delay-ticks", timerTicks);
cfg.removeProperty("worlds");
Iterator world = borders.entrySet().iterator();
@ -250,9 +324,9 @@ public class Config
Entry wdata = (Entry)world.next();
String name = (String)wdata.getKey();
BorderData bord = (BorderData)wdata.getValue();
cfg.setProperty("worlds." + name.replace(".", "/") + ".x", bord.getX());
cfg.setProperty("worlds." + name.replace(".", "/") + ".z", bord.getZ());
cfg.setProperty("worlds." + name.replace(".", "/") + ".radius", bord.getRadius());
cfg.setProperty("worlds." + name.replace(".", "¨") + ".x", bord.getX());
cfg.setProperty("worlds." + name.replace(".", "¨") + ".z", bord.getZ());
cfg.setProperty("worlds." + name.replace(".", "¨") + ".radius", bord.getRadius());
}
cfg.save();

View File

@ -285,26 +285,106 @@ public class WBCommand implements CommandExecutor
sender.sendMessage("Debug mode " + (Config.Debug() ? "enabled" : "disabled") + ".");
}
// "knockback" command from player or console
else if (split.length == 2 && split[0].equalsIgnoreCase("knockback"))
{
if (!Config.HasPermission(player, "knockback")) return true;
double numBlocks = 0.0;
try
{
numBlocks = Double.parseDouble(split[1]);
}
catch(NumberFormatException ex)
{
sender.sendMessage(ChatColor.RED + "The knockback must be a decimal value above 0.");
return true;
}
if (numBlocks <= 0.0)
{
sender.sendMessage(ChatColor.RED + "The knockback must be a decimal value above 0.");
return true;
}
Config.setKnockBack(numBlocks);
if (player != null)
sender.sendMessage("Knockback set to " + numBlocks + " blocks inside the border.");
}
// "delay" command from player or console
else if (split.length == 2 && split[0].equalsIgnoreCase("delay"))
{
if (!Config.HasPermission(player, "delay")) return true;
int delay = 0;
try
{
delay = Integer.parseInt(split[1]);
}
catch(NumberFormatException ex)
{
sender.sendMessage(ChatColor.RED + "The timer delay must be an integer of 1 or higher.");
return true;
}
if (delay < 1)
{
sender.sendMessage(ChatColor.RED + "The timer delay must be an integer of 1 or higher.");
return true;
}
Config.setTimerTicks(delay);
if (player != null)
sender.sendMessage("Timer delay set to " + delay + " tick(s). That is roughly " + (delay * 50) + "ms.");
}
// we couldn't decipher any known commands, so show help
else
{
if (!Config.HasPermission(player, "help")) return true;
sender.sendMessage(ChatColor.WHITE + plugin.getDescription().getFullName() + " - commands (" + (player != null ? ChatColor.DARK_GREEN + "[optional] " : "") + ChatColor.GREEN + "<required>" + ChatColor.WHITE + "):");
if (player != null)
sender.sendMessage(cmd+" set " + ChatColor.GREEN + "<radius>" + ChatColor.WHITE + " - set world border, centered on you.");
sender.sendMessage(cmdW+" set " + ChatColor.GREEN + "<radius> <x> <z>" + ChatColor.WHITE + " - set world border.");
sender.sendMessage(cmdW+" radius " + ChatColor.GREEN + "<radius>" + ChatColor.WHITE + " - change a border radius.");
sender.sendMessage(cmdW+" clear" + ChatColor.WHITE + " - remove border for this world.");
sender.sendMessage(cmd+" clear all" + ChatColor.WHITE + " - remove border for all worlds.");
sender.sendMessage(cmd+" list" + ChatColor.WHITE + " - show border information for all worlds.");
sender.sendMessage(cmd+" shape " + ChatColor.GREEN + "<round|square>" + ChatColor.WHITE + " - set the border shape.");
sender.sendMessage(cmd+" getmsg" + ChatColor.WHITE + " - display border message.");
sender.sendMessage(cmd+" setmsg " + ChatColor.GREEN + "<text>" + ChatColor.WHITE + " - set border message.");
if (player == null)
int page = (player == null) ? 0 : 1;
if (split.length == 1)
{
try
{
page = Integer.parseInt(split[0]);
}
catch(NumberFormatException ex)
{
}
if (page > 2)
page = 1;
}
sender.sendMessage(ChatColor.YELLOW + plugin.getDescription().getFullName() + " - commands (" + (player != null ? ChatColor.DARK_GREEN + "[optional] " : "") + ChatColor.GREEN + "<required>" + ChatColor.YELLOW + ")" + (page > 0 ? " " + page + "/2" : "") + ":");
if (page == 0 || page == 1)
{
if (player != null)
sender.sendMessage(cmd+" set " + ChatColor.GREEN + "<radius>" + ChatColor.WHITE + " - set world border, centered on you.");
sender.sendMessage(cmdW+" set " + ChatColor.GREEN + "<radius> <x> <z>" + ChatColor.WHITE + " - set world border.");
sender.sendMessage(cmdW+" radius " + ChatColor.GREEN + "<radius>" + ChatColor.WHITE + " - change a border radius.");
sender.sendMessage(cmdW+" clear" + ChatColor.WHITE + " - remove border for this world.");
sender.sendMessage(cmd+" clear all" + ChatColor.WHITE + " - remove border for all worlds.");
sender.sendMessage(cmd+" list" + ChatColor.WHITE + " - show border information for all worlds.");
sender.sendMessage(cmd+" shape " + ChatColor.GREEN + "<round|square>" + ChatColor.WHITE + " - set the border shape.");
sender.sendMessage(cmd+" knockback " + ChatColor.GREEN + "<distance>" + ChatColor.WHITE + " - how far to move the player back.");
if (page == 1)
sender.sendMessage(cmd+" 2" + ChatColor.WHITE + " - view second page of commands.");
}
if (page == 0 || page == 2)
{
sender.sendMessage(cmd+" getmsg" + ChatColor.WHITE + " - display border message.");
sender.sendMessage(cmd+" setmsg " + ChatColor.GREEN + "<text>" + ChatColor.WHITE + " - set border message.");
sender.sendMessage(cmd+" delay " + ChatColor.GREEN + "<amount>" + ChatColor.WHITE + " - time between border checks.");
sender.sendMessage(cmd+" reload" + ChatColor.WHITE + " - re-load data from config.yml.");
sender.sendMessage(cmd+" debug " + ChatColor.GREEN + "<on|off>" + ChatColor.WHITE + " - turn console debug output on or off.");
if (page == 2)
sender.sendMessage(cmd + ChatColor.WHITE + " - view first page of commands.");
}
}

View File

@ -1,11 +1,9 @@
package com.wimbli.WorldBorder;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.event.player.*;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import org.bukkit.World;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerListener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
public class WBPlayerListener extends PlayerListener
@ -13,21 +11,7 @@ public class WBPlayerListener extends PlayerListener
@Override
public void onPlayerJoin(PlayerJoinEvent event)
{
Player player = event.getPlayer();
if (player == null) return;
Location loc = player.getLocation();
if (loc == null) return;
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
return;
Location newLoc = newLocation(player, loc, border);
player.teleport(newLoc);
Config.movedPlayers.add(event.getPlayer().getName());
}
@Override
@ -35,30 +19,7 @@ public class WBPlayerListener extends PlayerListener
{
if (event.isCancelled()) return;
Player player = event.getPlayer();
if (player == null) return;
Location loc = event.getTo();
if (loc == null) return;
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
return;
Location newLoc = newLocation(player, loc, border);
if (!player.isInsideVehicle())
player.teleport(newLoc);
else
{
newLoc.setY(newLoc.getY() + 1);
player.getVehicle().setVelocity(new Vector(0, 0, 0));
player.getVehicle().teleport(newLoc);
}
event.setTo(newLoc);
Config.movedPlayers.add(event.getPlayer().getName());
}
@Override
@ -66,56 +27,6 @@ public class WBPlayerListener extends PlayerListener
{
if (event.isCancelled()) return;
Player player = event.getPlayer();
if (player == null) return;
Location loc = event.getTo();
if (loc == null) return;
World world = loc.getWorld();
if (world == null) return;
BorderData border = Config.Border(world.getName());
if (border == null) return;
if (border.insideBorder(loc.getX(), loc.getZ(), Config.ShapeRound()))
return;
Location newLoc = newLocation(player, loc, border);
if (!player.isInsideVehicle())
player.teleport(newLoc);
else
{
newLoc.setY(newLoc.getY() + 1);
player.getVehicle().setVelocity(new Vector(0, 0, 0));
player.getVehicle().teleport(newLoc);
}
event.setTo(newLoc);
}
private static Location newLocation(Player player, Location loc, BorderData border)
{
if (Config.Debug())
{
Config.LogWarn("Border crossing. 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()));
}
Location newLoc = border.correctedPosition(loc, Config.ShapeRound());
// it's remotely possible (such as in the Nether) a suitable location isn't available, in which case...
if (newLoc == null)
{
if (Config.Debug())
Config.LogWarn("Target new location unviable, using spawn.");
newLoc = player.getServer().getWorlds().get(0).getSpawnLocation();
}
if (Config.Debug())
Config.LogWarn("New position X: " + Config.coord.format(newLoc.getX()) + " Y: " + Config.coord.format(newLoc.getY()) + " Z: " + Config.coord.format(newLoc.getZ()));
player.sendMessage(ChatColor.RED + Config.Message());
return newLoc;
Config.movedPlayers.add(event.getPlayer().getName());
}
}

View File

@ -13,8 +13,8 @@ public class WorldBorder extends JavaPlugin
public void onEnable()
{
PluginDescriptionFile desc = this.getDescription();
System.out.println( desc.getName() + " version " + desc.getVersion() + " loading" );
PluginDescriptionFile desc = this.getDescription();
System.out.println( desc.getName() + " version " + desc.getVersion() + " loading" );
// Load (or create new) config file, and connect to Permissions if it's available
Config.load(this, false);
@ -35,7 +35,8 @@ public class WorldBorder extends JavaPlugin
public void onDisable()
{
PluginDescriptionFile desc = this.getDescription();
System.out.println( desc.getName() + " version " + desc.getVersion() + " shutting down" );
PluginDescriptionFile desc = this.getDescription();
System.out.println( desc.getName() + " version " + desc.getVersion() + " shutting down" );
Config.StopBorderTimer();
}
}

View File

@ -1,7 +1,7 @@
name: WorldBorder
author: Brettflan
description: Limit the size of your worlds with a border, round or square.
version: 1.0.1
version: 1.1
main: com.wimbli.WorldBorder.WorldBorder
commands:
wborder:
@ -17,4 +17,6 @@ commands:
/<command> list - show border information for all worlds.
/<command> shape <round|square> - set the border shape.
/<command> getmsg - display border message.
/<command> setmsg <text> - set border message.
/<command> setmsg <text> - set border message.
/<command> knockback <distance> - how far to move the player back.
/<command> delay <amount> - time between border checks.