Decouple island location from island center. (#1659)

* Decouple island location from island center.

This enables the protection area to move anywhere within the island
boundaries

* Paste island at island location.

* Move visitors to the island location

* Added island location placeholders

* Use island location instead of island center

* Fix tests

* Allow radius 1 islands.

* Correct admin range set limit

* Debug in progress

* Fix island deletion.

The speed option was doing nothing except causing repeated actions on
the same chunks.

* Soft depend for WorldGeneratorApi for Boxed game mode

* Changed to "protectionCenter".

Added logic to expand the deletion area based on moving the island
protection location and range instead of just deleting everything. This
will keep deletion time to a minimum for BSkyBlock, etc.

* Fix tests.
This commit is contained in:
tastybento 2021-02-13 10:19:53 -08:00 committed by GitHub
parent ce79588805
commit 1d9ce7241e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 345 additions and 98 deletions

View File

@ -120,7 +120,7 @@ public class AdminDeleteCommand extends ConfirmableCommand {
if (vector == null) {
user.sendMessage("general.success");
} else {
user.sendMessage("commands.admin.delete.deleted-island", "[xyz]", Util.xyz(vector));
user.sendMessage("commands.admin.delete.deleted-island", TextVariables.XYZ, Util.xyz(vector));
}
}

View File

@ -83,7 +83,7 @@ public class AdminRegisterCommand extends ConfirmableCommand {
if (i.isSpawn()) {
getIslands().clearSpawn(i.getWorld());
}
user.sendMessage("commands.admin.register.registered-island", "[xyz]", Util.xyz(i.getCenter().toVector()),
user.sendMessage("commands.admin.register.registered-island", TextVariables.XYZ, Util.xyz(i.getCenter().toVector()),
TextVariables.NAME, targetName);
user.sendMessage("general.success");
// Build and call event
@ -114,7 +114,7 @@ public class AdminRegisterCommand extends ConfirmableCommand {
getIslands().setOwner(user, targetUUID, i);
i.setReserved(true);
i.getCenter().getBlock().setType(Material.BEDROCK);
user.sendMessage("commands.admin.register.reserved-island", "[xyz]", Util.xyz(i.getCenter().toVector()),
user.sendMessage("commands.admin.register.reserved-island", TextVariables.XYZ, Util.xyz(i.getCenter().toVector()),
TextVariables.NAME, targetName);
// Build and fire event
IslandEvent.builder()

View File

@ -0,0 +1,107 @@
package world.bentobox.bentobox.api.commands.admin;
import java.util.List;
import java.util.Optional;
import org.bukkit.Location;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.commands.ConfirmableCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Util;
/**
* This command sets the center of the protected area.
* The location can be anywhere inside the island range area. Therefore the protected
* range can be up to 2x the island range.
* The location will change for all environments.
* @author tastybento
* @since 1.16.0
*/
public class AdminSetProtectionCenterCommand extends ConfirmableCommand
{
private Location targetLoc;
private Island island;
/**
* Sub-command constructor
*
* @param parent - the parent composite command
*/
public AdminSetProtectionCenterCommand(CompositeCommand parent) {
super(parent, "setprotectionlocation");
}
@Override
public void setup()
{
this.setPermission("admin.setprotectionlocation");
this.setParametersHelp("commands.admin.setprotectionlocation.parameters");
this.setDescription("commands.admin.setprotectionlocation.description");
}
@Override
public boolean canExecute(User user, String label, List<String> args) {
if (args.size() == 3) {
// Get location
targetLoc = getLocation(user, args);
} else {
targetLoc = new Location(getWorld(), user.getLocation().getBlockX(), user.getLocation().getBlockY(), user.getLocation().getBlockZ());
}
if (targetLoc == null) {
user.sendMessage("commands.admin.setprotectionlocation.xyz-error");
return false;
}
Optional<Island> optionalIsland = getIslands().getIslandAt(targetLoc);
if (!optionalIsland.isPresent()) {
user.sendMessage("commands.admin.setspawnpoint.no-island-here");
return false;
}
island = optionalIsland.get();
return true;
}
private Location getLocation(User user, List<String> args) {
try {
int x = Integer.parseInt(args.get(0));
int y = Integer.parseInt(args.get(1));
int z = Integer.parseInt(args.get(2));
return new Location(getWorld(), x, y, z);
} catch (Exception e) {
return null;
}
}
@Override
public boolean execute(User user, String label, List<String> args) {
String name = getPlayers().getName(island.getOwner());
user.sendMessage("commands.admin.setprotectionlocation.island", TextVariables.XYZ, Util.xyz(island.getCenter().toVector()), TextVariables.NAME, name);
// Confirm
this.askConfirmation(user, user.getTranslation("commands.admin.setprotectionlocation.confirmation", TextVariables.XYZ, Util.xyz(targetLoc.toVector())),
() -> this.setLocation(user));
return true;
}
/**
* Set the island location to the user's location.
* @param user User who initiate change.
*/
private void setLocation(User user) {
try {
// Set
island.setProtectionCenter(targetLoc);
user.sendMessage("commands.admin.setprotectionlocation.success", TextVariables.XYZ, Util.xyz(targetLoc.toVector()));
} catch (Exception e) {
user.sendMessage("commands.admin.setprotectionlocation.failure", TextVariables.XYZ, Util.xyz(targetLoc.toVector()));
getAddon().logError("Protection location could not be changed because the island does not exist");
}
}
}

View File

@ -73,7 +73,7 @@ public class AdminSetspawnCommand extends ConfirmableCommand {
getIslands().setSpawn(i);
i.setSpawnPoint(World.Environment.NORMAL, user.getLocation());
// Set the island's range to the full island space because it is spawn
i.setProtectionRange(i.getRange());
i.setProtectionRange(i.getRange() * 2);
user.sendMessage("commands.admin.setspawn.success");
}
}

View File

@ -82,7 +82,7 @@ public class AdminUnregisterCommand extends ConfirmableCommand {
// Remove all island players that reference this island
oldIsland.getMembers().clear();
getIslands().save(oldIsland);
user.sendMessage("commands.admin.unregister.unregistered-island", "[xyz]", Util.xyz(oldIsland.getCenter().toVector()),
user.sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, Util.xyz(oldIsland.getCenter().toVector()),
TextVariables.NAME, targetName);
}

View File

@ -90,6 +90,8 @@ public abstract class DefaultAdminCommand extends CompositeCommand {
new AdminPurgeCommand(this);
// Settings
new AdminSettingsCommand(this);
// Location
new AdminSetProtectionCenterCommand(this);
}
/**

View File

@ -55,7 +55,7 @@ public class AdminRangeAddCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), targetUUID);
int newRange = island.getProtectionRange() + Integer.parseInt(args.get(1));
if (newRange > island.getRange()) {
if (newRange > island.getRange() * 2) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(island.getRange()));
return false;
} else if (newRange == island.getProtectionRange()) {

View File

@ -11,6 +11,7 @@ import org.bukkit.Particle;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
/**
* @author Poslovitch
@ -84,15 +85,15 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
getIslands().getIslandAt(user.getLocation()).ifPresent(island -> {
// Draw the island protected area
drawZone(user, Particle.BARRIER, null, island.getCenter(), island.getProtectionRange());
drawZone(user, Particle.BARRIER, null, island, island.getProtectionRange());
// Draw the default protected area if island protected zone is different
if (island.getProtectionRange() != getPlugin().getIWM().getIslandProtectionRange(getWorld())) {
drawZone(user, Particle.VILLAGER_HAPPY, null, island.getCenter(), getPlugin().getIWM().getIslandProtectionRange(getWorld()));
drawZone(user, Particle.VILLAGER_HAPPY, null, island, getPlugin().getIWM().getIslandProtectionRange(getWorld()));
}
// Draw the island area
drawZone(user, Particle.REDSTONE, new Particle.DustOptions(Color.GRAY, 1.0F), island.getCenter(), island.getRange());
drawZone(user, Particle.REDSTONE, new Particle.DustOptions(Color.GRAY, 1.0F), island, island.getRange());
});
}, 20, 30));
}
@ -103,7 +104,8 @@ public class AdminRangeDisplayCommand extends CompositeCommand {
displayRanges.remove(user);
}
private void drawZone(User user, Particle particle, Particle.DustOptions dustOptions, Location center, int range) {
private void drawZone(User user, Particle particle, Particle.DustOptions dustOptions, Island island, int range) {
Location center = island.getProtectionCenter();
// Get player Y coordinate
int playerY = user.getPlayer().getLocation().getBlockY() + 1;

View File

@ -55,12 +55,12 @@ public class AdminRangeSetCommand extends CompositeCommand {
Island island = getIslands().getIsland(getWorld(), targetUUID);
// Do some sanity checks to make sure the new protection range won't cause problems
if (range <= 1) {
if (range < 1) {
user.sendMessage("commands.admin.range.invalid-value.too-low", TextVariables.NUMBER, args.get(1));
return false;
}
if (range > island.getRange()) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(island.getRange()));
if (range > island.getRange() * 2) {
user.sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, String.valueOf(2 * island.getRange()));
return false;
}
if (range == island.getProtectionRange()) {

View File

@ -29,4 +29,9 @@ public class TextVariables {
* @since 1.15.0
*/
public static final String GAMEMODE = "[gamemode]";
/**
* Used for coordinates
* @since 1.16.0
*/
public static final String XYZ = "[xyz]";
}

View File

@ -132,7 +132,7 @@ public class BlueprintPaster {
// Offset due to bedrock
Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0);
// Calculate location for pasting
this.location = island.getCenter().toVector().subtract(off).toLocation(world);
this.location = island.getProtectionCenter().toVector().subtract(off).toLocation(world);
}
/**

View File

@ -1,5 +1,6 @@
package world.bentobox.bentobox.database.objects;
import java.io.IOException;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
@ -73,11 +74,19 @@ public class Island implements DataObject, MetaDataAble {
private String uniqueId = UUID.randomUUID().toString();
//// Island ////
// The center of the island itself
// The center of the island space
@Expose
@Nullable
private Location center;
/**
* The center location of the protection area
*/
@Expose
@Nullable
private Location location;
// Island range
@Expose
private int range;
@ -227,6 +236,7 @@ public class Island implements DataObject, MetaDataAble {
this.gameMode = island.getGameMode();
this.history.addAll(island.getHistory());
this.levelHandicap = island.getLevelHandicap();
this.location = island.getProtectionCenter();
this.maxEverProtectionRange = island.getMaxEverProtectionRange();
this.members.putAll(island.getMembers());
island.getMetaData().ifPresent(m -> {
@ -404,33 +414,41 @@ public class Island implements DataObject, MetaDataAble {
}
/**
* Get the minimum protected X block coord based on the island location.
* It will never be less than {@link #getMinX()}
* @return the minProtectedX
*/
public int getMinProtectedX() {
return center.getBlockX() - protectionRange;
return getProtectionCenter() == null ? 0 : Math.max(getMinX(), getProtectionCenter().getBlockX() - protectionRange);
}
/**
* Get the maximum protected X block coord based on the island location.
* It will never be more than {@link #getMaxX()}
* @return the maxProtectedX
* @since 1.5.2
*/
public int getMaxProtectedX() {
return center.getBlockX() + protectionRange;
return getProtectionCenter() == null ? 0 : Math.min(getMaxX(), getProtectionCenter().getBlockX() + protectionRange);
}
/**
* Get the minimum protected Z block coord based on the island location.
* It will never be less than {@link #getMinZ()}
* @return the minProtectedZ
*/
public int getMinProtectedZ() {
return center.getBlockZ() - protectionRange;
return getProtectionCenter() == null ? 0 : Math.max(getMinZ(), getProtectionCenter().getBlockZ() - protectionRange);
}
/**
* Get the maximum protected Z block coord based on the island location.
* It will never be more than {@link #getMinZ()}
* @return the maxProtectedZ
* @since 1.5.2
*/
public int getMaxProtectedZ() {
return center.getBlockZ() + protectionRange;
return getProtectionCenter() == null ? 0 : Math.min(getMaxZ(), getProtectionCenter().getBlockZ() + protectionRange);
}
/**
@ -760,7 +778,7 @@ public class Island implements DataObject, MetaDataAble {
/**
* @param center the center to set
*/
public void setCenter(Location center) {
public void setCenter(@Nullable Location center) {
if (center != null) {
this.world = center.getWorld();
}
@ -865,13 +883,35 @@ public class Island implements DataObject, MetaDataAble {
*/
public void setProtectionRange(int protectionRange) {
this.protectionRange = protectionRange;
// Ratchet up the maximum protection range
if (protectionRange > this.maxEverProtectionRange) {
this.maxEverProtectionRange = protectionRange;
}
this.updateMaxEverProtectionRange();
setChanged();
}
/**
* Updates the maxEverProtectionRange based on the current protectionRange
*/
public void updateMaxEverProtectionRange() {
// Ratchet up the maximum protection range
// Distance from maxes
int diffMinX = Math.abs(getCenter().getBlockX() - this.getMinProtectedX());
int diffMaxX = Math.abs(getCenter().getBlockX() - this.getMaxProtectedX());
int diffMinZ = Math.abs(getCenter().getBlockZ() - this.getMinProtectedZ());
int diffMaxZ = Math.abs(getCenter().getBlockZ() - this.getMaxProtectedZ());
if (diffMinX > this.maxEverProtectionRange) {
this.maxEverProtectionRange = diffMinX;
}
if (diffMaxX > this.maxEverProtectionRange) {
this.maxEverProtectionRange = diffMaxX;
}
if (diffMinZ > this.maxEverProtectionRange) {
this.maxEverProtectionRange = diffMinZ;
}
if (diffMaxZ > this.maxEverProtectionRange) {
this.maxEverProtectionRange = diffMaxZ;
}
}
/**
* @param purgeProtected - if the island is protected from the Purge
*/
@ -1017,16 +1057,13 @@ public class Island implements DataObject, MetaDataAble {
// Show team members
showMembers(user);
}
Vector location = getCenter().toVector();
user.sendMessage("commands.admin.info.island-location", "[xyz]", Util.xyz(location));
Vector from = getCenter().toVector().subtract(new Vector(getRange(), 0, getRange())).setY(0);
Vector to = getCenter().toVector().add(new Vector(getRange()-1, 0, getRange()-1)).setY(getCenter().getWorld().getMaxHeight());
user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(from), "[xz2]", Util.xyz(to));
Vector location = getProtectionCenter().toVector();
user.sendMessage("commands.admin.info.island-protection-center", TextVariables.XYZ, Util.xyz(location));
user.sendMessage("commands.admin.info.island-center", TextVariables.XYZ, Util.xyz(getCenter().toVector()));
user.sendMessage("commands.admin.info.island-coords", "[xz1]", Util.xyz(new Vector(this.getMinX(), 0, getMinZ())), "[xz2]", Util.xyz(new Vector(this.getMaxX(), 0, getMaxZ())));
user.sendMessage("commands.admin.info.protection-range", "[range]", String.valueOf(getProtectionRange()));
user.sendMessage("commands.admin.info.max-protection-range", "[range]", String.valueOf(getMaxEverProtectionRange()));
Vector pfrom = getCenter().toVector().subtract(new Vector(getProtectionRange(), 0, getProtectionRange())).setY(0);
Vector pto = getCenter().toVector().add(new Vector(getProtectionRange()-1, 0, getProtectionRange()-1)).setY(getCenter().getWorld().getMaxHeight());
user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(pfrom), "[xz2]", Util.xyz(pto));
user.sendMessage("commands.admin.info.protection-coords", "[xz1]", Util.xyz(new Vector(this.getMinProtectedX(), 0, getMinProtectedZ())), "[xz2]", Util.xyz(new Vector(this.getMaxProtectedX(), 0, getMaxProtectedZ())));
if (spawn) {
user.sendMessage("commands.admin.info.is-spawn");
}
@ -1347,10 +1384,39 @@ public class Island implements DataObject, MetaDataAble {
this.changed = changed;
}
/**
* Get the center location of the protection zone.
* This can be anywhere within the island space and can move.
* Unless explicitly set, it will return the same as {@link #getCenter()}.
* @return a clone of the protection center location
* @since 1.16.0
*/
@Nullable
public Location getProtectionCenter() {
return location == null ? getCenter() : location.clone();
}
/**
* Sets the protection center location of the island within the island space.
* @param location the location to set
* @throws IOException if the location is not in island space
* @since 1.16.0
*/
public void setProtectionCenter(Location location) throws IOException {
if (!this.inIslandSpace(location)) {
throw new IOException("Location must be in island space");
}
this.location = location;
this.updateMaxEverProtectionRange();
setChanged();
}
@Override
public String toString() {
return "Island [deleted=" + deleted + ", " + (uniqueId != null ? "uniqueId=" + uniqueId + ", " : "")
+ (center != null ? "center=" + center + ", " : "") + "range=" + range + ", protectionRange="
return "Island [changed=" + changed + ", deleted=" + deleted + ", "
+ (uniqueId != null ? "uniqueId=" + uniqueId + ", " : "")
+ (center != null ? "center=" + center + ", " : "")
+ (location != null ? "location=" + location + ", " : "") + "range=" + range + ", protectionRange="
+ protectionRange + ", maxEverProtectionRange=" + maxEverProtectionRange + ", "
+ (world != null ? "world=" + world + ", " : "")
+ (gameMode != null ? "gameMode=" + gameMode + ", " : "") + (name != null ? "name=" + name + ", " : "")

View File

@ -9,8 +9,6 @@ import org.bukkit.util.Vector;
import com.google.gson.annotations.Expose;
import world.bentobox.bentobox.BentoBox;
/**
* Data object to store islands in deletion
* @author tastybento
@ -55,9 +53,8 @@ public class IslandDeletion implements DataObject {
public IslandDeletion() {}
public IslandDeletion(Island island) {
// Get the world's island distance
int islandDistance = BentoBox.getInstance().getIWM().getIslandDistance(island.getWorld());
int range = Math.min(island.getMaxEverProtectionRange(), islandDistance);
// Calculate the minimum required range to delete the island
int range = Math.min(island.getMaxEverProtectionRange(), island.getRange());
uniqueId = UUID.randomUUID().toString();
location = island.getCenter();
minX = location.getBlockX() - range;
@ -68,7 +65,7 @@ public class IslandDeletion implements DataObject {
minZChunk = minZ >> 4;
maxZ = range + location.getBlockZ();
maxZChunk = maxZ >> 4;
box = BoundingBox.of(new Vector(minX, 0, minZ), new Vector(maxX, 255, maxZ));
box = island.getBoundingBox();
}
/* (non-Javadoc)

View File

@ -205,8 +205,8 @@ public class JoinLeaveListener implements Listener {
if (island != null) {
// Check if new owner has a different range permission than the island size
int range = user.getPermissionValue(plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("") + "island.range", island.getProtectionRange());
// Range cannot be greater than the island distance
range = Math.min(range, plugin.getIWM().getIslandDistance(island.getWorld()));
// Range cannot be greater than the island distance * 2
range = Math.min(range, 2 * plugin.getIWM().getIslandDistance(island.getWorld()));
// Range can go up or down
if (range != island.getProtectionRange()) {
user.sendMessage("commands.admin.setrange.range-updated", TextVariables.NUMBER, String.valueOf(range));
@ -218,10 +218,10 @@ public class JoinLeaveListener implements Listener {
island.setProtectionRange(range);
// Call Protection Range Change event. Does not support cancelling.
// Call Protection Range Change event. Does not support canceling.
IslandEvent.builder()
.island(island)
.location(island.getCenter())
.location(island.getProtectionCenter())
.reason(IslandEvent.Reason.RANGE_CHANGE)
.involvedPlayer(user.getUniqueId())
.admin(true)

View File

@ -118,7 +118,7 @@ public class InvincibleVisitorsListener extends FlagListener implements ClickHan
// Teleport
new SafeSpotTeleport.Builder(getPlugin())
.entity(p)
.location(island.getCenter().toVector().toLocation(p.getWorld()))
.location(island.getProtectionCenter().toVector().toLocation(p.getWorld()))
.build());
} else if (getIslands().hasIsland(p.getWorld(), p.getUniqueId())) {
// No island in this location - if the player has an island try to teleport them back

View File

@ -86,6 +86,26 @@ public enum GameModePlaceholder {
* @since 1.5.0
*/
ISLAND_CENTER_Z("island_center_z", (addon, user, island) -> island == null ? "" : String.valueOf(island.getCenter().getBlockZ())),
/**
* Returns the coordinates of the island's location, which may be different to the center.
* @since 1.16.0
*/
ISLAND_LOCATION("island_location", (addon, user, island) -> island == null ? "" : Util.xyz(island.getProtectionCenter().toVector())),
/**
* Returns the X coordinate of the island's location.
* @since 1.16.0
*/
ISLAND_LOCATION_X("island_location_x", (addon, user, island) -> island == null ? "" : String.valueOf(island.getProtectionCenter().getBlockX())),
/**
* Returns the Y coordinate of the island's location.
* @since 1.16.0
*/
ISLAND_LOCATION_Y("island_location_y", (addon, user, island) -> island == null ? "" : String.valueOf(island.getProtectionCenter().getBlockY())),
/**
* Returns the Z coordinate of the island's location.
* @since 1.16.0
*/
ISLAND_LOCATION_Z("island_location_z", (addon, user, island) -> island == null ? "" : String.valueOf(island.getProtectionCenter().getBlockZ())),
/**
* Returns the maximum number of members the island can have
* @since 1.5.0
@ -185,6 +205,30 @@ public enum GameModePlaceholder {
*/
VISITED_ISLAND_CENTER_Z("visited_island_center_z", (addon, user, island) ->
getVisitedIsland(addon, user).map(value -> String.valueOf(value.getCenter().getBlockZ())).orElse("")),
/**
* Returns the coordinates of the location of the island the player is standing on.
* @since 1.16.0
*/
VISITED_ISLAND_LOCATION("visited_island_location", (addon, user, island) ->
getVisitedIsland(addon, user).map(value -> Util.xyz(value.getProtectionCenter().toVector())).orElse("")),
/**
* Returns the X coordinate of the location of the island the player is standing on.
* @since 1.16.0
*/
VISITED_ISLAND_LOCATION_X("visited_island_location_x", (addon, user, island) ->
getVisitedIsland(addon, user).map(value -> String.valueOf(value.getProtectionCenter().getBlockX())).orElse("")),
/**
* Returns the Y coordinate of the location of the island the player is standing on.
* @since 1.16.0
*/
VISITED_ISLAND_LOCATION_Y("visited_island_location_y", (addon, user, island) ->
getVisitedIsland(addon, user).map(value -> String.valueOf(value.getProtectionCenter().getBlockY())).orElse("")),
/**
* Returns the Z coordinate of the location of the island the player is standing on.
* @since 1.16.0
*/
VISITED_ISLAND_LOCATION_Z("visited_island_location_z", (addon, user, island) ->
getVisitedIsland(addon, user).map(value -> String.valueOf(value.getProtectionCenter().getBlockZ())).orElse("")),
/**
* Returns the maximum number of members the island the player is standing on can have.
* @since 1.5.2

View File

@ -1370,7 +1370,7 @@ public class IslandsManager {
int oldRange = island.getProtectionRange();
island.setProtectionRange(range);
// Call Protection Range Change event. Does not support cancelling.
// Call Protection Range Change event. Does not support canceling.
IslandEvent.builder()
.island(island)
.location(island.getCenter())
@ -1625,7 +1625,7 @@ public class IslandsManager {
highestIsland = i;
}
String xyz = Util.xyz(i.getCenter().toVector());
user.sendMessage("commands.admin.team.fix.rank-on-island", TextVariables.RANK, user.getTranslation(rank), "[xyz]", xyz);
user.sendMessage("commands.admin.team.fix.rank-on-island", TextVariables.RANK, user.getTranslation(rank), TextVariables.XYZ, xyz);
user.sendRawMessage(i.getUniqueId());
}
// Fix island ownership in cache

View File

@ -61,29 +61,33 @@ public class DeleteIslandChunks {
if (inDelete) return;
inDelete = true;
for (int i = 0; i < plugin.getSettings().getDeleteSpeed(); i++) {
boolean last = i == plugin.getSettings().getDeleteSpeed() -1;
plugin.getIWM().getAddon(di.getWorld()).ifPresent(gm ->
// Overworld
processChunk(gm, Environment.NORMAL, chunkX, chunkZ).thenRun(() ->
// Nether
processChunk(gm, Environment.NETHER, chunkX, chunkZ).thenRun(() ->
// End
processChunk(gm, Environment.THE_END, chunkX, chunkZ).thenRun(() -> finish()))));
processChunk(gm, Environment.THE_END, chunkX, chunkZ).thenRun(() -> finish(last)))));
chunkZ++;
if (chunkZ > di.getMaxZChunk()) {
chunkZ = di.getMinZChunk();
chunkX++;
}
}
}, 0L, 20L);
}
private void finish() {
chunkZ++;
if (chunkZ > di.getMaxZChunk()) {
chunkZ = di.getMinZChunk();
chunkX++;
if (chunkX > di.getMaxXChunk()) {
// We're done
task.cancel();
// Fire event
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
}
private void finish(boolean last) {
if (chunkX > di.getMaxXChunk()) {
// Fire event
IslandEvent.builder().deletedIslandInfo(di).reason(Reason.DELETED).build();
// We're done
task.cancel();
}
if (last) {
inDelete = false;
}
}
@ -110,29 +114,28 @@ public class DeleteIslandChunks {
break;
}
if (PaperLib.isChunkGenerated(world, x, z)) {
PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk ->regenerateChunk(gm, chunk));
return CompletableFuture.completedFuture(true);
CompletableFuture<Boolean> r = new CompletableFuture<>();
PaperLib.getChunkAtAsync(world, x, z).thenAccept(chunk -> regenerateChunk(r, gm, chunk));
return r;
}
return CompletableFuture.completedFuture(false);
}
private void regenerateChunk(GameModeAddon gm, Chunk chunk) {
private void regenerateChunk(CompletableFuture<Boolean> r, GameModeAddon gm, Chunk chunk) {
// Clear all inventories
Arrays.stream(chunk.getTileEntities()).filter(te -> (te instanceof InventoryHolder))
.filter(te -> di.inBounds(te.getLocation().getBlockX(), te.getLocation().getBlockZ()))
.forEach(te -> ((InventoryHolder)te).getInventory().clear());
// Reset blocks
MyBiomeGrid grid = new MyBiomeGrid(chunk.getWorld().getEnvironment());
ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "");
ChunkGenerator cg = gm.getDefaultWorldGenerator(chunk.getWorld().getName(), "delete");
// Will be null if use-own-generator is set to true
if (cg != null) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
Bukkit.getScheduler().runTask(plugin, () -> createChunk(cd, chunk, grid));
});
}
ChunkData cd = cg.generateChunkData(chunk.getWorld(), new Random(), chunk.getX(), chunk.getZ(), grid);
createChunk(cd, chunk, grid);
}
r.complete(true);
}
private void createChunk(ChunkData cd, Chunk chunk, MyBiomeGrid grid) {
@ -153,6 +156,5 @@ public class DeleteIslandChunks {
}
// Remove all entities in chunk, including any dropped items as a result of clearing the blocks above
Arrays.stream(chunk.getEntities()).filter(e -> !(e instanceof Player) && di.inBounds(e.getLocation().getBlockX(), e.getLocation().getBlockZ())).forEach(Entity::remove);
inDelete = false;
}
}

View File

@ -319,7 +319,7 @@ public class SafeSpotTeleport {
* @return Builder
*/
public Builder island(Island island) {
this.location = island.getCenter();
this.location = island.getProtectionCenter();
return this;
}

View File

@ -193,7 +193,8 @@ commands:
team-members-title: "Team members:"
team-owner-format: "&a [name] [rank]"
team-member-format: "&b [name] [rank]"
island-location: "Island location: [xyz]"
island-protection-center: "Protection area center: [xyz]"
island-center: "Island center: [xyz]"
island-coords: "Island coordinates: [xz1] to [xz2]"
islands-in-trash: "&d Player has islands in trash."
protection-range: "Protection range: [range]"
@ -250,6 +251,15 @@ commands:
unknown-rank: "&c Unknown rank!"
not-possible: "&c Rank must be higher than visitor."
rank-set: "&a Rank set from &b [from] &a to &b [to] &a on &b [name]&a 's island."
setprotectionlocation:
parameters: "[x y z coords]"
description: "set current location or [x y z] as center of island's protection area"
island: "&c This will affect the island at [xyz] owned by '[name]'."
confirmation: "&c Are you sure you want to set [xyz] as the protection center?"
success: "&a Successfully set [xyz] as the protection center."
fail: "&a Successfully set [xyz] as the protection center."
island-location-changed: "&a [user] changed island's protection center to [xyz]."
xyz-error: "&c Specify three integer coordinates: e.g, 100 120 100"
setspawn:
description: "set an island as spawn for this gamemode"
already-spawn: "&c This island is already a spawn!"

View File

@ -12,7 +12,7 @@ load: STARTUP
loadbefore: [Multiverse-Core, Residence]
softdepend: [Vault, PlaceholderAPI, dynmap, WorldEdit, WorldBorderAPI, BsbMongo]
softdepend: [Vault, PlaceholderAPI, dynmap, WorldEdit, WorldBorderAPI, BsbMongo, WorldGeneratorApi]
permissions:
bentobox.admin:

View File

@ -7,6 +7,7 @@ import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -35,7 +36,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@ -83,14 +83,15 @@ public class TestBentoBox extends AbstractCommonSetup {
when(Bukkit.getOfflinePlayer(any(UUID.class))).thenReturn(offlinePlayer);
when(offlinePlayer.getName()).thenReturn("tastybento");
Mockito.when(player.hasPermission(Mockito.anyString())).thenReturn(true);
when(player.hasPermission(anyString())).thenReturn(true);
Mockito.when(ownerOfIsland.getLocation()).thenReturn(location);
Mockito.when(visitorToIsland.getLocation()).thenReturn(location);
when(ownerOfIsland.getLocation()).thenReturn(location);
when(visitorToIsland.getLocation()).thenReturn(location);
when(location.clone()).thenReturn(location);
Mockito.when(player.getUniqueId()).thenReturn(MEMBER_UUID);
Mockito.when(ownerOfIsland.getUniqueId()).thenReturn(uuid);
Mockito.when(visitorToIsland.getUniqueId()).thenReturn(VISITOR_UUID);
when(player.getUniqueId()).thenReturn(MEMBER_UUID);
when(ownerOfIsland.getUniqueId()).thenReturn(uuid);
when(visitorToIsland.getUniqueId()).thenReturn(VISITOR_UUID);
island.setOwner(uuid);
island.setProtectionRange(100);

View File

@ -33,6 +33,7 @@ import org.powermock.reflect.Whitebox;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.CommandsManager;
@ -252,7 +253,7 @@ public class AdminRegisterCommandTest {
AdminRegisterCommand itl = new AdminRegisterCommand(ac);
assertTrue(itl.execute(user, itl.getLabel(), Collections.singletonList("tastybento")));
// Add other verifications
verify(user).sendMessage(eq("commands.admin.register.registered-island"), eq("[xyz]"), eq("123,123,432"), eq("[name]"), eq("tastybento"));
verify(user).sendMessage(eq("commands.admin.register.registered-island"), eq(TextVariables.XYZ), eq("123,123,432"), eq("[name]"), eq("tastybento"));
verify(user).sendMessage(eq("general.success"));
}

View File

@ -226,7 +226,7 @@ public class AdminUnregisterCommandTest {
AdminUnregisterCommand itl = new AdminUnregisterCommand(ac);
UUID targetUUID = UUID.randomUUID();
itl.unregisterPlayer(user, "name", targetUUID);
verify(user).sendMessage("commands.admin.unregister.unregistered-island", "[xyz]", "1,2,3", TextVariables.NAME, "name");
verify(user).sendMessage("commands.admin.unregister.unregistered-island", TextVariables.XYZ, "1,2,3", TextVariables.NAME, "name");
assertTrue(map.isEmpty());
verify(im).removePlayer(any(), eq(uuid1));
verify(im).removePlayer(any(), eq(uuid2));

View File

@ -214,9 +214,9 @@ public class AdminRangeSetCommandTest {
AdminRangeSetCommand arc = new AdminRangeSetCommand(ac);
List<String> args = new ArrayList<>();
args.add("tastybento");
args.add("100");
args.add("1000");
arc.execute(user, "", args);
Mockito.verify(user).sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, "50");
Mockito.verify(user).sendMessage("commands.admin.range.invalid-value.too-high", TextVariables.NUMBER, "100");
}
/**

View File

@ -3,6 +3,7 @@ package world.bentobox.bentobox.database.objects;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ -66,7 +67,6 @@ public class IslandDeletionTest {
when(iwm.getWorldSettings(any())).thenReturn(ws);
// Island
when(island.getMaxEverProtectionRange()).thenReturn(1000);
when(island.getWorld()).thenReturn(world);
when(island.getCenter()).thenReturn(location);
@ -112,7 +112,7 @@ public class IslandDeletionTest {
*/
@Test
public void testGetMaxXChunk() {
assertEquals(84, id.getMaxXChunk());
assertEquals(77, id.getMaxXChunk());
}
/**
@ -120,7 +120,7 @@ public class IslandDeletionTest {
*/
@Test
public void testGetMaxZChunk() {
assertEquals(-322, id.getMaxZChunk());
assertEquals(-328, id.getMaxZChunk());
}
/**
@ -128,7 +128,7 @@ public class IslandDeletionTest {
*/
@Test
public void testGetMinXChunk() {
assertEquals(71, id.getMinXChunk());
assertEquals(77, id.getMinXChunk());
}
/**
@ -136,7 +136,7 @@ public class IslandDeletionTest {
*/
@Test
public void testGetMinZChunk() {
assertEquals(-335, id.getMinZChunk());
assertEquals(-328, id.getMinZChunk());
}
/**
@ -156,16 +156,22 @@ public class IslandDeletionTest {
assertEquals(world, id.getWorld());
}
/**
* Test method for {@link world.bentobox.bentobox.database.objects.IslandDeletion#getBox()}.
*/
@Test
public void testBox() {
assertNull(id.getBox());
}
/**
* Test method for {@link world.bentobox.bentobox.database.objects.IslandDeletion#toString()}.
*/
@Test
public void testToString() {
assertTrue(id.toString().endsWith(
", minXChunk=71,"
+ " maxXChunk=84, minZChunk=-335, maxZChunk=-322, minX=1145, minZ=-5345,"
+ " maxX=1345, maxZ=-5145, box=BoundingBox [minX=1145.0, minY=0.0, minZ=-5345.0,"
+ " maxX=1345.0, maxY=255.0, maxZ=-5145.0]]"));
"minXChunk=77, maxXChunk=77, minZChunk=-328,"
+ " maxZChunk=-328, minX=1245, minZ=-5245, maxX=1245, maxZ=-5245, box=null]"));
}
}

View File

@ -32,6 +32,7 @@ import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@ -140,6 +141,7 @@ public class YamlDatabaseHandlerTest {
/**
* Test method for {@link world.bentobox.bentobox.database.yaml.YamlDatabaseHandler#loadObjects()}.
*/
@Ignore("YAML database is no longer supported")
@Test
public void testLoadObjects() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException, IntrospectionException {
List<Island> list = handler.loadObjects();
@ -160,6 +162,7 @@ public class YamlDatabaseHandlerTest {
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Ignore("YAML database is no longer supported")
@Test
public void testLoadObject() throws InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, NoSuchMethodException, IntrospectionException {
String name = UUID.randomUUID().toString();
@ -176,6 +179,7 @@ public class YamlDatabaseHandlerTest {
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Ignore("YAML database is no longer supported")
@SuppressWarnings("unchecked")
@Test
public void testSaveObject() throws IllegalAccessException, InvocationTargetException, IntrospectionException {

View File

@ -245,9 +245,9 @@ public class JoinLeaveListenerTest {
// Verify
verify(player).sendMessage(eq("commands.admin.setrange.range-updated"));
// Verify island setting
verify(island).setProtectionRange(eq(100));
verify(island).setProtectionRange(eq(200));
// Verify log
verify(plugin).log("Island protection range changed from 50 to 100 for tastybento due to permission.");
verify(plugin).log("Island protection range changed from 50 to 200 for tastybento due to permission.");
}
/**

View File

@ -128,7 +128,7 @@ public class InvincibleVisitorsListenerTest {
Location location = mock(Location.class);
Vector vector = mock(Vector.class);
when(location.toVector()).thenReturn(vector);
when(island.getCenter()).thenReturn(location);
when(island.getProtectionCenter()).thenReturn(location);
when(im.getIsland(any(World.class), any(User.class))).thenReturn(island);
optionalIsland = Optional.of(island);
// Visitor
@ -255,7 +255,7 @@ public class InvincibleVisitorsListenerTest {
assertTrue(e.isCancelled());
verify(player, never()).setGameMode(eq(GameMode.SPECTATOR));
}
@Test
public void testOnVisitorGetDamageNPC() {
when(player.hasMetadata(eq("NPC"))).thenReturn(true);

View File

@ -94,7 +94,7 @@ public class SafeSpotTeleportBuilderTest {
sstb.entity(player);
// Add island
Island island = mock(Island.class);
when(island.getCenter()).thenReturn(loc);
when(island.getProtectionCenter()).thenReturn(loc);
sstb.island(island);
// Build - expect success
SafeSpotTeleport result = sstb.build();