328 lines
12 KiB
Java
328 lines
12 KiB
Java
package world.bentobox.bentobox.managers.island;
|
|
|
|
import java.io.IOException;
|
|
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.World;
|
|
import org.bukkit.World.Environment;
|
|
import org.bukkit.util.Vector;
|
|
|
|
import world.bentobox.bentobox.BStats;
|
|
import world.bentobox.bentobox.BentoBox;
|
|
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
|
import world.bentobox.bentobox.api.events.IslandBaseEvent;
|
|
import world.bentobox.bentobox.api.events.island.IslandCreateEvent;
|
|
import world.bentobox.bentobox.api.events.island.IslandEvent;
|
|
import world.bentobox.bentobox.api.events.island.IslandEvent.Reason;
|
|
import world.bentobox.bentobox.api.events.island.IslandResetEvent;
|
|
import world.bentobox.bentobox.api.user.User;
|
|
import world.bentobox.bentobox.database.objects.Island;
|
|
import world.bentobox.bentobox.managers.BlueprintsManager;
|
|
import world.bentobox.bentobox.managers.IslandsManager;
|
|
|
|
/**
|
|
* Create and paste a new island
|
|
*
|
|
* @author tastybento
|
|
*
|
|
*/
|
|
public class NewIsland {
|
|
private final BentoBox plugin;
|
|
private Island island;
|
|
private final User user;
|
|
private final Reason reason;
|
|
private final World world;
|
|
private String name;
|
|
private final boolean noPaste;
|
|
private final GameModeAddon addon;
|
|
|
|
private NewIslandLocationStrategy locationStrategy;
|
|
|
|
public NewIsland(Builder builder) throws IOException {
|
|
plugin = BentoBox.getInstance();
|
|
this.user = builder.user2;
|
|
this.reason = builder.reason2;
|
|
this.world = builder.world2;
|
|
this.name = builder.name2;
|
|
this.noPaste = builder.noPaste2;
|
|
this.addon = builder.addon2;
|
|
this.locationStrategy = builder.locationStrategy2;
|
|
|
|
if (this.locationStrategy == null) {
|
|
this.locationStrategy = new DefaultNewIslandLocationStrategy();
|
|
}
|
|
// Fire pre-create event
|
|
IslandBaseEvent event = IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECREATE)
|
|
.build();
|
|
if (event.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(event.isCancelled())) {
|
|
// Do nothing
|
|
return;
|
|
}
|
|
newIsland(builder.oldIsland2);
|
|
}
|
|
|
|
/**
|
|
* @return the island that was created
|
|
*/
|
|
public Island getIsland() {
|
|
return island;
|
|
}
|
|
|
|
/**
|
|
* Start building a new island
|
|
*
|
|
* @return New island builder object
|
|
*/
|
|
public static Builder builder() {
|
|
return new Builder();
|
|
}
|
|
|
|
/**
|
|
* Build a new island for a player
|
|
*
|
|
* @author tastybento
|
|
*/
|
|
public static class Builder {
|
|
private Island oldIsland2;
|
|
private User user2;
|
|
private Reason reason2;
|
|
private World world2;
|
|
private String name2 = BlueprintsManager.DEFAULT_BUNDLE_NAME;
|
|
private boolean noPaste2;
|
|
private GameModeAddon addon2;
|
|
private NewIslandLocationStrategy locationStrategy2;
|
|
|
|
public Builder oldIsland(Island oldIsland) {
|
|
this.oldIsland2 = oldIsland;
|
|
this.world2 = oldIsland.getWorld();
|
|
return this;
|
|
}
|
|
|
|
public Builder player(User player) {
|
|
this.user2 = player;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Sets the reason
|
|
*
|
|
* @param reason reason, can only be {@link Reason#CREATE} or
|
|
* {@link Reason#RESET}.
|
|
*/
|
|
public Builder reason(Reason reason) {
|
|
if (!reason.equals(Reason.CREATE) && !reason.equals(Reason.RESET)) {
|
|
throw new IllegalArgumentException("Reason must be CREATE or RESET.");
|
|
}
|
|
this.reason2 = reason;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set the addon
|
|
*
|
|
* @param addon a game mode addon
|
|
*/
|
|
public Builder addon(GameModeAddon addon) {
|
|
this.addon2 = addon;
|
|
this.world2 = addon.getOverWorld();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* No blocks will be pasted
|
|
*/
|
|
public Builder noPaste() {
|
|
this.noPaste2 = true;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @param name - name of Blueprint bundle
|
|
*/
|
|
public Builder name(String name) {
|
|
this.name2 = name;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @param strategy - the location strategy to use
|
|
* @since 1.8.0
|
|
*/
|
|
public Builder locationStrategy(NewIslandLocationStrategy strategy) {
|
|
this.locationStrategy2 = strategy;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* @return Island
|
|
* @throws IOException - if there are insufficient parameters, i.e., no user
|
|
*/
|
|
public Island build() throws IOException {
|
|
if (user2 != null) {
|
|
NewIsland newIsland = new NewIsland(this);
|
|
return newIsland.getIsland();
|
|
}
|
|
throw new IOException("Insufficient parameters. Must have a user!");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes an island.
|
|
*
|
|
* @param oldIsland old island that is being replaced, if any
|
|
* @throws IOException - if an island cannot be made. Message is the tag to show
|
|
* the user.
|
|
*/
|
|
public void newIsland(Island oldIsland) throws IOException {
|
|
// Find the new island location
|
|
Location next = checkReservedIsland();
|
|
if (next == null) {
|
|
next = this.makeNextIsland();
|
|
}
|
|
// Clean up the user
|
|
cleanUpUser(next);
|
|
// Fire event
|
|
IslandBaseEvent event = IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(reason).island(island)
|
|
.location(island.getCenter())
|
|
.blueprintBundle(plugin.getBlueprintsManager().getBlueprintBundles(addon).get(name))
|
|
.oldIsland(oldIsland).build();
|
|
if (event.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(event.isCancelled())) {
|
|
// Do nothing
|
|
return;
|
|
}
|
|
event = event.getNewEvent().orElse(event);
|
|
// Get the new BlueprintBundle if it was changed
|
|
switch (reason) {
|
|
case CREATE -> name = ((IslandCreateEvent) event).getBlueprintBundle().getUniqueId();
|
|
case RESET -> name = ((IslandResetEvent) event).getBlueprintBundle().getUniqueId();
|
|
default -> {
|
|
// Do nothing of other cases
|
|
}
|
|
}
|
|
// Set the player's primary island
|
|
plugin.getIslands().setPrimaryIsland(user.getUniqueId(), island);
|
|
// Run task to run after creating the island in one tick if island is not being
|
|
// pasted
|
|
if (noPaste) {
|
|
Bukkit.getScheduler().runTask(plugin, () -> postCreationTask(oldIsland));
|
|
} else {
|
|
// Find out how far away the player is from the new island
|
|
boolean useNMS = !user.getWorld().equals(island.getWorld())
|
|
|| (user.getLocation().distance(island.getCenter()) >= Bukkit.getViewDistance() * 16D);
|
|
// Create islands, then run task
|
|
plugin.getBlueprintsManager().paste(addon, island, name, () -> postCreationTask(oldIsland), useNMS);
|
|
}
|
|
// Set default settings
|
|
island.setFlagsDefaults();
|
|
// Register metrics
|
|
plugin.getMetrics().ifPresent(BStats::increaseIslandsCreatedCount);
|
|
// Save island
|
|
IslandsManager.updateIsland(island);
|
|
}
|
|
|
|
/**
|
|
* Tasks to run after the new island has been created
|
|
*
|
|
* @param oldIsland - old island that will be deleted
|
|
*/
|
|
private void postCreationTask(Island oldIsland) {
|
|
// Set initial spawn point if one exists
|
|
if (island.getSpawnPoint(Environment.NORMAL) != null) {
|
|
plugin.getIslands().setHomeLocation(user, island.getSpawnPoint(Environment.NORMAL));
|
|
}
|
|
// Stop the player from falling or moving if they are
|
|
if (user.isOnline()) {
|
|
if (reason.equals(Reason.RESET) || (reason.equals(Reason.CREATE)
|
|
&& plugin.getIWM().isTeleportPlayerToIslandUponIslandCreation(world))) {
|
|
user.getPlayer().setVelocity(new Vector(0, 0, 0));
|
|
user.getPlayer().setFallDistance(0F);
|
|
// Teleport player after this island is built
|
|
plugin.getIslands().homeTeleportAsync(world, user.getPlayer(), true).thenRun(() -> tidyUp(oldIsland));
|
|
return;
|
|
} else {
|
|
// let's send him a message so that he knows he can teleport to his island!
|
|
user.sendMessage("commands.island.create.you-can-teleport-to-your-island");
|
|
}
|
|
} else {
|
|
// Remove the player again to completely clear the data
|
|
User.removePlayer(user.getPlayer());
|
|
}
|
|
tidyUp(oldIsland);
|
|
}
|
|
|
|
/**
|
|
* Cleans up a user before moving them to a new island. Resets deaths. Checks
|
|
* range permissions and saves the player to the database.
|
|
*
|
|
* @param loc - the new island location
|
|
*/
|
|
private void cleanUpUser(Location loc) {
|
|
// Reset deaths
|
|
if (plugin.getIWM().isDeathsResetOnNewIsland(world)) {
|
|
plugin.getPlayers().setDeaths(world, user.getUniqueId(), 0);
|
|
}
|
|
// Check if owner has a different range permission than the island size
|
|
island.setProtectionRange(user.getPermissionValue(
|
|
plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("")
|
|
+ "island.range",
|
|
island.getProtectionRange()));
|
|
}
|
|
|
|
/**
|
|
* Get the next island location and add it to the island grid
|
|
*
|
|
* @return location of new island
|
|
* @throws IOException - if there are no unoccupied spots or the island could
|
|
* not be added to the grid
|
|
*/
|
|
private Location makeNextIsland() throws IOException {
|
|
// If the reservation fails, then we need to make a new island anyway
|
|
Location next = this.locationStrategy.getNextLocation(world);
|
|
if (next == null) {
|
|
plugin.logError("Failed to make island - no unoccupied spot found.");
|
|
plugin.logError("If the world was imported, try multiple times until all unowned islands are known.");
|
|
throw new IOException("commands.island.create.cannot-create-island");
|
|
}
|
|
// Add to the grid
|
|
island = plugin.getIslands().createIsland(next, user.getUniqueId());
|
|
if (island == null) {
|
|
plugin.logError("Failed to make island! Island could not be added to the grid.");
|
|
throw new IOException("commands.island.create.unable-create-island");
|
|
}
|
|
return next;
|
|
}
|
|
|
|
/**
|
|
* Get the reserved island location
|
|
*
|
|
* @return reserved island location, or null if none found
|
|
*/
|
|
private Location checkReservedIsland() {
|
|
if (plugin.getIslands().hasIsland(world, user)) {
|
|
// Island exists, it just needs pasting
|
|
island = plugin.getIslands().getIsland(world, user);
|
|
if (island != null && island.isReserved()) {
|
|
Location l = island.getCenter();
|
|
// Clear the reservation
|
|
island.setReserved(false);
|
|
return l;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void tidyUp(Island oldIsland) {
|
|
// Delete old island
|
|
if (oldIsland != null) {
|
|
// Delete the old island
|
|
plugin.getIslands().deleteIsland(oldIsland, true, user.getUniqueId());
|
|
}
|
|
|
|
// Fire exit event
|
|
IslandEvent.builder().involvedPlayer(user.getUniqueId())
|
|
.reason(reason == Reason.RESET ? Reason.RESETTED : Reason.CREATED).island(island)
|
|
.location(island.getCenter()).oldIsland(oldIsland).build();
|
|
|
|
}
|
|
} |