Fix island resetting. #2223

Islands were being deleted in all worlds, and all islands were being
deleted from the player instead of just the one island.
This commit is contained in:
tastybento 2023-11-10 17:06:46 -08:00
parent c63de278fe
commit 19d7e2fe0a
4 changed files with 1209 additions and 1085 deletions

View File

@ -19,7 +19,6 @@ import world.bentobox.bentobox.managers.island.NewIsland.Builder;
import world.bentobox.bentobox.panels.IslandCreationPanel;
import world.bentobox.bentobox.util.Util;
/**
* @author tastybento
*/
@ -33,6 +32,7 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Creates the island reset command
*
* @param islandCommand - parent command
* @param noPaste - true if resetting should not paste a new island
*/
@ -93,7 +93,8 @@ public class IslandResetCommand extends ConfirmableCommand {
} else {
// Show panel after confirmation
if (getPlugin().getSettings().isResetConfirmation()) {
this.askConfirmation(user, user.getTranslation("commands.island.reset.confirmation"), () -> selectBundle(user, label));
this.askConfirmation(user, user.getTranslation("commands.island.reset.confirmation"),
() -> selectBundle(user, label));
} else {
selectBundle(user, label);
}
@ -103,6 +104,7 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Either selects the bundle to use or asks the user to choose.
*
* @since 1.5.1
*/
private void selectBundle(@NonNull User user, @NonNull String label) {
@ -117,6 +119,7 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Reset island
*
* @param user user
* @param name name of Blueprint Bundle
* @return true if successful
@ -129,13 +132,10 @@ public class IslandResetCommand extends ConfirmableCommand {
user.sendMessage("commands.island.create.creating-island");
// Create new island and then delete the old one
try {
Builder builder = NewIsland.builder()
.player(user)
.reason(Reason.RESET)
.addon(getAddon())
.oldIsland(oldIsland)
.name(name);
if (noPaste) builder.noPaste();
Builder builder = NewIsland.builder().player(user).reason(Reason.RESET).addon(getAddon())
.oldIsland(oldIsland).name(name);
if (noPaste)
builder.noPaste();
builder.build();
} catch (IOException e) {
getPlugin().logError("Could not create island for player. " + e.getMessage());
@ -148,13 +148,8 @@ public class IslandResetCommand extends ConfirmableCommand {
private void deleteOldIsland(User user, Island oldIsland) {
// Fire island preclear event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(Reason.PRECLEAR)
.island(oldIsland)
.oldIsland(oldIsland)
.location(oldIsland.getCenter())
.build();
IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECLEAR).island(oldIsland)
.oldIsland(oldIsland).location(oldIsland.getCenter()).build();
// Reset the island
@ -167,33 +162,33 @@ public class IslandResetCommand extends ConfirmableCommand {
/**
* Kicks the members (incl. owner) of the island.
*
* @since 1.7.0
*/
private void kickMembers(Island island) {
/*
* We cannot assume the island owner can run /[cmd] team kick (it might be disabled, or there could be permission restrictions...)
* Therefore, we need to do it manually.
* Plus, a more specific team event (TeamDeleteEvent) is called by this method.
* We cannot assume the island owner can run /[cmd] team kick (it might be
* disabled, or there could be permission restrictions...) Therefore, we need to
* do it manually. Plus, a more specific team event (TeamDeleteEvent) is called
* by this method.
*/
island.getMemberSet().forEach(memberUUID -> {
User member = User.getInstance(memberUUID);
// Send a "you're kicked" message if the member is not the island owner (send before removing!)
// Send a "you're kicked" message if the member is not the island owner (send
// before removing!)
if (!memberUUID.equals(island.getOwner())) {
member.sendMessage("commands.island.reset.kicked-from-island", TextVariables.GAMEMODE, getAddon().getDescription().getName());
member.sendMessage("commands.island.reset.kicked-from-island", TextVariables.GAMEMODE,
getAddon().getDescription().getName());
}
// Remove player
getIslands().removePlayer(getWorld(), memberUUID);
getIslands().removePlayer(island, memberUUID);
// Clean player
getPlayers().cleanLeavingPlayer(getWorld(), member, false, island);
// Fire event
TeamEvent.builder()
.island(island)
.reason(TeamEvent.Reason.DELETE)
.involvedPlayer(memberUUID)
.build();
TeamEvent.builder().island(island).reason(TeamEvent.Reason.DELETE).involvedPlayer(memberUUID).build();
});
}
}

View File

@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@ -21,6 +22,7 @@ import world.bentobox.bentobox.util.Util;
/**
* This class stores the islands in memory
*
* @author tastybento
*/
public class IslandCache {
@ -32,7 +34,8 @@ public class IslandCache {
@NonNull
private final Map<@NonNull String, @NonNull Island> islandsById;
/**
* Every player who is associated with an island is in this map.
* Every player who is associated with an island is in this map. Key is player
* UUID, value is a set of islands
*/
@NonNull
private final Map<@NonNull UUID, Set<Island>> islandsByUUID;
@ -49,15 +52,16 @@ public class IslandCache {
/**
* Adds an island to the grid
*
* @param island island to add, not null
* @return true if successfully added, false if not
*/
public boolean addIsland(@NonNull Island island) {
if (island.getCenter() == null || island.getWorld() == null) {
/* Special handling - return true.
The island will not be quarantined, but just not loaded
This can occur when a gamemode is removed temporarily from the server
TODO: have an option to remove these when the purge command is added
/*
* Special handling - return true. The island will not be quarantined, but just
* not loaded This can occur when a gamemode is removed temporarily from the
* server TODO: have an option to remove these when the purge command is added
*/
return true;
}
@ -76,8 +80,10 @@ public class IslandCache {
/**
* Adds a player's UUID to the look up for islands. Does no checking
*
* @param uuid player's uuid
* @param island island to associate with this uuid. Only one island can be associated per world.
* @param island island to associate with this uuid. Only one island can be
* associated per world.
*/
public void addPlayer(@NonNull UUID uuid, @NonNull Island island) {
islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).add(island);
@ -85,6 +91,7 @@ public class IslandCache {
/**
* Adds an island to the grid register
*
* @param newIsland new island
* @return true if successfully added, false if not
*/
@ -100,6 +107,7 @@ public class IslandCache {
/**
* Deletes an island from the cache. Does not remove blocks.
*
* @param island island to delete
* @return true if successful, false if not
*/
@ -116,12 +124,20 @@ public class IslandCache {
private void removeFromIslandsByUUID(Island island) {
for (Set<Island> set : islandsByUUID.values()) {
set.removeIf(island::equals);
Iterator<Island> is = set.iterator();
while (is.hasNext()) {
Island i = is.next();
if (i.equals(island)) {
is.remove();
}
}
// set.removeIf(island::equals);
}
}
/**
* Delete island from the cache by ID. Does not remove blocks.
*
* @param uniqueId - island unique ID
*/
public void deleteIslandFromCache(@NonNull String uniqueId) {
@ -134,6 +150,7 @@ public class IslandCache {
/**
* Get island based on the exact center location of the island
*
* @param location location to search for
* @return island or null if it does not exist
*/
@ -143,8 +160,9 @@ public class IslandCache {
}
/**
* Returns island referenced by player's UUID.
* Returns the island the player is on now, or their last known island
* Returns island referenced by player's UUID. Returns the island the player is
* on now, or their last known island
*
* @param world world to check. Includes nether and end worlds.
* @param uuid player's UUID
* @return island or null if none
@ -168,6 +186,7 @@ public class IslandCache {
/**
* Returns all the islands referenced by player's UUID.
*
* @param world world to check. Includes nether and end worlds.
* @param uuid player's UUID
* @return list of island or empty list if none
@ -177,11 +196,13 @@ public class IslandCache {
if (w == null) {
return new HashSet<>();
}
return islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).stream().filter(i -> w.equals(i.getWorld())).collect(Collectors.toSet());
return islandsByUUID.computeIfAbsent(uuid, k -> new HashSet<>()).stream().filter(i -> w.equals(i.getWorld()))
.collect(Collectors.toSet());
}
/**
* Sets the current island for the user as their primary island
*
* @param uuid UUID of user
* @param island island to make primary
*/
@ -192,8 +213,8 @@ public class IslandCache {
}
/**
* Returns the island at the location or null if there is none.
* This includes the full island space, not just the protected area
* Returns the island at the location or null if there is none. This includes
* the full island space, not just the protected area
*
* @param location the location
* @return Island object
@ -208,7 +229,9 @@ public class IslandCache {
}
/**
* Returns an <strong>unmodifiable collection</strong> of all the islands (even those who may be unowned).
* Returns an <strong>unmodifiable collection</strong> of all the islands (even
* those who may be unowned).
*
* @return unmodifiable collection containing every island.
*/
@NonNull
@ -217,9 +240,12 @@ public class IslandCache {
}
/**
* Returns an <strong>unmodifiable collection</strong> of all the islands (even those who may be unowned) in the specified world.
* Returns an <strong>unmodifiable collection</strong> of all the islands (even
* those who may be unowned) in the specified world.
*
* @param world World of the gamemode.
* @return unmodifiable collection containing all the islands in the specified world.
* @return unmodifiable collection containing all the islands in the specified
* world.
* @since 1.7.0
*/
@NonNull
@ -235,24 +261,27 @@ public class IslandCache {
/**
* Get the members of the user's team
*
* @param world world to check
* @param uuid uuid of player to check
* @param minimumRank minimum rank requested
* @return set of UUID's of island members. If there are no islands, this set will be empty
* @return set of UUID's of island members. If there are no islands, this set
* will be empty
*/
@NonNull
public Set<UUID> getMembers(@NonNull World world, @NonNull UUID uuid, int minimumRank) {
return getIslands(world, uuid)
.stream()
.flatMap(island -> island.getMemberSet(minimumRank).stream())
return getIslands(world, uuid).stream().flatMap(island -> island.getMemberSet(minimumRank).stream())
.collect(Collectors.toSet());
}
/**
* Get the UUID of the owner of the island of the player, which may be their UUID
* Get the UUID of the owner of the island of the player, which may be their
* UUID
*
* @param world the world to check
* @param uuid the player's UUID
* @return island owner's UUID or null if there is no island owned by the player in this world
* @return island owner's UUID or null if there is no island owned by the player
* in this world
*/
@Nullable
public UUID getOwner(@NonNull World world, @NonNull UUID uuid) {
@ -267,6 +296,7 @@ public class IslandCache {
/**
* Checks is a player has an island and owns it
*
* @param world the world to check
* @param uuid the player
* @return true if player has island and owns it
@ -276,8 +306,10 @@ public class IslandCache {
}
/**
* Removes a player from the cache. If the player has an island, the island owner is removed and membership cleared.
* The island is removed from the islandsByUUID map, but kept in the location map.
* Removes a player from the cache. If the player has an island, the island
* owner is removed and membership cleared. The island is removed from the
* islandsByUUID map, but kept in the location map.
*
* @param world world
* @param uuid player's UUID
* @return list of islands player had or empty if none
@ -289,7 +321,7 @@ public class IslandCache {
return Collections.emptySet(); // Return empty list if no islands map exists for the world
}
islandSet.forEach(island -> {
islandSet.stream().filter(i -> w.equals(i.getWorld())).forEach(island -> {
if (uuid.equals(island.getOwner())) {
island.getMembers().clear();
island.setOwner(null);
@ -305,6 +337,7 @@ public class IslandCache {
/**
* Removes player from island and removes the cache reference
*
* @param island member's island
* @param uuid uuid of member to remove
*/
@ -318,6 +351,7 @@ public class IslandCache {
/**
* Get the number of islands in the cache
*
* @return the number of islands
*/
public int size() {
@ -326,6 +360,7 @@ public class IslandCache {
/**
* Gets the number of islands in the cache for this world
*
* @param world world to get the number of islands in
* @return the number of islands
*/
@ -334,8 +369,8 @@ public class IslandCache {
}
/**
* Sets an island owner.
* Clears out any other owner.
* Sets an island owner. Clears out any other owner.
*
* @param island island
* @param newOwnerUUID new owner
*/
@ -350,6 +385,7 @@ public class IslandCache {
/**
* Get the island by unique id
*
* @param uniqueId unique id of the Island.
* @return island or null if none found
* @since 1.3.0
@ -360,7 +396,9 @@ public class IslandCache {
}
/**
* Removes an island from the cache completely without altering the island object
* Removes an island from the cache completely without altering the island
* object
*
* @param island - island to remove
* @since 1.3.0
*/
@ -380,6 +418,7 @@ public class IslandCache {
/**
* Resets all islands in this game mode to default flag settings
*
* @param world - world
* @since 1.3.0
*/
@ -393,6 +432,7 @@ public class IslandCache {
/**
* Resets a specific flag on all game mode islands in world to default setting
*
* @param world - world
* @param flag - flag to reset
* @since 1.8.0
@ -402,12 +442,14 @@ public class IslandCache {
if (w == null) {
return;
}
int setting = BentoBox.getInstance().getIWM().getDefaultIslandFlags(w).getOrDefault(flag, flag.getDefaultRank());
int setting = BentoBox.getInstance().getIWM().getDefaultIslandFlags(w).getOrDefault(flag,
flag.getDefaultRank());
islandsById.values().stream().filter(i -> i.getWorld().equals(w)).forEach(i -> i.setFlag(flag, setting));
}
/**
* Get all the island ids
*
* @return set of ids
* @since 1.8.0
*/
@ -415,5 +457,4 @@ public class IslandCache {
return islandsById.keySet();
}
}

View File

@ -22,6 +22,7 @@ import world.bentobox.bentobox.managers.BlueprintsManager;
/**
* Create and paste a new island
*
* @author tastybento
*
*/
@ -51,9 +52,7 @@ public class NewIsland {
this.locationStrategy = new DefaultNewIslandLocationStrategy();
}
// Fire pre-create event
IslandBaseEvent event = IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(Reason.PRECREATE)
IslandBaseEvent event = IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(Reason.PRECREATE)
.build();
if (event.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(event.isCancelled())) {
// Do nothing
@ -71,6 +70,7 @@ public class NewIsland {
/**
* Start building a new island
*
* @return New island builder object
*/
public static Builder builder() {
@ -79,6 +79,7 @@ public class NewIsland {
/**
* Build a new island for a player
*
* @author tastybento
*/
public static class Builder {
@ -97,7 +98,6 @@ public class NewIsland {
return this;
}
public Builder player(User player) {
this.user2 = player;
return this;
@ -105,7 +105,9 @@ public class NewIsland {
/**
* Sets the reason
* @param reason reason, can only be {@link Reason#CREATE} or {@link Reason#RESET}.
*
* @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)) {
@ -117,6 +119,7 @@ public class NewIsland {
/**
* Set the addon
*
* @param addon a game mode addon
*/
public Builder addon(GameModeAddon addon) {
@ -165,8 +168,10 @@ public class NewIsland {
/**
* 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.
* @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
@ -177,14 +182,10 @@ public class NewIsland {
// Clean up the user
cleanUpUser(next);
// Fire event
IslandBaseEvent event = IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(reason)
.island(island)
IslandBaseEvent event = IslandEvent.builder().involvedPlayer(user.getUniqueId()).reason(reason).island(island)
.location(island.getCenter())
.blueprintBundle(plugin.getBlueprintsManager().getBlueprintBundles(addon).get(name))
.oldIsland(oldIsland)
.build();
.oldIsland(oldIsland).build();
if (event.getNewEvent().map(IslandBaseEvent::isCancelled).orElse(event.isCancelled())) {
// Do nothing
return;
@ -200,7 +201,8 @@ public class NewIsland {
}
// 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
// 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 {
@ -217,6 +219,7 @@ public class NewIsland {
/**
* Tasks to run after the new island has been created
*
* @param oldIsland - old island that will be deleted
*/
private void postCreationTask(Island oldIsland) {
@ -226,7 +229,8 @@ public class NewIsland {
}
// 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))) {
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
@ -244,9 +248,9 @@ public class NewIsland {
}
/**
* Cleans up a user before moving them to a new island.
* Resets deaths.
* Checks range permissions and saves the player to the database.
* 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) {
@ -255,16 +259,20 @@ public class NewIsland {
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()));
island.setProtectionRange(user.getPermissionValue(
plugin.getIWM().getAddon(island.getWorld()).map(GameModeAddon::getPermissionPrefix).orElse("")
+ "island.range",
island.getProtectionRange()));
// Save the player so that if the server crashes weird things won't happen
plugin.getPlayers().save(user.getUniqueId());
}
/**
* 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
* @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
@ -285,6 +293,7 @@ public class NewIsland {
/**
* Get the reserved island location
*
* @return reserved island location, or null if none found
*/
private Location checkReservedIsland() {
@ -297,7 +306,8 @@ public class NewIsland {
island.setReserved(false);
return l;
} else {
// This should never happen unless we allow another way to paste over islands without reserving
// This should never happen unless we allow another way to paste over islands
// without reserving
plugin.logError("New island for user " + user.getName() + " was not reserved!");
}
}
@ -312,13 +322,9 @@ public class NewIsland {
}
// Fire exit event
IslandEvent.builder()
.involvedPlayer(user.getUniqueId())
.reason(reason == Reason.RESET ? Reason.RESETTED : Reason.CREATED)
.island(island)
.location(island.getCenter())
.oldIsland(oldIsland)
.build();
IslandEvent.builder().involvedPlayer(user.getUniqueId())
.reason(reason == Reason.RESET ? Reason.RESETTED : Reason.CREATED).island(island)
.location(island.getCenter()).oldIsland(oldIsland).build();
}
}