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(); } }